Merge pull request #332 from sp-tarkov/develop

Develop
This commit is contained in:
Chomp
2025-06-01 14:30:04 +01:00
committed by GitHub
221 changed files with 16578 additions and 16334 deletions
+2 -1
View File
@@ -3,6 +3,7 @@ using Benchmarks.Mock;
using SPTarkov.Server.Core.Models.Spt.Templates;
using SPTarkov.Server.Core.Utils;
using SPTarkov.Server.Core.Utils.Cloners;
using SPTarkov.Server.Core.Utils.Json.Converters;
namespace Benchmarks;
@@ -19,7 +20,7 @@ public class ClonerBenchmarks
[GlobalSetup]
public void Setup()
{
var jsonUtil = new JsonUtil();
var jsonUtil = new JsonUtil([ new SptJsonConverterRegistrator() ]);
var importer = new ImporterUtil(new MockLogger<ImporterUtil>(), new FileUtil(),
jsonUtil);
var loadTask = importer.LoadRecursiveAsync<Templates>("./Assets/database/templates/");
@@ -1,7 +1,7 @@
namespace SPTarkov.DI.Annotations;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? type = null, int typePriority = int.MaxValue) : Attribute
public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? typeOverride = null, int typePriority = int.MaxValue) : Attribute
{
public InjectionType InjectionType
{
@@ -14,6 +14,12 @@ public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type
get;
set;
} = typePriority;
public Type? TypeOverride
{
get;
set;
} = typeOverride;
}
public enum InjectionType
@@ -12,7 +12,7 @@ public class DependencyInjectionHandler
private readonly IServiceCollection _serviceCollection;
private readonly Dictionary<string, object> _injectedValues = new();
private readonly object _injectedValuesLock = new();
private readonly Lock _injectedValuesLock = new();
private bool _oneTimeUseFlag;
@@ -61,10 +61,24 @@ public class DependencyInjectionHandler
throw new Exception("Invalid usage of DependencyInjectionHandler, this is a one time use service!");
}
_oneTimeUseFlag = true;
var sortedInjectableTypes = _injectedTypeNames.Values
.Select(t =>
new TypeRefContainer(((Injectable[]) Attribute.GetCustomAttributes(t, typeof(Injectable)))[0], t, t))
.OrderBy(tRef => tRef.InjectableAttribute.TypePriority);
var typeRefValues = _injectedTypeNames.Values
.Select(t => new TypeRefContainer(((Injectable[]) Attribute.GetCustomAttributes(t, typeof(Injectable)))[0], t, t));
// All the components that have a type override, we need to find them and remove them before injecting everything
var componentsToRemove = typeRefValues.Where(tr => tr.InjectableAttribute.TypeOverride != null).Select(tr =>
string.IsNullOrEmpty(tr.InjectableAttribute.TypeOverride!.FullName)
? $"{tr.InjectableAttribute.TypeOverride.Namespace}.{tr.InjectableAttribute.TypeOverride.Name}"
: tr.InjectableAttribute.TypeOverride.FullName!)
.ToHashSet();
// All the components without the removed overrides
var cleanedComponents = typeRefValues.Where(tr =>
{
var name = string.IsNullOrEmpty(tr.Type.FullName)
? $"{tr.Type.Namespace}.{tr.Type.Name}"
: tr.Type.FullName!;
return !componentsToRemove.Contains(name);
});
// All the components sorted and ready to be inserted into the DI container
var sortedInjectableTypes = cleanedComponents.OrderBy(tRef => tRef.InjectableAttribute.TypePriority);
foreach (var typeRefToInject in sortedInjectableTypes)
{
@@ -174,7 +188,7 @@ public class DependencyInjectionHandler
_serviceCollection.AddScoped(registrableInterface, implementationType);
break;
default:
throw new ArgumentOutOfRangeException(nameof(injectionType), "unknown injection type");
throw new ArgumentOutOfRangeException(nameof(injectionType), $"Unknown injection type on {implementationType.Namespace}.{implementationType.Name}");
}
}
@@ -148,6 +148,36 @@
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["590c346786f77423e50ed342"],
"requiredItems": ["679b9d55708cfcb2060b9ae3"],
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["64d4b23dc1b37504b41ac2b6"],
"requiredItems": ["679b9d6390622daf9708da76"],
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["590c31c586f774245e3141b2"],
"requiredItems": ["679b9cce4e4ed4b3b40ae5c5"],
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["5b4335ba86f7744d2837a264"],
"requiredItems": ["679b9d4b3374fb14f40efe6d"],
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["62a09e974f842e1bd12da3f0"],
"requiredItems": ["679b9d43597ba2ed120c3d44"],
"craftTimeSeconds": 3960,
"repeatable": false
},
{
"reward": ["5d1b376e86f774252519444e"],
"requiredItems": ["6582dbf0b8d7830efc45016f"],
@@ -575,7 +575,8 @@
"67614b6b47c71ea3d40256d7",
"67408903268737ef6908d432",
"660ea4ba5a58d057b009efab",
"660312cc4d6cdfa6f500c703"
"660312cc4d6cdfa6f500c703",
"679ba90d269ddfea47012159"
],
"rewardItemTypeBlacklist": [
"65649eb40bf0ed77b8044453"
@@ -130,7 +130,10 @@
"66c0b39ca1f68fcc1d0c0cc3",
"66a0e523e749756c920d02d0",
"593a87af86f774122f54a951"
]
],
"labyrinth": [
"679b992329acd1f2f60985a5"
]
},
"rogueLighthouseSpawnTimeSettings": {
"enabled": false,
@@ -2345,7 +2345,7 @@
"TriggerName": ""
},
{
"BossChance": 80,
"BossChance": 70,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -2363,7 +2363,7 @@
"TriggerName": ""
},
{
"BossChance": 80,
"BossChance": 70,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -2381,7 +2381,7 @@
"TriggerName": ""
},
{
"BossChance": 70,
"BossChance": 60,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -2399,7 +2399,7 @@
"TriggerName": ""
},
{
"BossChance": 70,
"BossChance": 60,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -2417,7 +2417,7 @@
"TriggerName": ""
},
{
"BossChance": 70,
"BossChance": 60,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -2435,7 +2435,7 @@
"TriggerName": ""
},
{
"BossChance": 70,
"BossChance": 60,
"BossDifficult": "normal",
"BossEscortAmount": "0,1,1,2,2,2,2,3",
"BossEscortDifficult": "normal",
@@ -8894,9 +8894,9 @@
},
"grenades": {
"weights": {
"0": 13,
"1": 6,
"2": 2,
"0": 28,
"1": 3,
"2": 1,
"3": 1,
"4": 0,
"5": 0
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,370 +1,370 @@
{
"assort-missing_loyalty_level_object": "stripQuestAssort(): Barang untuk Vendor {{traderId}} tidak memiliki data loyal_level_items, penghapusan barang quest dilewati",
"assort-missing_quest_assort_unlock": "Tidak dapat menemukan quest assort unlock {{traderName}} yang sesuai dengan quest: {{questName}}. Menyelesaikan quest ini tidak akan membuka item trader untuk pembelian",
"assort-missing_questassort": "stripQuestAssort(): Barang untuk vendor: %s tidak memiliki json questassort, penghapusan barang quest dilewati",
"baseclass-item_not_found": "Barang %s tidak ditemukan di dalam cache dasar item, memperbarui cache",
"baseclass-item_not_found_failed": "Barang %s masih tidak ditemukan di cache dasar setelah pembaruan",
"baseclass-missing_db_no_cache": "Database kosong, tidak dapat membuat cache item dasar",
"bleeding_edge_build": "BLEEDINGEDGE",
"bot-bot-cache_has_zero_bots_of_requested_type": "PERINGATAN - Cache bot tidak memiliki bot yang telah dihasilkan sebelumnya dengan tipe %s, akan perlu dibuat, konfigurasikan properti konfigurasi bot.json (presetBatch) menjadi lebih tinggi",
"bot-bot_preset_count_value_missing": "Tidak dapat menemukan jumlah preset yang digunakan untuk dihasilkan untuk bot: %s, default ke 30",
"bot-compatibility_check_missing_props": "Tidak dapat memvalidasi barang: {{id}} {{name}} dalam slot: {{slot}} bisa digunakan, tidak ada nilai _props",
"bot-generation_failed": "pembuatan bot gagal lihat catatan server untuk rincian",
"bot-incompatible_ammo_for_weapon_falling_back_to_default": "Amunisi tidak sesuai {{chosenAmmo}} ditemukan untuk {{weaponId}} - {{weaponName}}, kembali ke default: {{defaultAmmo}}",
"bot-invalid_item_compatibility_check": "Tidak dapat mengecek kesesuain barang dengan barang yang digunakan, barang yang diinginkan: {{itemTpl}} dalam slot: {{slot}} tidak valid",
"bot-item_missing_props_property": "Barang {{itemTpl}} {{name}} tidak memiliki data _props",
"bot-item_spawn_limit_reached_skipping_item": "{{botRole}} tidak dapat memunculkan barang {{itemName}} setelah {{attempts}} percobaan, mengabaikan batas pemunculan",
"bot-loot_type_not_found": "Cache loot gagal untuk barang: {{lootType}} di bot: {{botRole}}, adalah pmc: {{isPmc}}",
"bot-missing_application_context": "applicationContext tidak dapat menemukan nilai %s. Apakah anda mulai ulang server tanpa memulai ulang game?",
"bot-missing_cartridge_slot": "Tidak dapat menambahkan cartridges ke dalam senjata karena modPool tidak memiliki cartridges untuk CylinderMagazine %s, dilewati",
"bot-missing_container_with_tpl": "Tidak dapat menemukan template kontainer dengan tpl: %s",
"bot-missing_equipment_settings": "Bot {{botRole}} tidak memiliki pengaturan perlengkapan: tidak dapat mendapatkan nilai untuk: {{setting}}, kembali ke default: {{defaultValue}}",
"bot-missing_equipment_settings_property": "Bot {{botRole}} tidak memiliki nilai pengaturan perlengkapan untuk: {{setting}}, kembali ke default: {{defaultValue}}",
"bot-missing_item_template": "Tidak dapat menemukan template barang dengan tpl: %s",
"bot-missing_saved_match_info": "getBotCap() Tidak dapat mendapatkan info pertandingan yang tersimpan, kembali ke default. Apakah anda restart server dan bukan client?",
"bot-missing_weapon_preset": "Tidak dapat menemukan preset untuk senjata dengan tpl: %s",
"bot-mod_not_in_slot_filter_list": "Mod: {{modId}} tidak ditemukan dalam filter kompatibilitas barang untuk slot: '{{modSlot}}' buat barnag: {{parentName}}, melewatkan - {{botRole}}",
"bot-mod_slot_missing_from_item": "Slot '{{modSlot}}' tidak ada untuk barang: {{parentId}} {{parentName}} dalam {{botRole}}",
"bot-no_ammo_found_in_bot_json": "Tidak dapat menemukan amunisi untuk tipe bot: %s",
"bot-no_bot_cap_found_for_location": "Batas lokasi bot tidak ditemukan untuk bot: %s, menggunakan default",
"bot-no_bot_type_in_cache": "PERINGATAN - Cache bot tidak memiliki pengertian tentang tipe %s",
"bot-no_caliber_data_for_weapon_falling_back_to_default": "Tidak dapat menemukan data kaliber untuk {{weaponId}} - {{weaponName}}, kembali ke amunisi default: {{defaultAmmo}}",
"bot-no_compatible_camora_ammo_found": "Tidak dapat menemukan amunisi kompatibel untuk slot: %s. pengisian slot camora dilewati",
"bot-no_item_template_found_when_adding_mod": "Tidak dapat menemukan template mod barang dengan tpl: {{modId}} untuk slot {{modSlot}}",
"bot-no_spawn_chance_defined_for_equipment_slot": "Tidak ada kesempatan pemunculan yang ditetapkan untuk peralatan: %s",
"bot-single_bot_generation_not_found_in_cache": "Bot: %s tidak ditemukan di cache, membuat yang baru, ini dapat menghambat game",
"bot-unable_to_add_mod_item_invalid": "Mod: {{itemName}} bukan barang valid, tidak dapat memasukan ke dalam slot: '{{modSlot}}' dalam barang: {{parentItemName}}, dilewati",
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot": "Tidak dapat menambahkan mods ke dalam senjata: {{weaponName}} {{weaponId}} karena kekurangan slot, cartdridges atau chambers - {{botRole}}",
"bot-unable_to_edit_limits_of_unknown_map": "Tidak dapat menyunting batas bot sebuah lokasi: %s karena tidak ditemukan",
"bot-unable_to_fill_camora_slot_mod_pool_empty": "Tidak dapat mengisi slot camora (chamber) senjata: {{weaponId}} - {{weaponName}}. Kumpulan mod untuk itu kosong, mencoba untuk menghasilkan secara dinamis",
"bot-unable_to_filter_mod_slot_all_blacklisted": "Tidak dapat membuat filter kelompok mod senjata yang dinamis karena daftar hitam menyaring semua mods untuk slot: %s, mengabaikan daftar hitam dan membuat ulang kelompok",
"bot-unable_to_filter_mods_all_blacklisted": "Tidak dapat menyaring mods untuk slot: {{slotName}} di {{itemName}} karena daftar hitam, mengabaikan daftar hitam",
"bot-unable_to_find_ammo_item": "Tidak dapat menemukan template amunisi dengan tpl: %s",
"bot-unable_to_find_bot_in_cache": "Tidak dapat menemukan bot bernama: %s dalam cache",
"bot-unable_to_find_default_magazine_item": "Tidak dapat menemukan template magasin: %s dalam database",
"bot-unable_to_find_loot_n_value_for_bot": "Tidak dapat menemukan nilai N benda untuk bot: %s, menggunakan nilai n scav sebagai gantinya",
"bot-unable_to_find_magazine_item": "Tidak dapat menemukan template magasin: %s dalam database",
"bot-unable_to_find_spawn_limits_fallback_to_defaults": "Tidak dapat menemukan batas pemunculan untuk peran: %s kembali ke default",
"bot-unable_to_generate_bot_loot": "Tidak dapat menghasilkan loot bot untuk: %s karena properti bots generation.items tidak memiliki data, melewatkan bot",
"bot-unable_to_generate_item_pool_no_items": "Tidak dapat menghasilkan kumpulan item dengan tipe: %s karena tidak ada item yang disediakan, kumpulan ini akan dilewati",
"bot-unable_to_get_bot_difficulty_fallback_to_assault": "Tidak dapat menemukan bot: {{botType}} kesulitan {{difficulty}}, menggunakan assault sebagai default",
"bot-unable_to_get_bot_fallback_to_assault": "Tidak dapat menemukan bot: %s JSON, menggunakan bot assault sebagai default",
"bot-unable_to_load_raid_settings_from_appcontext": "Tidak dapat memuat pengaturan raid dari ApplicationContext",
"bot-weapon_contains_invalid_item": "Slot yang diperlukan: '{{modSlot}}' pada senjata: {{weaponTpl}} memiliki item yang tidak sah: {{modName}}",
"bot-weapon_generated_incorrect_using_default": "Senjata %s salah dhasilkan, kembali ke preset senjata lihat error di atas",
"bot-weapon_missing_magazine_or_chamber": "Senjata dengan tpl: {{weaponId}} tidak memiliki magasin atau chamber - {{botRole}}",
"bot-weapon_missing_mod_slot": "Slot: {{modSlot}}' tidak ada untuk senjata: {{weaponId}} {{weaponName}} dalam {{botRole}}",
"bot-weapons_required_slot_missing_item": "Slot dibutuhkan '{{modSlot}}' di {{modName}} {{slotId}} kosong pada {{botRole}}",
"build-unable_to_delete_preset": "Tidak dapat menghapus preset, tidak bisa menemukan %s dalam preset weapon, equipment atau magazine",
"chat-unable_to_register_command_already_registered": "Tidak dapat mendaftarkan perintah yang telah terdaftar: %s",
"client_request": "[Client Request] %s",
"client_request_ip": "[Permintaan Klien] {{ip}} {{url}}",
"customisation-item_already_purchased": "Baju {{itemId}} {{itemName}} telah dibeli",
"customisation-suit_lacks_upd_or_stack_property": "Suit dengan tpl: %s tidak memiliki properti upd object atau stackobjectcount",
"customisation-unable_to_find_clothing_item_in_inventory": "Baju tidak ditemukan di inventory dengan id: %s",
"customisation-unable_to_find_suit_by_id": "Tidak dapat menemukan penawaran setelan vendor dengan id: %s",
"customisation-unable_to_find_suit_with_id": "Tidak dapat menemukan suit dengan id penawaran: %s",
"customisation-unable_to_get_trader_suits": "Tidak bisa mendapatkan suits dari trader: %s",
"database-data_at_path_missing": "Database tidak dapat mengambil data dari: [%s] \nTolong pastikan konfigurasimu sah dan data pada lokasi ada",
"database-no_trader_found_with_id": "Tidak dapat menemukan trader: %s dalam database",
"dialog-chatbot_id_already_exists": "Chat bot: %s sedang diregistrasi telah ada, tidak dapat mendaftarkan bot",
"dialog-missing_item_template": "Tidak dapat menemukan template barang {{tpl}} dalam database, tidak dapat mengirim pesan dengan tipe {{type}}, dilewati",
"dialogue-unable_to_find_dialogs_in_profile": "Tidak ada objek percakapan dalam profile: {{sessionId}}",
"dialogue-unable_to_find_in_profile": "Tidak ada percakapan di profile: {{sessionId}} yang ditemukan dengan id: {{dialogueId}}",
"event-unhandled_event": "[UNHANDLED EVENT] %s",
"executing_startup_callbacks": "Server: menjalankan panggilan balik startup...",
"fence-ammo_not_found_in_db": "Peluru: %s bukanlah item yang sah",
"fence-unable_to_find_assort_by_id": "Tidak dapat menemukan barang fence dengan id: %s",
"fence-unable_to_find_offer_by_id": "Tidak dapat menemukan penawaran dengan id: %s",
"fence-unable_to_get_ammo_penetration_value": "Tidak ada nilai penetrasi yang ditemukan untuk Amunisi: %s, Tidak dapat memeriksa apakah di atas batas penetrasi, mengasumsikan salah",
"fixer-clothing_item_found": "Item pakaian: %s ditemukan di profil yang tidak ada di SPT. Anda AKAN mengalami errors, hal ini dapat disebabkan karena menggunakan mod pakaian dan menghapus mod dengan karakter Anda masih memakainya. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, edit 'removeModItemsFromProfile' menjadi true. Ini akan memungkinkan server untuk mengedit profil Anda dan mudah-mudahan menghapus pakaian yang hilang",
"fixer-mod_item_found": "Barang: %s yang ditemukan dalam profil tidak ada dalam database. Anda AKAN mengalami permasalahan, hal ini disebabkan dengan menggunakan mod barang dan menghapuskan mod tanpa menghilangkan barang dari mod tersebut. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, ubah 'removeModItemsFromProfile' menjadi true. Hal ini memperbolehkan server untuk menyunting profil anda sehingga barang yang bermasalah dapat dihapus",
"fixer-trader_found": "Trader: %s yang ditemukan dalam profil tidak ada di dalam SPT. Anda AKAN mengalami permasalahan, hal ini dapat disebabkan dengan menggunakan mod trader dan menghapuskan mod tanpa menghilangkan pesan dari trader tersebut. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, ubah 'removeModItemsFromProfile' menjadi true. Hal ini memperbolehkan server untuk menyunting profil anda sehingga barang yang bermasalah dapat dihapus",
"fixer-updated_pockets": "Update benda 'pocket' ke versi 18876 yang baru dengan x3 slot spesial",
"gameevent-bot_not_found": "addEventGearToScavs() - tidak dapat menemukan bot dengan tipe %s di database, dilewati",
"gameevent-no_gear_data": "Tidak ada data perlengkapan dalam konfigurasi seasonalevents.json untuk acara %s",
"gift-unable_to_handle_message_type_command": "Jenis pesan hadiah: %s tidak ditangani",
"health-healing_item_not_found": "Tidak dapat menemukan barang penyembuhan %s dalam inventory player",
"health-unable_to_find_item_to_consume": "Tidak dapat menemukan barang konsumsi %s dalam inventory player",
"hideout-craft_has_undefined_progress_value_defaulting": "Hideout craft: %s memiliki nilai properti kemajuan yang tidak ditentukan, default menjadi 0",
"hideout-missing_recipe_for_area": "Tidak dapat menemukan resep: %s untuk tipe area",
"hideout-missing_recipe_in_db": "Gagal menemukan resep dengan _id: %s",
"hideout-no_bitcoins_to_collect": "Tidak ada bitcoin siap diambil",
"hideout-unable_to_apply_stashsize_bonus_no_stash_found": "Tidak dapat menerapkan bonus StashSize, stash dengan id: %s tidak ditemukan di profil",
"hideout-unable_to_find_area": "Tidak dapat menemukan area markas: %s dalam profil",
"hideout-unable_to_find_area_in_database": "Tidak dapat menemukan area: %s dalam database",
"hideout-unable_to_find_item_in_inventory": "Gagal mencari barang dalam inventory dengan id %s",
"hideout-unable_to_find_item_to_remove_from_area": "Tidak dapat menemukan barang untuk dihapus dari slot di area: %s",
"hideout-unable_to_find_production_in_profile_by_recipie_id": "Tidak dapat menemukan resep produksi Id: %s dalam profil",
"hideout-unable_to_find_scav_case_recipie_in_database": "Gagal mencari resep Scav Case dengan id: %s dalam databse",
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory": "Tidak dapat mencari barang: %s yang diminta oleh Scav Case",
"hideout-unhandled_remove_item_from_area_request": "Upaya yang tidak ditangani untuk menghapus barang dari area markas: %s",
"http-unknown_error": "Permasalahan yang tidak diketahui terjadi",
"importing_database": "Mengimpor basis data...",
"importing_database_finish": "Impor basis data selesai",
"importing_spt_configs": "Mengimpor konfigurasi...",
"inraid-missing_standing_for_kill": "Standing untuk membunuh tidak ditemukan untuk {{victimSide}}:{{victimRole}}",
"inraid-no_profile_found": "Tidak dapat menambahkan pemain. Tidak ada profil yang ditemukan dengan Id: %s",
"inraid-taskconditioncounter_keys_differ": "TaskConditionCounters: Nilai {{key}} berbeda setelah raid, asli: ${oldValue} baru: ${newValue}",
"inraid-unable_to_deliver_item_no_trader_found": "Tidak dapat mengirim item karena trader %s tidak memiliki data percakapan",
"inraid-unable_to_find_key_in_taskconditioncounters": "Tidak dapat menemukan key: %s dalam TaskConditionCounters data pre-raid",
"inraid-unable_to_migrate_pmc_quest_not_found_in_profile": "Tidak dapat memigrasikan quest pasca raid: %s data ke profil, tidak dapat menemukan quest di profil",
"insurance-item_not_found_in_post_raid_data": "Tidak dapat menemukan item inventaris yang diasuransikan dengan id: %s dalam data pasca raid",
"insurance-missing_insurance_price_multiplier": "Pengganda asuransi tidak ditemukan untuk vendor: %s, cek apakah ada di InsuranceConfig.js, kembali ke nilai default: 0.3",
"insurance-post_raid_item_not_found": "Item yang diasuransikan: %s tidak ditemukan dalam inventaris pasca raid",
"insurance-pre_raid_item_not_found": "Barang inventaris sebelum raid: %s tidak ditemukan",
"insurance-trader_lacks_dialogue_property": "Trader: %s tidak memiliki properti percakapan, tidak dapat mengirim item asuransi",
"insurance-trader_missing_from_enum": "Trader: %s tidak ada dalam enum Traders",
"insurance-unable_to_find_attachment_in_db": "Tidak dapat menemukan attachment yang diasuransikan dalam database - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}",
"insurance-unable_to_find_main_parent_for_attachment": "Tidak dapat menemukan main-parent untuk attachment yang diasuransikan - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}, ID Induk: {{parentId}}",
"insurance-unable_to_find_parent_of_item": "Tidak dapat menemukan parent untuk attachment yang diasuransikan - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}, Parent ID: {{parentId}}",
"insurance-unable_to_find_trader_by_id": "Trader: %s tidak dapat ditemukan",
"inventory-edit_trader_item": "Tidak dapat menyunting barang vendor",
"inventory-examine_item_does_not_exist": "examineItem() - id dengan %s tidak ditemukan",
"inventory-fill_container_failed": "fillContainerMapWithItem() kembali dengan masalah %s",
"inventory-get_item_size_item_not_found_by_tpl": "getSizeByInventoryItemHash() Barang dengan tpl: %s tidak ditemukan",
"inventory-invalid_item_missing_from_db": "Tidak bisa mendapatkan barang: %s dari database",
"inventory-invalid_move_to_container": "Mencoba memindahkan barang dengan slotid: {{slotId}} ke dalam {{container}}, korupsi profil dicegah",
"inventory-item_missing_props_property": "Barang tpl: {{itemTpl}} nama: {{itemName}} kehilangan data props, ukuran tidak bisa diambil",
"inventory-item_to_toggle_missing_upd": "Barang inveotry dengan _id: %s kehilangan objek upd, menambahkan",
"inventory-missing_stash_size": "Tidak dapat menentukan ukuran penyimpanan karena tidak ditemukan dalam inventory player",
"inventory-no_stash_space": "Tidak cukup ruang penyimpanan",
"inventory-return_default_size": "Mendefaultkan barang %s ke ukuran 1x1",
"inventory-stash_not_found": "Tidak dapat menemukan penyimpanan %s dalam database",
"inventory-unable_to_fill_container": "[OOB] untuk barang: {{id}}; permasalahan: {{error}}",
"inventory-unable_to_find_item": "getExaminedItemTpl() Tidak dapat menemukan barang dengan tpl: %s dalam database atau pasar",
"inventory-unable_to_find_item_to_move": "Tidak dapat menemukan item untuk dipindahkan: %s",
"inventory-unable_to_find_item_to_swap": "Tidak dapat menemukan item: {{item1Id}} untuk bertukar posisi dengan: {{item2Id}}",
"inventory-unable_to_find_stash": "Penyimpanan tidak ditemukan",
"inventory-unable_to_fit_item_into_inventory": "Tidak dapat memasukkan item ke dalam inventaris: %s",
"inventory-unable_to_fold_item_not_found_in_inventory": "Tidak dapat melipat item dengan id: %s. Tidak dapat ditemukan di inventaris pemain",
"inventory-unable_to_inspect_item_not_in_db": "Tidak dapat memeriksa item: %s karena tidak dapat ditemukan dalam item DB",
"inventory-unable_to_remove_item_id_not_found": "Tidak dapat menghapus item dengan Id: {{childId}} dari profil: {{profileId}} karena tidak dapat ditemukan",
"inventory-unable_to_remove_item_no_id_given": "Tidak dapat menghapus item karena tidak ada itemId yang diberikan",
"item-durability_value_invalid_use_default": "getRepairableItemQualityValue() tpl senjata: %s nilai durabilitas invalid, kembali ke 1",
"launcher-missing_property": "Profil: %s kehilangan sebuah data descriptionLocaleKey",
"launcher-profile-edgeofdarkness": "Sama seperti Prepare To Escape tambahan; penyimpanan lebih besar (10x68), ekstra perlengkapan/barang, reputasi awal lebih tinggi dengan vendor, 1000 dolar, 500 euro",
"launcher-profile-tournament": "Memiliki wadah aman \"Tournament\", dan Bayonet ER Fulcrum (Senjata tajam), Pasar loak dan tukang tadah dimasuk daftar hitam. Semua pakaian Ragman terbuka. Kirim kode \"TOURNAMENTGIFT\" kepada SPT dalam daftar teman untuk membuka semua fungsi profil.",
"launcher-profile-unheard": "Sama dengan \"Edge Of Darkness\" dengan kantong PMC (PMS) lebih luas, Kedudukan dengan tukang tadah berlebih, Akan ada slot lebih di dalam pasar loak, Senjata jarak dekat unik, Jeda waktu untuk menerima barang dikirimi oleh pedagang akan berlebih, Latar Belakang tambahan, dan peralatan dan sumber daya di penyimpanan akan lebih",
"launcher-profile_leftbehind": "Sama seperti Standard tambahan; ukuran penyimpanan lebih besar (10x38), ekstra perlengkapan/barang, 500 dolar",
"launcher-profile_preparetoescape": "Sama seperti Left Behind tambahan; penyimpanan lebih besar (10x48), ekstra perlengkapan/barang, reputasi awal lebih tinggi dengan vendor, 250 euro",
"launcher-profile_sptdeveloper": "Profil percobaan, level awal 69, Banyak Rubel/Dolar/Euro, USEC mulai dengan quest siap diterima, BEAR mulai dengan quest siap diserahkan, balaklava kekebalan",
"launcher-profile_sptzerotohero": "Mulai dengan tidak ada apapun, tidak ada Rubel/Dolar/Euro, tidak ada reputasi vendor, 1 senjata melee, tidak ada quest selesai",
"launcher-profile_standard": "Sama seperti live, ukuran penyimpanan sederhana (10x28), 500,000 rubel",
"location-containers_generated_success": "Total %s kontainer statik dihasilkan",
"location-critical_error_see_log": "Permasalahan kritikal terjadi ketika menghasilkan barang, lihat catatan server untuk detil",
"location-dynamic_items_spawned_success": "Total %s barang dinamis dimunculkan",
"location-generated_success": "Lokasi dihasilkan %s",
"location-missing_dynamic_template": "Spawnpoint dinamis yang dipilih %s tidak mempunyai template, dilewati",
"location-missing_root_item": "createItem() gagal, root barang null, tpl: {{tpl}}, parentId: {{parentId}}",
"location-preset_not_found": "preset tidak ditemukan untuk {{tpl}}, defaultPreset: {{defaultId}} nama: {{defaultName}}, parentId: {{parentId}}",
"location-spawn_point_count_requested_vs_found": "{{requested}} spawnpoints diminta saat {{found}} tersedia {{mapName}}",
"location-spawnpoint_missing_items": "Spawnpoint dinamis yang dipilih %s tidak mempunyai barang, dilewati",
"location-unable_to_find_airdrop_drop_config_of_type": "Tidak dapat menemukan konfigurasi pengaturan airdrop untuk tipe: %s, kembali ke drop tipe: mixed ",
"location-unable_to_find_static_weapon_for_map": "Tidak dapat menemukan data senjata statis untuk peta: %s",
"location-unable_to_fix_broken_waves_missing_base": "%s tidak memiliki base json, map wave fixes dilewati",
"location-unable_to_generate_static_loot": "Tidak dapat untuk menghasilkan wadah rampasan untuk peta: %s wadah tersebut kekurangan data statis",
"location-unable_to_reparent_item": "createItem() gagal, tidak dapat mengalihkan {{tpl}}, parentId: {{parentId}}",
"loot-item_missing_parentid": "Barang: %s kekurangan nilai parentId, tidak bisa menggunakan barang sebagai loot",
"loot-non_item_picked_as_sealed_weapon_crate_reward": "Senjata invalid: %s, dipilih sebagai hadiah sealed weapon crate, tidak dapat membuat barang",
"mail-unable_to_find_message_sender_by_id": "Tidak bisa menemukan pesanan dengan pengirim dengan id: %s",
"mail-unable_to_give_gift_not_handled": "Tidak bisa untuk memberi player hadiah menggunakan kode: %s kode tersebut tidak di tangani server",
"mailsend-missing_trader": "Tidak dapat mengirim tipe pesan: {{messageType}} ke player: {{sessionId}}, karena enum vendor null",
"mod-send_bundle_url": "[BUNDLE]: %s",
"modloader-async_mod_error": "ModLoader: Terjadi permasalahan ketika loading mod async: %s",
"modloader-checked": "telah dicek",
"modloader-checking_mod": "sedang mengecek: %s",
"modloader-cyclic_dependency": "Dependensi siklik terdeteksi. Masalah ini perlu dibenarkan. Server tidak bisa mulai hingga masalah ini diperbaiki dan akan dimatikan",
"modloader-dependency_container_not_initalized": "Kontainer dependensi telah diminta tapi belum diinisialisasi",
"modloader-error_parsing_mod_load_order": "Permasalahan dalam penguraian urutan pemuatan mod",
"modloader-incompatibilities_not_string_array": "Mod %s data package.json 'incompatibilities' harus string array",
"modloader-incompatible_mod_found": "Mod {{author}}-{{name}} tidak compatibel dengan {{incompatibleModName}}",
"modloader-installing_external_dependencies": "Menginstal dependensi untuk Mod: {{name}} oleh: {{author}}",
"modloader-installing_external_dependencies_disabled": "Mod: {{name}} oleh: {{author}} memerlukan dependensi eksternal tapi fitur ini sedang dinonaktifkan, pergi ke \"{{configPath}}\", ubah \"{{configOption}}\" menjadi true lalu mulai ulang server. \nDengan mengaktifkan fitur ini anda bertanggung jawab atas apa yang {{name}} unduh ke dalam mesin anda.",
"modloader-invalid_version_property": "Mod %s package.json memiliki versi string yang invalid",
"modloader-is_client_mod": "Mod (%s) merupakan mod client dan harus dipasang dalam folder: /spt/bepinex/plugins",
"modloader-load_order_conflict": "'{{modOneName}}' dan '{{modTwoName}}' memiliki urutan pemuatan yang bertentangan, server tidak bisa mulai hingga masalah ini diperbaiki dan akan dimatikan",
"modloader-loaded_mod": "Mod: {{name}} versi: {{version}} oleh: {{author}} dimuat",
"modloader-loading_mods": "ModLoader: loading %s server mods...",
"modloader-main_property_not_js": "Mod %s properti utama package.json harus berupa file .js",
"modloader-main_property_points_to_nothing": "Mod %s properti utama package.json mengarah ke file yang tidak ada",
"modloader-missing_dependency": "Mod {{mod}} memerlukan dependesi {{modDependency}} dipasang.",
"modloader-missing_package_json": "Mod (%s) kehilangan package.json. Pastikan anda sudah mengecek halaman hub mod untuk panduan instal",
"modloader-missing_package_json_property": "Mod package.json {{modName}} memerlukan data {{prop}}",
"modloader-mod_has_no_main_property": "ModLoader: Mod (%s) tidak kompatibel. Tidak memiliki properti 'utama'",
"modloader-mod_isnt_present": "Mod: %s tidak ada",
"modloader-mod_order_error": "ModLoader: Permasalahan ditemukan dalam order.json, MENGGUNAKAN URUTAN PEMUATAN DEFAULT",
"modloader-mod_order_missing": "ModLoader: order.json tidak ada, membuatkan...",
"modloader-mod_order_missing_from_json": "ModLoader: Mod %s tidak ada di order.json, menambahkan",
"modloader-no_mods_loaded": "Permasalahan ditemukan dengan mods, TIDAK ADA MOD YANG AKAN DIPASANG",
"modloader-not_correct_mod_folder": "Folder bernama (%s) ada di dalam folder mods. Anda salah memasang mod. Anda mungkin mengekstrak isi sebuah mod langsung ke dalam folder mod dengan tidak sengaja. Lihat FAQ situs web dan halaman hub mod untuk mengetahui cara memasang mod dengan benar",
"modloader-outdated_dependency": "Mod {{mod}} memerlukan dependensi {{modDependency}} versi {{requiredVersion}}. Versi yang diinstal saat ini {{currentVersion}}",
"modloader-outdated_sptversion_field": "Mod: {{modName}}{{modVersion}}tidak sesuai dengan versi SPT yang terkini, Mod tersebut dibuat untuk versi SPT: {{desiredSptVersion}} Silahkan mengecek untuk versi yang lebih baru untuk mod ini. Masalah akan terjadi - bantuan akan tidak disediakan!",
"modloader-skipped_mod": "Melewatkan pemuatan Mod: {{mod}}",
"modloader-user_mod_folder_missing": "ModLoader: folder user/mod tidak ada, membuatkan...",
"modloader-visited": "dikunjungi",
"modloader-x_duplicates_found": "Anda mencoba memuatkan lebih dari satu versi mod %s. Semua dilewatkan.",
"openzone-unable_to_find_map": "Tidak dapat menambahkan zona ke lokasi: %s karena tidak ada",
"payment-not_enough_money_to_complete_transation": "Kamu tidak cukup uang untuk melakukan transaksi: Butuh{{amountToPay}}, Punya {{amountAvailable}}",
"payment-not_enough_money_to_complete_transation_short": "Uang tidak cukup untuk melakukan transaksi biaya sebesar: %s",
"payment-zero_price_no_payment": "Gratis, tidak butuh pembayaran/biaya",
"player-attempt_to_increment_skill_with_negative_value": "Tidak dapat menaikkan keahlian: %s dengan nilai negatif",
"pmc-name_prefix_1": "Suci",
"pmc-name_prefix_10": "Percaya diri",
"pmc-name_prefix_11": "Luwes/Menarik",
"pmc-name_prefix_12": "Canggi (Refers to something that is developed in a futuristic way or to a high degree of complexity)",
"pmc-name_prefix_13": "Modis",
"pmc-name_prefix_14": "Suci",
"pmc-name_prefix_15": "Tidak Jujur",
"pmc-name_prefix_16": "Serakah",
"pmc-name_prefix_17": "Menjadi Botak",
"pmc-name_prefix_18": "Menarik",
"pmc-name_prefix_19": "Kekanak-kanakan",
"pmc-name_prefix_2": "Jahat",
"pmc-name_prefix_20": "Jahat",
"pmc-name_prefix_21": "Rendah Hati",
"pmc-name_prefix_22": "Indah",
"pmc-name_prefix_23": "Malas",
"pmc-name_prefix_24": "Gelisah (Something like Pessimistic)",
"pmc-name_prefix_3": "Capek/Lelah",
"pmc-name_prefix_31": "Bos-Beb",
"pmc-name_prefix_32": "Raja",
"pmc-name_prefix_37": "Mencurigakan",
"pmc-name_prefix_41": "Bencong/Boti",
"pmc-name_prefix_42": "Palsu",
"pmc-name_prefix_43": "Dendam",
"pmc-name_prefix_44": "Bingung",
"pmc-name_prefix_47": "Besar",
"pmc-name_prefix_49": "Mencurigakan",
"pmc-name_prefix_6": "Jujur",
"pmc-name_prefix_7": "Masuk akal",
"pmc-name_prefix_8": "Ceroboh",
"pmc-name_prefix_9": "Ambisius",
"pmcresponse-killer_negative_1": "mksh utk brg gratisnya",
"pmcresponse-killer_negative_11": "Kukira kamu perlu latihan lebih",
"pmcresponse-killer_negative_2": "Terima Kasih untuk perlengkapan barunya",
"pmcresponse-killer_negative_21": "Rekt",
"pmcresponse-killer_negative_22": "Kukira saya yang jelek",
"pmcresponse-killer_negative_23": "Apakah semuanya {{playerSide}} secupu ini?",
"pmcresponse-killer_negative_24": "Makasih untuk barangnya",
"pmcresponse-killer_negative_25": "Perlengkapanmu sampah banget, aku jual semuanya ke tukang tadah",
"pmcresponse-killer_negative_26": "Semua pemain {{playerSide}}:",
"pmcresponse-killer_negative_29": "Kamu niat apa tidak",
"pmcresponse-killer_negative_4": "Nyapain pake zirah itu wkwk",
"pmcresponse-killer_negative_5": "wkkwkwkwkwk",
"pmcresponse-killer_negative_8": "Itulah adanya",
"pmcresponse-killer_pity_14": "Kamu",
"pmcresponse-killer_plead_4": "Sampai Jumpa",
"pmcresponse-killer_positive_15": "Pertarungan bersih, kuberi hormat",
"pmcresponse-suffix_1": "bro",
"pmcresponse-suffix_10": "bro",
"pmcresponse-suffix_18": "bang",
"pmcresponse-suffix_19": "bg",
"pmcresponse-suffix_20": ":)",
"pmcresponse-suffix_21": "(:",
"pmcresponse-suffix_22": ":))))))",
"pmcresponse-suffix_26": "adik",
"pmcresponse-suffix_27": "cupu",
"pmcresponse-suffix_3": "adik",
"pmcresponse-suffix_4": "bang",
"pmcresponse-suffix_5": "bang",
"pmcresponse-suffix_6": "ketua",
"pmcresponse-suffix_7": "bung",
"pmcresponse-suffix_9": "bro",
"pmcresponse-victim_negative_1": "Aimbotnya keren banget",
"pmcresponse-victim_negative_10": "Aku tidak di depan komputer!!",
"pmcresponse-victim_negative_100": "Aku tau kamu mengunduh SAIN dan harus hapusin karena kamu selalu dibunuh",
"pmcresponse-victim_negative_11": "Kulapor kamu untuk kecurangan",
"pmcresponse-victim_negative_12": "Kamu hanya bisa mengalahkan saya karena internet ku lambat",
"pmcresponse-victim_negative_45": "Perilaku khas {{playerSide}}",
"pmcresponse-victim_negative_46": "Aku mengharap lebih dari tingkat {{playerLevel}}",
"pmcresponse-victim_plead_1": "Aku lagi jalanin tugas",
"pmcresponse-victim_plead_7": "Kamu ga liatin aku goyang tah?!!",
"pmcresponse-victim_positive_17": "Adil, aim mu bagus",
"pmcresponse-victim_positive_18": "Nikmati harta ku",
"pmcresponse-victim_positive_5": "Sangat hokki",
"pmcresponse-victim_positive_9": "gg",
"port_already_in_use": "Port %s masih dipakai, cek apakah server masih berjalan",
"profile-unable_to_find_profile_by_id_cannot_delete": "Tidak dapat menghapus profil dengan id: %s, tidak ada profil dengan id tersebut",
"profile_save_callback_error": "Permasalahan ketika menjalankan onBeforeSaveCallback: {{callback}}, {{error}}",
"profile_saved": "%s Perubahan profil disimpan",
"quest-compare_operator_unhandled": "loyaltyRequirementCheck() operator %s tidak dapat ditangani, kembali ke false",
"quest-handover_wrong_item": "Tidak dapat menyerahkan barang untuk quest {{questId}}, tpl yang diminta: {{requiredTpl}} tpl yang diserahkan: {{handedInTpl}}",
"quest-item_not_found_in_inventory": "changeItemStack() barang dengan _id: %s tidak ditemukan di inventory",
"quest-no_skill_found": "Keahlian %s tidak ditemukan",
"quest-reward_type_not_handled": "Tipe hadiah quest: {{rewardType}} tidak ditangani untuk quest: {{questId}} nama: {{questName}}",
"quest-unable_to_find_compare_condition": "Metode perbandingan tidak dikenal: %s",
"quest-unable_to_find_matching_hideout_production": "Tidak dapat menemukan pembukaan resep markas yang cocok untuk quest: {{questName}}, kecocokan yang ditemukan: {{matchCount}}",
"ragfair-invalid_player_offer_request": "Tidak dapat mengajukan penawaran, permintaan tidak valid",
"ragfair-missing_barter_scheme": "generateFleaOffersForTrader() Gagal mencari barterScheme untuk id barang: {{itemId}} tpl: {{tpl}} di {{name}}",
"ragfair-no_trader_assorts_cant_generate_flea_offers": "Tidak dapat membuat penawaran pasar untuk vendor %s, barang tidak ditemukan",
"ragfair-offer_no_longer_exists": "Penawaran sudah tidak ada lagi",
"ragfair-offer_not_found_in_profile_short": "Penawaran tidak ditemukan di profil",
"ragfair-offer_not_found_unable_to_hide": "hideItem() offerId: %s tidak ditemukan, tidak dapat menyembunyikan penawaran",
"ragfair-tpl_not_a_valid_item": "generateFleaOffersForTrader() tpl: %s bukan barang valid, dilewati",
"ragfair-unable_to_adjust_stack_count_assort_not_found": "Vendor: {{traderId}} penawaran: {{offerId}} tidak dapat menyesuaikan jumlah tumpukan supaya sesuai dengan nilai barang vendor (barang tidak ditemukan)",
"ragfair-unable_to_find_item_in_inventory": "Tidak dapat menemukan barang dengan id: {{id}} dalam inventory",
"ragfair-unable_to_find_locale_by_key": "Tidak dapat menemukan lokal EFT dengan key: %s",
"ragfair-unable_to_find_offer_to_remove": "Tidak dapat menemukan tawaran dengan id: %s untuk dihapus",
"ragfair-unable_to_find_preset_with_id": "Tidak dapat menemukan preset dengan id: %s, menggunakan basis harga senjata yang sudah ada",
"ragfair-unable_to_find_requested_items_in_inventory": "Tidak dapat menemukan barang yang diminta dalam inventory",
"ragfair-unable_to_pay_commission_fee": "Tidak dapat membayar biaya komisi: %s rubel",
"ragfair-unable_to_place_offer_with_no_requirements": "Tidak dapat mengajukan tawaran tanpa biaya",
"ragfair-unable_to_purchase_0_count_item": "Tidak dapat membeli barang: %s dengan jumlah 0",
"ragfair-unable_to_remove_offer_doesnt_exist": "Tidak dapat menghapus tawaran dengan id: %s karena tidak dapat ditemukan di pasar",
"ragfair-unable_to_remove_offer_not_found_in_profile": "Tidak dapat menemukan tawaran: {{offerId}} dalam profil: {{profileId}} karena tawaran tidak terdefinisi, membuatkan",
"release-beta-disclaimer-accept": "Pengguna menerima peringatan penggunaan beta",
"release-illegal-plugins-exception": "Mod klien terdeteksi. Mod tidak dinyalakan untuk BleedingEdge/versi pengujian SPT\n- Tolong dihapuskan sebelum melanjut!",
"repair-unable_to_find_item_in_db": "Tidak dapat memperbaiki barang: %s, tidak bisa dicari di database barang, tidak bisa menambahkan poin keahlian perbaikan",
"repeatable-accepted_repeatable_quest_not_found_in_active_quests": "Menerima quest berulang: %s yang tidak dapat ditemukan di array activeQuests. Tolong laporkan bug ini",
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive": "Membuat Quest Penyelesaian: Tidak ada barang tersisa. Daftar putih terlalu kecil atau Daftar hitam terlalu ketat",
"repeatable-difficulty_was_nan": "Pembuatan hadiah yang diulang: Kesulitan merupakan NaN. Diubah menjadi 1.",
"repeatable-no_reward_item_found_in_price_range": "Pembuatan Hadiah yang Diulang: Tidak ditemukan barang dengan jarak harga {{minPrice}} sampai {{roublesBudget}}",
"repeatable-quest_handover_failed_condition_already_satisfied": "Permasalahan dalam penyerahan quest: kondisi sudah terpenuhi? qid: {{questId}}, kondisi: {{conditionId}}, profileCounter: {{profileCounter}}, nilai: {{value}}",
"repeatable-unable_to_accept_quest_see_log": "Tidak dapat menerima quest, lihat catatn server untuk detil",
"repeatable-unable_to_accept_quest_starting_message_not_found": "Tidak dapat menerima quest: {{questId}} tidak dapat mencari pesan quest mulai dengan id: {{messageId}}",
"route_onupdate_no_response": "onUpdate: rute %s tidak melaporkan sukses atau gagal",
"scav-missing_karma_level_getting_default": "getScavKarmaLevel() gagal, tidak dapat mencari fence di profile.traderInfo. Kembali ke karma level 0",
"scav-missing_karma_settings": "Tidak bisa mendapatkan pengaturan karma untuk level %s",
"scheduled_event_failed_to_run": "Event terjadwal: '%s' gagal dijalankan.",
"seasonal-missing_equipment_slot_on_bot": "Tidak bisa menghapus perlengkapan natal dari slot: {{equipmentSlot}} karena tidak ditemukan di bot: {{botRole}}",
"seasonal-missing_loot_container_slot_on_bot": "Tidak bisa menghapus barang natal dari slot: {{lootContainer}} karena tidak ditemukan di bot: {{botRole}}",
"server_running": "Server sedang berjalan, jangan dimatikan saat main SPT",
"server_start_meme_1": "Hidup, Tertawa, Cinta",
"server_start_meme_2": "Wibu bau bawang",
"server_start_meme_20": "Kau tahu tidak, 9 dari 10 pengguna tidak bisa baca pesan ini",
"server_start_meme_7": "bogos binted",
"server_start_player_active_botreload_skill": "Karakter anda memiliki keahlian 'BotReload' aktif, hal ini akan membuat senjata anda isi ulang cepat sekali, abaikan pesan ini jika disengaja",
"started_webserver_success": "Webserver dimulai pada %s",
"trader-missing_durability_threshold_value": "Tidak dapat menemukan nilai ambang batas durabilitas untuk vendor: {{traderId}}, kembali ke nilai: {{value}}",
"trader-missing_trader_details_using_default_refresh_time": "Vendor: {{traderId}} tidak ditemukan, membuatkan entry sementara dengan waktu penyegaran default: {{updateTime}}",
"trader-price_multipler_is_zero_use_default": "traderPriceMultipler adalah 0, ini tidak valid, diganti menjadi 0.01",
"trader-unable_to_delete_stale_purchases": "Tidak dapat memproses pembelian barang vendor di profil: {{profileId}} karena vendor: {{traderId}} tidak ditemukan, dilewati",
"trader-unable_to_find_profile_by_id": "Tidak bisa menemukan profil dengan id: %s",
"trader-unable_to_find_profile_with_id": "Tidak dapat menemukan profil dengan sessionid: %s",
"trader-unable_to_find_trader_by_id": "Tidak bisa menemukan pedagang dengan id: %s",
"unhandled_response": "[UNHANDLED][%s]",
"unknown_request": "Permintaan tidak diketahui!",
"validation_error_decode": "Tidak dapat membaca checks.dat. Validasi berkas dilewati.",
"validation_error_exception": "Pengecualian tertangkap saat mencoba validasi berkas: %s",
"validation_error_file": "Validasi berkas gagal untuk berkas: %s",
"validation_not_found": "Berkas checks.dat tidak ditemukan. Validasi berkas dilewatkan.",
"watermark-commercial_use_prohibited": "Penggunaan komersial dilarang",
"watermark-discord_url": "https://discord.sp-tarkov.com",
"watermark-do_not_report": "TIDAK USAH DILAPORKAN",
"watermark-free_of_charge": "Hasil kerja ini tidak memungut biaya apapun",
"watermark-modding_disabled": "SERVER MODDING DINONAKTIFKAN DALAM VERSI INI",
"watermark-no_support": "BANTUAN TIDAK AKAN DIBERIKAN",
"watermark-not_an_issue": "INI BUKAN PERMASALAHAN",
"watermark-paid_scammed": "Jika anda dipungut biaya, anda telah ditipu",
"watermark-report_issues_to": "LAPORKAN PERMASALAHAN KE",
"watermark-testing_build": "INI ADALAH VERSI TESTING",
"watermark-use_at_own_risk": "GUNAKAN DENGAN RISIKO ANDA SENDIRI",
"websocket-message_send_failed_with_error": "[WS] sendMessage gagal, dengan error: %s",
"websocket-message_sent": "[WS] pesan terkirim",
"websocket-not_ready_message_not_sent": "[WS] Soket belum siap untuk %s, pesan tidak terkirim",
"websocket-pinging_player": "[WS] notifikasikan player: %s",
"websocket-player_connected": "[WS] Player: %s tersambung",
"websocket-received_message": "[WS] Menerima pesan dari pengguna %s ",
"websocket-started": "Websocket mulai di %s"
"assort-missing_loyalty_level_object": "stripQuestAssort(): Barang untuk Vendor {{traderId}} tidak memiliki data loyal_level_items, penghapusan barang quest dilewati",
"assort-missing_quest_assort_unlock": "Tidak dapat menemukan quest assort unlock {{traderName}} yang sesuai dengan quest: {{questName}}. Menyelesaikan quest ini tidak akan membuka item trader untuk pembelian",
"assort-missing_questassort": "stripQuestAssort(): Barang untuk vendor: %s tidak memiliki json questassort, penghapusan barang quest dilewati",
"baseclass-item_not_found": "Barang %s tidak ditemukan di dalam cache dasar item, memperbarui cache",
"baseclass-item_not_found_failed": "Barang %s masih tidak ditemukan di cache dasar setelah pembaruan",
"baseclass-missing_db_no_cache": "Database kosong, tidak dapat membuat cache item dasar",
"bleeding_edge_build": "BLEEDINGEDGE",
"bot-bot-cache_has_zero_bots_of_requested_type": "PERINGATAN - Cache bot tidak memiliki bot yang telah dihasilkan sebelumnya dengan tipe %s, akan perlu dibuat, konfigurasikan properti konfigurasi bot.json (presetBatch) menjadi lebih tinggi",
"bot-bot_preset_count_value_missing": "Tidak dapat menemukan jumlah preset yang digunakan untuk dihasilkan untuk bot: %s, default ke 30",
"bot-compatibility_check_missing_props": "Tidak dapat memvalidasi barang: {{id}} {{name}} dalam slot: {{slot}} bisa digunakan, tidak ada nilai _props",
"bot-generation_failed": "pembuatan bot gagal lihat catatan server untuk rincian",
"bot-incompatible_ammo_for_weapon_falling_back_to_default": "Amunisi tidak sesuai {{chosenAmmo}} ditemukan untuk {{weaponId}} - {{weaponName}}, kembali ke default: {{defaultAmmo}}",
"bot-invalid_item_compatibility_check": "Tidak dapat mengecek kesesuain barang dengan barang yang digunakan, barang yang diinginkan: {{itemTpl}} dalam slot: {{slot}} tidak valid",
"bot-item_missing_props_property": "Barang {{itemTpl}} {{name}} tidak memiliki data _props",
"bot-item_spawn_limit_reached_skipping_item": "{{botRole}} tidak dapat memunculkan barang {{itemName}} setelah {{attempts}} percobaan, mengabaikan batas pemunculan",
"bot-loot_type_not_found": "Cache loot gagal untuk barang: {{lootType}} di bot: {{botRole}}, adalah pmc: {{isPmc}}",
"bot-missing_application_context": "applicationContext tidak dapat menemukan nilai %s. Apakah anda mulai ulang server tanpa memulai ulang game?",
"bot-missing_cartridge_slot": "Tidak dapat menambahkan cartridges ke dalam senjata karena modPool tidak memiliki cartridges untuk CylinderMagazine %s, dilewati",
"bot-missing_container_with_tpl": "Tidak dapat menemukan template kontainer dengan tpl: %s",
"bot-missing_equipment_settings": "Bot {{botRole}} tidak memiliki pengaturan perlengkapan: tidak dapat mendapatkan nilai untuk: {{setting}}, kembali ke default: {{defaultValue}}",
"bot-missing_equipment_settings_property": "Bot {{botRole}} tidak memiliki nilai pengaturan perlengkapan untuk: {{setting}}, kembali ke default: {{defaultValue}}",
"bot-missing_item_template": "Tidak dapat menemukan template barang dengan tpl: %s",
"bot-missing_saved_match_info": "getBotCap() Tidak dapat mendapatkan info pertandingan yang tersimpan, kembali ke default. Apakah anda restart server dan bukan client?",
"bot-missing_weapon_preset": "Tidak dapat menemukan preset untuk senjata dengan tpl: %s",
"bot-mod_not_in_slot_filter_list": "Mod: {{modId}} tidak ditemukan dalam filter kompatibilitas barang untuk slot: '{{modSlot}}' buat barnag: {{parentName}}, melewatkan - {{botRole}}",
"bot-mod_slot_missing_from_item": "Slot '{{modSlot}}' tidak ada untuk barang: {{parentId}} {{parentName}} dalam {{botRole}}",
"bot-no_ammo_found_in_bot_json": "Tidak dapat menemukan amunisi untuk tipe bot: %s",
"bot-no_bot_cap_found_for_location": "Batas lokasi bot tidak ditemukan untuk bot: %s, menggunakan default",
"bot-no_bot_type_in_cache": "PERINGATAN - Cache bot tidak memiliki pengertian tentang tipe %s",
"bot-no_caliber_data_for_weapon_falling_back_to_default": "Tidak dapat menemukan data kaliber untuk {{weaponId}} - {{weaponName}}, kembali ke amunisi default: {{defaultAmmo}}",
"bot-no_compatible_camora_ammo_found": "Tidak dapat menemukan amunisi kompatibel untuk slot: %s. pengisian slot camora dilewati",
"bot-no_item_template_found_when_adding_mod": "Tidak dapat menemukan template mod barang dengan tpl: {{modId}} untuk slot {{modSlot}}",
"bot-no_spawn_chance_defined_for_equipment_slot": "Tidak ada kesempatan pemunculan yang ditetapkan untuk peralatan: %s",
"bot-single_bot_generation_not_found_in_cache": "Bot: %s tidak ditemukan di cache, membuat yang baru, ini dapat menghambat game",
"bot-unable_to_add_mod_item_invalid": "Mod: {{itemName}} bukan barang valid, tidak dapat memasukan ke dalam slot: '{{modSlot}}' dalam barang: {{parentItemName}}, dilewati",
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot": "Tidak dapat menambahkan mods ke dalam senjata: {{weaponName}} {{weaponId}} karena kekurangan slot, cartdridges atau chambers - {{botRole}}",
"bot-unable_to_edit_limits_of_unknown_map": "Tidak dapat menyunting batas bot sebuah lokasi: %s karena tidak ditemukan",
"bot-unable_to_fill_camora_slot_mod_pool_empty": "Tidak dapat mengisi slot camora (chamber) senjata: {{weaponId}} - {{weaponName}}. Kumpulan mod untuk itu kosong, mencoba untuk menghasilkan secara dinamis",
"bot-unable_to_filter_mod_slot_all_blacklisted": "Tidak dapat membuat filter kelompok mod senjata yang dinamis karena daftar hitam menyaring semua mods untuk slot: %s, mengabaikan daftar hitam dan membuat ulang kelompok",
"bot-unable_to_filter_mods_all_blacklisted": "Tidak dapat menyaring mods untuk slot: {{slotName}} di {{itemName}} karena daftar hitam, mengabaikan daftar hitam",
"bot-unable_to_find_ammo_item": "Tidak dapat menemukan template amunisi dengan tpl: %s",
"bot-unable_to_find_bot_in_cache": "Tidak dapat menemukan bot bernama: %s dalam cache",
"bot-unable_to_find_default_magazine_item": "Tidak dapat menemukan template magasin: %s dalam database",
"bot-unable_to_find_loot_n_value_for_bot": "Tidak dapat menemukan nilai N benda untuk bot: %s, menggunakan nilai n scav sebagai gantinya",
"bot-unable_to_find_magazine_item": "Tidak dapat menemukan template magasin: %s dalam database",
"bot-unable_to_find_spawn_limits_fallback_to_defaults": "Tidak dapat menemukan batas pemunculan untuk peran: %s kembali ke default",
"bot-unable_to_generate_bot_loot": "Tidak dapat menghasilkan loot bot untuk: %s karena properti bots generation.items tidak memiliki data, melewatkan bot",
"bot-unable_to_generate_item_pool_no_items": "Tidak dapat menghasilkan kumpulan item dengan tipe: %s karena tidak ada item yang disediakan, kumpulan ini akan dilewati",
"bot-unable_to_get_bot_difficulty_fallback_to_assault": "Tidak dapat menemukan bot: {{botType}} kesulitan {{difficulty}}, menggunakan assault sebagai default",
"bot-unable_to_get_bot_fallback_to_assault": "Tidak dapat menemukan bot: %s JSON, menggunakan bot assault sebagai default",
"bot-unable_to_load_raid_settings_from_appcontext": "Tidak dapat memuat pengaturan raid dari ApplicationContext",
"bot-weapon_contains_invalid_item": "Slot yang diperlukan: '{{modSlot}}' pada senjata: {{weaponTpl}} memiliki item yang tidak sah: {{modName}}",
"bot-weapon_generated_incorrect_using_default": "Senjata %s salah dhasilkan, kembali ke preset senjata lihat error di atas",
"bot-weapon_missing_magazine_or_chamber": "Senjata dengan tpl: {{weaponId}} tidak memiliki magasin atau chamber - {{botRole}}",
"bot-weapon_missing_mod_slot": "Slot: {{modSlot}}' tidak ada untuk senjata: {{weaponId}} {{weaponName}} dalam {{botRole}}",
"bot-weapons_required_slot_missing_item": "Slot dibutuhkan '{{modSlot}}' di {{modName}} {{slotId}} kosong pada {{botRole}}",
"build-unable_to_delete_preset": "Tidak dapat menghapus preset, tidak bisa menemukan %s dalam preset weapon, equipment atau magazine",
"chat-unable_to_register_command_already_registered": "Tidak dapat mendaftarkan perintah yang telah terdaftar: %s",
"client_request": "[Client Request] %s",
"client_request_ip": "[Permintaan Klien] {{ip}} {{url}}",
"customisation-item_already_purchased": "Baju {{itemId}} {{itemName}} telah dibeli",
"customisation-suit_lacks_upd_or_stack_property": "Suit dengan tpl: %s tidak memiliki properti upd object atau stackobjectcount",
"customisation-unable_to_find_clothing_item_in_inventory": "Baju tidak ditemukan di inventory dengan id: %s",
"customisation-unable_to_find_suit_by_id": "Tidak dapat menemukan penawaran setelan vendor dengan id: %s",
"customisation-unable_to_find_suit_with_id": "Tidak dapat menemukan suit dengan id penawaran: %s",
"customisation-unable_to_get_trader_suits": "Tidak bisa mendapatkan suits dari trader: %s",
"database-data_at_path_missing": "Database tidak dapat mengambil data dari: [%s] \nTolong pastikan konfigurasimu sah dan data pada lokasi ada",
"database-no_trader_found_with_id": "Tidak dapat menemukan trader: %s dalam database",
"dialog-chatbot_id_already_exists": "Chat bot: %s sedang diregistrasi telah ada, tidak dapat mendaftarkan bot",
"dialog-missing_item_template": "Tidak dapat menemukan template barang {{tpl}} dalam database, tidak dapat mengirim pesan dengan tipe {{type}}, dilewati",
"dialogue-unable_to_find_dialogs_in_profile": "Tidak ada objek percakapan dalam profile: {{sessionId}}",
"dialogue-unable_to_find_in_profile": "Tidak ada percakapan di profile: {{sessionId}} yang ditemukan dengan id: {{dialogueId}}",
"event-unhandled_event": "[UNHANDLED EVENT] %s",
"executing_startup_callbacks": "Server: menjalankan panggilan balik startup...",
"fence-ammo_not_found_in_db": "Peluru: %s bukanlah item yang sah",
"fence-unable_to_find_assort_by_id": "Tidak dapat menemukan barang fence dengan id: %s",
"fence-unable_to_find_offer_by_id": "Tidak dapat menemukan penawaran dengan id: %s",
"fence-unable_to_get_ammo_penetration_value": "Tidak ada nilai penetrasi yang ditemukan untuk Amunisi: %s, Tidak dapat memeriksa apakah di atas batas penetrasi, mengasumsikan salah",
"fixer-clothing_item_found": "Item pakaian: %s ditemukan di profil yang tidak ada di SPT. Anda AKAN mengalami errors, hal ini dapat disebabkan karena menggunakan mod pakaian dan menghapus mod dengan karakter Anda masih memakainya. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, edit 'removeModItemsFromProfile' menjadi true. Ini akan memungkinkan server untuk mengedit profil Anda dan mudah-mudahan menghapus pakaian yang hilang",
"fixer-mod_item_found": "Barang: %s yang ditemukan dalam profil tidak ada dalam database. Anda AKAN mengalami permasalahan, hal ini disebabkan dengan menggunakan mod barang dan menghapuskan mod tanpa menghilangkan barang dari mod tersebut. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, ubah 'removeModItemsFromProfile' menjadi true. Hal ini memperbolehkan server untuk menyunting profil anda sehingga barang yang bermasalah dapat dihapus",
"fixer-trader_found": "Trader: %s yang ditemukan dalam profil tidak ada di dalam SPT. Anda AKAN mengalami permasalahan, hal ini dapat disebabkan dengan menggunakan mod trader dan menghapuskan mod tanpa menghilangkan pesan dari trader tersebut. JANGAN GUNAKAN PROFIL INI. Buka SPT_Data\\Server\\configs\\core.json, ubah 'removeModItemsFromProfile' menjadi true. Hal ini memperbolehkan server untuk menyunting profil anda sehingga barang yang bermasalah dapat dihapus",
"fixer-updated_pockets": "Update benda 'pocket' ke versi 18876 yang baru dengan x3 slot spesial",
"gameevent-bot_not_found": "addEventGearToScavs() - tidak dapat menemukan bot dengan tipe %s di database, dilewati",
"gameevent-no_gear_data": "Tidak ada data perlengkapan dalam konfigurasi seasonalevents.json untuk acara %s",
"gift-unable_to_handle_message_type_command": "Jenis pesan hadiah: %s tidak ditangani",
"health-healing_item_not_found": "Tidak dapat menemukan barang penyembuhan %s dalam inventory player",
"health-unable_to_find_item_to_consume": "Tidak dapat menemukan barang konsumsi %s dalam inventory player",
"hideout-craft_has_undefined_progress_value_defaulting": "Hideout craft: %s memiliki nilai properti kemajuan yang tidak ditentukan, default menjadi 0",
"hideout-missing_recipe_for_area": "Tidak dapat menemukan resep: %s untuk tipe area",
"hideout-missing_recipe_in_db": "Gagal menemukan resep dengan _id: %s",
"hideout-no_bitcoins_to_collect": "Tidak ada bitcoin siap diambil",
"hideout-unable_to_apply_stashsize_bonus_no_stash_found": "Tidak dapat menerapkan bonus StashSize, stash dengan id: %s tidak ditemukan di profil",
"hideout-unable_to_find_area": "Tidak dapat menemukan area markas: %s dalam profil",
"hideout-unable_to_find_area_in_database": "Tidak dapat menemukan area: %s dalam database",
"hideout-unable_to_find_item_in_inventory": "Gagal mencari barang dalam inventory dengan id %s",
"hideout-unable_to_find_item_to_remove_from_area": "Tidak dapat menemukan barang untuk dihapus dari slot di area: %s",
"hideout-unable_to_find_production_in_profile_by_recipie_id": "Tidak dapat menemukan resep produksi Id: %s dalam profil",
"hideout-unable_to_find_scav_case_recipie_in_database": "Gagal mencari resep Scav Case dengan id: %s dalam databse",
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory": "Tidak dapat mencari barang: %s yang diminta oleh Scav Case",
"hideout-unhandled_remove_item_from_area_request": "Upaya yang tidak ditangani untuk menghapus barang dari area markas: %s",
"http-unknown_error": "Permasalahan yang tidak diketahui terjadi",
"importing_database": "Mengimpor basis data...",
"importing_database_finish": "Impor basis data selesai",
"importing_spt_configs": "Mengimpor konfigurasi...",
"inraid-missing_standing_for_kill": "Standing untuk membunuh tidak ditemukan untuk {{victimSide}}:{{victimRole}}",
"inraid-no_profile_found": "Tidak dapat menambahkan pemain. Tidak ada profil yang ditemukan dengan Id: %s",
"inraid-taskconditioncounter_keys_differ": "TaskConditionCounters: Nilai {{key}} berbeda setelah raid, asli: ${oldValue} baru: ${newValue}",
"inraid-unable_to_deliver_item_no_trader_found": "Tidak dapat mengirim item karena trader %s tidak memiliki data percakapan",
"inraid-unable_to_find_key_in_taskconditioncounters": "Tidak dapat menemukan key: %s dalam TaskConditionCounters data pre-raid",
"inraid-unable_to_migrate_pmc_quest_not_found_in_profile": "Tidak dapat memigrasikan quest pasca raid: %s data ke profil, tidak dapat menemukan quest di profil",
"insurance-item_not_found_in_post_raid_data": "Tidak dapat menemukan item inventaris yang diasuransikan dengan id: %s dalam data pasca raid",
"insurance-missing_insurance_price_multiplier": "Pengganda asuransi tidak ditemukan untuk vendor: %s, cek apakah ada di InsuranceConfig.js, kembali ke nilai default: 0.3",
"insurance-post_raid_item_not_found": "Item yang diasuransikan: %s tidak ditemukan dalam inventaris pasca raid",
"insurance-pre_raid_item_not_found": "Barang inventaris sebelum raid: %s tidak ditemukan",
"insurance-trader_lacks_dialogue_property": "Trader: %s tidak memiliki properti percakapan, tidak dapat mengirim item asuransi",
"insurance-trader_missing_from_enum": "Trader: %s tidak ada dalam enum Traders",
"insurance-unable_to_find_attachment_in_db": "Tidak dapat menemukan attachment yang diasuransikan dalam database - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}",
"insurance-unable_to_find_main_parent_for_attachment": "Tidak dapat menemukan main-parent untuk attachment yang diasuransikan - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}, ID Induk: {{parentId}}",
"insurance-unable_to_find_parent_of_item": "Tidak dapat menemukan parent untuk attachment yang diasuransikan - ID: {{insuredItemId}}, Template: {{insuredItemTpl}}, Parent ID: {{parentId}}",
"insurance-unable_to_find_trader_by_id": "Trader: %s tidak dapat ditemukan",
"inventory-edit_trader_item": "Tidak dapat menyunting barang vendor",
"inventory-examine_item_does_not_exist": "examineItem() - id dengan %s tidak ditemukan",
"inventory-fill_container_failed": "fillContainerMapWithItem() kembali dengan masalah %s",
"inventory-get_item_size_item_not_found_by_tpl": "getSizeByInventoryItemHash() Barang dengan tpl: %s tidak ditemukan",
"inventory-invalid_item_missing_from_db": "Tidak bisa mendapatkan barang: %s dari database",
"inventory-invalid_move_to_container": "Mencoba memindahkan barang dengan slotid: {{slotId}} ke dalam {{container}}, korupsi profil dicegah",
"inventory-item_missing_props_property": "Barang tpl: {{itemTpl}} nama: {{itemName}} kehilangan data props, ukuran tidak bisa diambil",
"inventory-item_to_toggle_missing_upd": "Barang inveotry dengan _id: %s kehilangan objek upd, menambahkan",
"inventory-missing_stash_size": "Tidak dapat menentukan ukuran penyimpanan karena tidak ditemukan dalam inventory player",
"inventory-no_stash_space": "Tidak cukup ruang penyimpanan",
"inventory-return_default_size": "Mendefaultkan barang %s ke ukuran 1x1",
"inventory-stash_not_found": "Tidak dapat menemukan penyimpanan %s dalam database",
"inventory-unable_to_fill_container": "[OOB] untuk barang: {{id}}; permasalahan: {{error}}",
"inventory-unable_to_find_item": "getExaminedItemTpl() Tidak dapat menemukan barang dengan tpl: %s dalam database atau pasar",
"inventory-unable_to_find_item_to_move": "Tidak dapat menemukan item untuk dipindahkan: %s",
"inventory-unable_to_find_item_to_swap": "Tidak dapat menemukan item: {{item1Id}} untuk bertukar posisi dengan: {{item2Id}}",
"inventory-unable_to_find_stash": "Penyimpanan tidak ditemukan",
"inventory-unable_to_fit_item_into_inventory": "Tidak dapat memasukkan item ke dalam inventaris: %s",
"inventory-unable_to_fold_item_not_found_in_inventory": "Tidak dapat melipat item dengan id: %s. Tidak dapat ditemukan di inventaris pemain",
"inventory-unable_to_inspect_item_not_in_db": "Tidak dapat memeriksa item: %s karena tidak dapat ditemukan dalam item DB",
"inventory-unable_to_remove_item_id_not_found": "Tidak dapat menghapus item dengan Id: {{childId}} dari profil: {{profileId}} karena tidak dapat ditemukan",
"inventory-unable_to_remove_item_no_id_given": "Tidak dapat menghapus item karena tidak ada itemId yang diberikan",
"item-durability_value_invalid_use_default": "getRepairableItemQualityValue() tpl senjata: %s nilai durabilitas invalid, kembali ke 1",
"launcher-missing_property": "Profil: %s kehilangan sebuah data descriptionLocaleKey",
"launcher-profile-edgeofdarkness": "Sama seperti Prepare To Escape tambahan; penyimpanan lebih besar (10x68), ekstra perlengkapan/barang, reputasi awal lebih tinggi dengan vendor, 1000 dolar, 500 euro",
"launcher-profile-tournament": "Memiliki wadah aman \"Tournament\", dan Bayonet ER Fulcrum (Senjata tajam), Pasar loak dan tukang tadah dimasuk daftar hitam. Semua pakaian Ragman terbuka. Kirim kode \"TOURNAMENTGIFT\" kepada SPT dalam daftar teman untuk membuka semua fungsi profil.",
"launcher-profile-unheard": "Sama dengan \"Edge Of Darkness\" dengan kantong PMC (PMS) lebih luas, Kedudukan dengan tukang tadah berlebih, Akan ada slot lebih di dalam pasar loak, Senjata jarak dekat unik, Jeda waktu untuk menerima barang dikirimi oleh pedagang akan berlebih, Latar Belakang tambahan, dan peralatan dan sumber daya di penyimpanan akan lebih",
"launcher-profile_leftbehind": "Sama seperti Standard tambahan; ukuran penyimpanan lebih besar (10x38), ekstra perlengkapan/barang, 500 dolar",
"launcher-profile_preparetoescape": "Sama seperti Left Behind tambahan; penyimpanan lebih besar (10x48), ekstra perlengkapan/barang, reputasi awal lebih tinggi dengan vendor, 250 euro",
"launcher-profile_sptdeveloper": "Profil percobaan, level awal 69, Banyak Rubel/Dolar/Euro, USEC mulai dengan quest siap diterima, BEAR mulai dengan quest siap diserahkan, balaklava kekebalan",
"launcher-profile_sptzerotohero": "Mulai dengan tidak ada apapun, tidak ada Rubel/Dolar/Euro, tidak ada reputasi vendor, 1 senjata melee, tidak ada quest selesai",
"launcher-profile_standard": "Sama seperti live, ukuran penyimpanan sederhana (10x28), 500,000 rubel",
"location-containers_generated_success": "Total %s kontainer statik dihasilkan",
"location-critical_error_see_log": "Permasalahan kritikal terjadi ketika menghasilkan barang, lihat catatan server untuk detil",
"location-dynamic_items_spawned_success": "Total %s barang dinamis dimunculkan",
"location-generated_success": "Lokasi dihasilkan %s",
"location-missing_dynamic_template": "Spawnpoint dinamis yang dipilih %s tidak mempunyai template, dilewati",
"location-missing_root_item": "createItem() gagal, root barang null, tpl: {{tpl}}, parentId: {{parentId}}",
"location-preset_not_found": "preset tidak ditemukan untuk {{tpl}}, defaultPreset: {{defaultId}} nama: {{defaultName}}, parentId: {{parentId}}",
"location-spawn_point_count_requested_vs_found": "{{requested}} spawnpoints diminta saat {{found}} tersedia {{mapName}}",
"location-spawnpoint_missing_items": "Spawnpoint dinamis yang dipilih %s tidak mempunyai barang, dilewati",
"location-unable_to_find_airdrop_drop_config_of_type": "Tidak dapat menemukan konfigurasi pengaturan airdrop untuk tipe: %s, kembali ke drop tipe: mixed ",
"location-unable_to_find_static_weapon_for_map": "Tidak dapat menemukan data senjata statis untuk peta: %s",
"location-unable_to_fix_broken_waves_missing_base": "%s tidak memiliki base json, map wave fixes dilewati",
"location-unable_to_generate_static_loot": "Tidak dapat untuk menghasilkan wadah rampasan untuk peta: %s wadah tersebut kekurangan data statis",
"location-unable_to_reparent_item": "createItem() gagal, tidak dapat mengalihkan {{tpl}}, parentId: {{parentId}}",
"loot-item_missing_parentid": "Barang: %s kekurangan nilai parentId, tidak bisa menggunakan barang sebagai loot",
"loot-non_item_picked_as_sealed_weapon_crate_reward": "Senjata invalid: %s, dipilih sebagai hadiah sealed weapon crate, tidak dapat membuat barang",
"mail-unable_to_find_message_sender_by_id": "Tidak bisa menemukan pesanan dengan pengirim dengan id: %s",
"mail-unable_to_give_gift_not_handled": "Tidak bisa untuk memberi player hadiah menggunakan kode: %s kode tersebut tidak di tangani server",
"mailsend-missing_trader": "Tidak dapat mengirim tipe pesan: {{messageType}} ke player: {{sessionId}}, karena enum vendor null",
"mod-send_bundle_url": "[BUNDLE]: %s",
"modloader-async_mod_error": "ModLoader: Terjadi permasalahan ketika loading mod async: %s",
"modloader-checked": "telah dicek",
"modloader-checking_mod": "sedang mengecek: %s",
"modloader-cyclic_dependency": "Dependensi siklik terdeteksi. Masalah ini perlu dibenarkan. Server tidak bisa mulai hingga masalah ini diperbaiki dan akan dimatikan",
"modloader-dependency_container_not_initalized": "Kontainer dependensi telah diminta tapi belum diinisialisasi",
"modloader-error_parsing_mod_load_order": "Permasalahan dalam penguraian urutan pemuatan mod",
"modloader-incompatibilities_not_string_array": "Mod %s data package.json 'incompatibilities' harus string array",
"modloader-incompatible_mod_found": "Mod {{author}}-{{name}} tidak compatibel dengan {{incompatibleModName}}",
"modloader-installing_external_dependencies": "Menginstal dependensi untuk Mod: {{name}} oleh: {{author}}",
"modloader-installing_external_dependencies_disabled": "Mod: {{name}} oleh: {{author}} memerlukan dependensi eksternal tapi fitur ini sedang dinonaktifkan, pergi ke \"{{configPath}}\", ubah \"{{configOption}}\" menjadi true lalu mulai ulang server. \nDengan mengaktifkan fitur ini anda bertanggung jawab atas apa yang {{name}} unduh ke dalam mesin anda.",
"modloader-invalid_version_property": "Mod %s package.json memiliki versi string yang invalid",
"modloader-is_client_mod": "Mod (%s) merupakan mod client dan harus dipasang dalam folder: /spt/bepinex/plugins",
"modloader-load_order_conflict": "'{{modOneName}}' dan '{{modTwoName}}' memiliki urutan pemuatan yang bertentangan, server tidak bisa mulai hingga masalah ini diperbaiki dan akan dimatikan",
"modloader-loaded_mod": "Mod: {{name}} versi: {{version}} oleh: {{author}} dimuat",
"modloader-loading_mods": "ModLoader: loading %s server mods...",
"modloader-main_property_not_js": "Mod %s properti utama package.json harus berupa file .js",
"modloader-main_property_points_to_nothing": "Mod %s properti utama package.json mengarah ke file yang tidak ada",
"modloader-missing_dependency": "Mod {{mod}} memerlukan dependesi {{modDependency}} dipasang.",
"modloader-missing_package_json": "Mod (%s) kehilangan package.json. Pastikan anda sudah mengecek halaman hub mod untuk panduan instal",
"modloader-missing_package_json_property": "Mod package.json {{modName}} memerlukan data {{prop}}",
"modloader-mod_has_no_main_property": "ModLoader: Mod (%s) tidak kompatibel. Tidak memiliki properti 'utama'",
"modloader-mod_isnt_present": "Mod: %s tidak ada",
"modloader-mod_order_error": "ModLoader: Permasalahan ditemukan dalam order.json, MENGGUNAKAN URUTAN PEMUATAN DEFAULT",
"modloader-mod_order_missing": "ModLoader: order.json tidak ada, membuatkan...",
"modloader-mod_order_missing_from_json": "ModLoader: Mod %s tidak ada di order.json, menambahkan",
"modloader-no_mods_loaded": "Permasalahan ditemukan dengan mods, TIDAK ADA MOD YANG AKAN DIPASANG",
"modloader-not_correct_mod_folder": "Folder bernama (%s) ada di dalam folder mods. Anda salah memasang mod. Anda mungkin mengekstrak isi sebuah mod langsung ke dalam folder mod dengan tidak sengaja. Lihat FAQ situs web dan halaman hub mod untuk mengetahui cara memasang mod dengan benar",
"modloader-outdated_dependency": "Mod {{mod}} memerlukan dependensi {{modDependency}} versi {{requiredVersion}}. Versi yang diinstal saat ini {{currentVersion}}",
"modloader-outdated_sptversion_field": "Mod: {{modName}}{{modVersion}}tidak sesuai dengan versi SPT yang terkini, Mod tersebut dibuat untuk versi SPT: {{desiredSptVersion}} Silahkan mengecek untuk versi yang lebih baru untuk mod ini. Masalah akan terjadi - bantuan akan tidak disediakan!",
"modloader-skipped_mod": "Melewatkan pemuatan Mod: {{mod}}",
"modloader-user_mod_folder_missing": "ModLoader: folder user/mod tidak ada, membuatkan...",
"modloader-visited": "dikunjungi",
"modloader-x_duplicates_found": "Anda mencoba memuatkan lebih dari satu versi mod %s. Semua dilewatkan.",
"openzone-unable_to_find_map": "Tidak dapat menambahkan zona ke lokasi: %s karena tidak ada",
"payment-not_enough_money_to_complete_transation": "Kamu tidak cukup uang untuk melakukan transaksi: Butuh{{amountToPay}}, Punya {{amountAvailable}}",
"payment-not_enough_money_to_complete_transation_short": "Uang tidak cukup untuk melakukan transaksi biaya sebesar: %s",
"payment-zero_price_no_payment": "Gratis, tidak butuh pembayaran/biaya",
"player-attempt_to_increment_skill_with_negative_value": "Tidak dapat menaikkan keahlian: %s dengan nilai negatif",
"pmc-name_prefix_1": "Suci",
"pmc-name_prefix_10": "Percaya diri",
"pmc-name_prefix_11": "Luwes/Menarik",
"pmc-name_prefix_12": "Canggi (Refers to something that is developed in a futuristic way or to a high degree of complexity)",
"pmc-name_prefix_13": "Modis",
"pmc-name_prefix_14": "Suci",
"pmc-name_prefix_15": "Tidak Jujur",
"pmc-name_prefix_16": "Serakah",
"pmc-name_prefix_17": "Menjadi Botak",
"pmc-name_prefix_18": "Menarik",
"pmc-name_prefix_19": "Kekanak-kanakan",
"pmc-name_prefix_2": "Jahat",
"pmc-name_prefix_20": "Jahat",
"pmc-name_prefix_21": "Rendah Hati",
"pmc-name_prefix_22": "Indah",
"pmc-name_prefix_23": "Malas",
"pmc-name_prefix_24": "Gelisah (Something like Pessimistic)",
"pmc-name_prefix_3": "Capek/Lelah",
"pmc-name_prefix_31": "Bos-Beb",
"pmc-name_prefix_32": "Raja",
"pmc-name_prefix_37": "Mencurigakan",
"pmc-name_prefix_41": "Bencong/Boti",
"pmc-name_prefix_42": "Palsu",
"pmc-name_prefix_43": "Dendam",
"pmc-name_prefix_44": "Bingung",
"pmc-name_prefix_47": "Besar",
"pmc-name_prefix_49": "Mencurigakan",
"pmc-name_prefix_6": "Jujur",
"pmc-name_prefix_7": "Masuk akal",
"pmc-name_prefix_8": "Ceroboh",
"pmc-name_prefix_9": "Ambisius",
"pmcresponse-killer_negative_1": "mksh utk brg gratisnya",
"pmcresponse-killer_negative_11": "Kukira kamu perlu latihan lebih",
"pmcresponse-killer_negative_2": "Terima Kasih untuk perlengkapan barunya",
"pmcresponse-killer_negative_21": "Rekt",
"pmcresponse-killer_negative_22": "Kukira saya yang jelek",
"pmcresponse-killer_negative_23": "Apakah semuanya {{playerSide}} secupu ini?",
"pmcresponse-killer_negative_24": "Makasih untuk barangnya",
"pmcresponse-killer_negative_25": "Perlengkapanmu sampah banget, aku jual semuanya ke tukang tadah",
"pmcresponse-killer_negative_26": "Semua pemain {{playerSide}}:",
"pmcresponse-killer_negative_29": "Kamu niat apa tidak",
"pmcresponse-killer_negative_4": "Nyapain pake zirah itu wkwk",
"pmcresponse-killer_negative_5": "wkkwkwkwkwk",
"pmcresponse-killer_negative_8": "Itulah adanya",
"pmcresponse-killer_pity_14": "Kamu",
"pmcresponse-killer_plead_4": "Sampai Jumpa",
"pmcresponse-killer_positive_15": "Pertarungan bersih, kuberi hormat",
"pmcresponse-suffix_1": "bro",
"pmcresponse-suffix_10": "bro",
"pmcresponse-suffix_18": "bang",
"pmcresponse-suffix_19": "bg",
"pmcresponse-suffix_20": ":)",
"pmcresponse-suffix_21": "(:",
"pmcresponse-suffix_22": ":))))))",
"pmcresponse-suffix_26": "adik",
"pmcresponse-suffix_27": "cupu",
"pmcresponse-suffix_3": "adik",
"pmcresponse-suffix_4": "bang",
"pmcresponse-suffix_5": "bang",
"pmcresponse-suffix_6": "ketua",
"pmcresponse-suffix_7": "bung",
"pmcresponse-suffix_9": "bro",
"pmcresponse-victim_negative_1": "Aimbotnya keren banget",
"pmcresponse-victim_negative_10": "Aku tidak di depan komputer!!",
"pmcresponse-victim_negative_100": "Aku tau kamu mengunduh SAIN dan harus hapusin karena kamu selalu dibunuh",
"pmcresponse-victim_negative_11": "Kulapor kamu untuk kecurangan",
"pmcresponse-victim_negative_12": "Kamu hanya bisa mengalahkan saya karena internet ku lambat",
"pmcresponse-victim_negative_45": "Perilaku khas {{playerSide}}",
"pmcresponse-victim_negative_46": "Aku mengharap lebih dari tingkat {{playerLevel}}",
"pmcresponse-victim_plead_1": "Aku lagi jalanin tugas",
"pmcresponse-victim_plead_7": "Kamu ga liatin aku goyang tah?!!",
"pmcresponse-victim_positive_17": "Adil, aim mu bagus",
"pmcresponse-victim_positive_18": "Nikmati harta ku",
"pmcresponse-victim_positive_5": "Sangat hokki",
"pmcresponse-victim_positive_9": "gg",
"port_already_in_use": "Port %s masih dipakai, cek apakah server masih berjalan",
"profile-unable_to_find_profile_by_id_cannot_delete": "Tidak dapat menghapus profil dengan id: %s, tidak ada profil dengan id tersebut",
"profile_save_callback_error": "Permasalahan ketika menjalankan onBeforeSaveCallback: {{callback}}, {{error}}",
"profile_saved": "%s Perubahan profil disimpan",
"quest-compare_operator_unhandled": "loyaltyRequirementCheck() operator %s tidak dapat ditangani, kembali ke false",
"quest-handover_wrong_item": "Tidak dapat menyerahkan barang untuk quest {{questId}}, tpl yang diminta: {{requiredTpl}} tpl yang diserahkan: {{handedInTpl}}",
"quest-item_not_found_in_inventory": "changeItemStack() barang dengan _id: %s tidak ditemukan di inventory",
"quest-no_skill_found": "Keahlian %s tidak ditemukan",
"quest-reward_type_not_handled": "Tipe hadiah quest: {{rewardType}} tidak ditangani untuk quest: {{questId}} nama: {{questName}}",
"quest-unable_to_find_compare_condition": "Metode perbandingan tidak dikenal: %s",
"quest-unable_to_find_matching_hideout_production": "Tidak dapat menemukan pembukaan resep markas yang cocok untuk quest: {{questName}}, kecocokan yang ditemukan: {{matchCount}}",
"ragfair-invalid_player_offer_request": "Tidak dapat mengajukan penawaran, permintaan tidak valid",
"ragfair-missing_barter_scheme": "generateFleaOffersForTrader() Gagal mencari barterScheme untuk id barang: {{itemId}} tpl: {{tpl}} di {{name}}",
"ragfair-no_trader_assorts_cant_generate_flea_offers": "Tidak dapat membuat penawaran pasar untuk vendor %s, barang tidak ditemukan",
"ragfair-offer_no_longer_exists": "Penawaran sudah tidak ada lagi",
"ragfair-offer_not_found_in_profile_short": "Penawaran tidak ditemukan di profil",
"ragfair-offer_not_found_unable_to_hide": "hideItem() offerId: %s tidak ditemukan, tidak dapat menyembunyikan penawaran",
"ragfair-tpl_not_a_valid_item": "generateFleaOffersForTrader() tpl: %s bukan barang valid, dilewati",
"ragfair-unable_to_adjust_stack_count_assort_not_found": "Vendor: {{traderId}} penawaran: {{offerId}} tidak dapat menyesuaikan jumlah tumpukan supaya sesuai dengan nilai barang vendor (barang tidak ditemukan)",
"ragfair-unable_to_find_item_in_inventory": "Tidak dapat menemukan barang dengan id: {{id}} dalam inventory",
"ragfair-unable_to_find_locale_by_key": "Tidak dapat menemukan lokal EFT dengan key: %s",
"ragfair-unable_to_find_offer_to_remove": "Tidak dapat menemukan tawaran dengan id: %s untuk dihapus",
"ragfair-unable_to_find_preset_with_id": "Tidak dapat menemukan preset dengan id: %s, menggunakan basis harga senjata yang sudah ada",
"ragfair-unable_to_find_requested_items_in_inventory": "Tidak dapat menemukan barang yang diminta dalam inventory",
"ragfair-unable_to_pay_commission_fee": "Tidak dapat membayar biaya komisi: %s rubel",
"ragfair-unable_to_place_offer_with_no_requirements": "Tidak dapat mengajukan tawaran tanpa biaya",
"ragfair-unable_to_purchase_0_count_item": "Tidak dapat membeli barang: %s dengan jumlah 0",
"ragfair-unable_to_remove_offer_doesnt_exist": "Tidak dapat menghapus tawaran dengan id: %s karena tidak dapat ditemukan di pasar",
"ragfair-unable_to_remove_offer_not_found_in_profile": "Tidak dapat menemukan tawaran: {{offerId}} dalam profil: {{profileId}} karena tawaran tidak terdefinisi, membuatkan",
"release-beta-disclaimer-accept": "Pengguna menerima peringatan penggunaan beta",
"release-illegal-plugins-exception": "Mod klien terdeteksi. Mod tidak dinyalakan untuk BleedingEdge/versi pengujian SPT\n- Tolong dihapuskan sebelum melanjut!",
"repair-unable_to_find_item_in_db": "Tidak dapat memperbaiki barang: %s, tidak bisa dicari di database barang, tidak bisa menambahkan poin keahlian perbaikan",
"repeatable-accepted_repeatable_quest_not_found_in_active_quests": "Menerima quest berulang: %s yang tidak dapat ditemukan di array activeQuests. Tolong laporkan bug ini",
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive": "Membuat Quest Penyelesaian: Tidak ada barang tersisa. Daftar putih terlalu kecil atau Daftar hitam terlalu ketat",
"repeatable-difficulty_was_nan": "Pembuatan hadiah yang diulang: Kesulitan merupakan NaN. Diubah menjadi 1.",
"repeatable-no_reward_item_found_in_price_range": "Pembuatan Hadiah yang Diulang: Tidak ditemukan barang dengan jarak harga {{minPrice}} sampai {{roublesBudget}}",
"repeatable-quest_handover_failed_condition_already_satisfied": "Permasalahan dalam penyerahan quest: kondisi sudah terpenuhi? qid: {{questId}}, kondisi: {{conditionId}}, profileCounter: {{profileCounter}}, nilai: {{value}}",
"repeatable-unable_to_accept_quest_see_log": "Tidak dapat menerima quest, lihat catatn server untuk detil",
"repeatable-unable_to_accept_quest_starting_message_not_found": "Tidak dapat menerima quest: {{questId}} tidak dapat mencari pesan quest mulai dengan id: {{messageId}}",
"route_onupdate_no_response": "onUpdate: rute %s tidak melaporkan sukses atau gagal",
"scav-missing_karma_level_getting_default": "getScavKarmaLevel() gagal, tidak dapat mencari fence di profile.traderInfo. Kembali ke karma level 0",
"scav-missing_karma_settings": "Tidak bisa mendapatkan pengaturan karma untuk level %s",
"scheduled_event_failed_to_run": "Event terjadwal: '%s' gagal dijalankan.",
"seasonal-missing_equipment_slot_on_bot": "Tidak bisa menghapus perlengkapan natal dari slot: {{equipmentSlot}} karena tidak ditemukan di bot: {{botRole}}",
"seasonal-missing_loot_container_slot_on_bot": "Tidak bisa menghapus barang natal dari slot: {{lootContainer}} karena tidak ditemukan di bot: {{botRole}}",
"server_running": "Server sedang berjalan, jangan dimatikan saat main SPT",
"server_start_meme_1": "Hidup, Tertawa, Cinta",
"server_start_meme_2": "Wibu bau bawang",
"server_start_meme_20": "Kau tahu tidak, 9 dari 10 pengguna tidak bisa baca pesan ini",
"server_start_meme_7": "bogos binted",
"server_start_player_active_botreload_skill": "Karakter anda memiliki keahlian 'BotReload' aktif, hal ini akan membuat senjata anda isi ulang cepat sekali, abaikan pesan ini jika disengaja",
"started_webserver_success": "Webserver dimulai pada %s",
"trader-missing_durability_threshold_value": "Tidak dapat menemukan nilai ambang batas durabilitas untuk vendor: {{traderId}}, kembali ke nilai: {{value}}",
"trader-missing_trader_details_using_default_refresh_time": "Vendor: {{traderId}} tidak ditemukan, membuatkan entry sementara dengan waktu penyegaran default: {{updateTime}}",
"trader-price_multipler_is_zero_use_default": "traderPriceMultipler adalah 0, ini tidak valid, diganti menjadi 0.01",
"trader-unable_to_delete_stale_purchases": "Tidak dapat memproses pembelian barang vendor di profil: {{profileId}} karena vendor: {{traderId}} tidak ditemukan, dilewati",
"trader-unable_to_find_profile_by_id": "Tidak bisa menemukan profil dengan id: %s",
"trader-unable_to_find_profile_with_id": "Tidak dapat menemukan profil dengan sessionid: %s",
"trader-unable_to_find_trader_by_id": "Tidak bisa menemukan pedagang dengan id: %s",
"unhandled_response": "[UNHANDLED][%s]",
"unknown_request": "Permintaan tidak diketahui!",
"validation_error_decode": "Tidak dapat membaca checks.dat. Validasi berkas dilewati.",
"validation_error_exception": "Pengecualian tertangkap saat mencoba validasi berkas: %s",
"validation_error_file": "Validasi berkas gagal untuk berkas: %s",
"validation_not_found": "Berkas checks.dat tidak ditemukan. Validasi berkas dilewatkan.",
"watermark-commercial_use_prohibited": "Penggunaan komersial dilarang",
"watermark-discord_url": "https://discord.sp-tarkov.com",
"watermark-do_not_report": "TIDAK USAH DILAPORKAN",
"watermark-free_of_charge": "Hasil kerja ini tidak memungut biaya apapun",
"watermark-modding_disabled": "SERVER MODDING DINONAKTIFKAN DALAM VERSI INI",
"watermark-no_support": "BANTUAN TIDAK AKAN DIBERIKAN",
"watermark-not_an_issue": "INI BUKAN PERMASALAHAN",
"watermark-paid_scammed": "Jika anda dipungut biaya, anda telah ditipu",
"watermark-report_issues_to": "LAPORKAN PERMASALAHAN KE",
"watermark-testing_build": "INI ADALAH VERSI TESTING",
"watermark-use_at_own_risk": "GUNAKAN DENGAN RISIKO ANDA SENDIRI",
"websocket-message_send_failed_with_error": "[WS] sendMessage gagal, dengan error: %s",
"websocket-message_sent": "[WS] pesan terkirim",
"websocket-not_ready_message_not_sent": "[WS] Soket belum siap untuk %s, pesan tidak terkirim",
"websocket-pinging_player": "[WS] notifikasikan player: %s",
"websocket-player_connected": "[WS] Player: {{sessionId}} {{contextId}} tersambung",
"websocket-received_message": "[WS] Menerima pesan dari pengguna %s ",
"websocket-started": "Websocket mulai di %s"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,425 +1,425 @@
{
"assort-missing_loyalty_level_object": "stripQuestAssort(): Sortering for Handler {{traderId}} inneholder ikke loyal_level_items data, hopper over fjerning av oppdrags sortering",
"assort-missing_quest_assort_unlock": "Kan ikke finne tilsvarende {{traderName}} oppdrags opplåsing for oppdrag: {{questName}}. Fullføring av dette oppdraget vil ikke belønne en handelselement for kjøp",
"assort-missing_questassort": "stripQuestAssort(): Assort for Trader: %s inneholder ikke et questassort json, hopper over fjerning av nest assorts",
"baseclass-item_not_found": "%s ble ikke funnet i elementets basis-mellomlager, regenererer hurtigbuffer",
"baseclass-item_not_found_failed": "Elementet %s ble fremdeles ikke funnet i base-mellomlager etter regenerering",
"baseclass-missing_db_no_cache": "Databasen var tom, kan ikke generere elementets buffer",
"bleeding_edge_build": "BLEEDINGEDGE",
"bot-compatibility_check_missing_props": "Kan ikke validere element: {{id}} {{name}} i plass:{{slot}} kan brukes, det mangler a _props verdi",
"bot-generation_failed": "bot generering mislykket. se serverloggen for ytterligere detaljer",
"bot-incompatible_ammo_for_weapon_falling_back_to_default": "Inkompatibel ammunisjon {{chosenAmmo}} ble funnet for {{weaponId}} - {{weaponName}}, faller tilbake til standard: {{defaultAmmo}}",
"bot-invalid_item_compatibility_check": "Kan ikke kontrollere elementets kompatibilitet med utstyrte elementer, ønsket element: {{itemTpl}} felt: {{slot}} er ikke et gyldig produkt",
"bot-item_missing_props_property": "Artikkel {{itemTpl}} {{name}} mangler a _props egenskap",
"bot-item_spawn_limit_reached_skipping_item": "{{botRole}} kan ikke fremkalle element {{itemName}} etter {{attempts}} forsøk, ignorerer fremkallingsgrense",
"bot-loot_type_not_found": "Loot mellomlager feilet for loot: {{lootType}} på bot: {{botRole}}, var en pmc: {{isPmc}}",
"bot-missing_application_context": "applikasjonsKontekst kunne ikke finne %s verdi. Har du startet serveren på nytt uten å starte spillet på nytt?",
"bot-missing_cartridge_slot": "Kan ikke legge til patroner til våpenet fordi modPool ikke inneholder patroner for et sylinderMagasin %s, hopper over",
"bot-missing_container_with_tpl": "Kunne ikke finne beholder mal med tpl.: %s",
"bot-missing_equipment_settings": "Bot {{botRole}} mangler utstyrsinnstillingene: ikke mulig å oppnå verdi for: {{setting}}, faller tilbake til standard for: {{defaultValue}}",
"bot-missing_equipment_settings_property": "Bot {{botRole}} mangler utstyrsinnstillingene: ikke mulig å oppnå verdi for: {{setting}}, faller tilbake til standard for: {{defaultValue}}",
"bot-missing_item_template": "Kan ikke finne objektmal med tpl: %s",
"bot-missing_saved_match_info": "getBotCap() kan ikke få lagret match-informasjon, faller tilbake til standard. Restartet du serveren og ikke klienten?",
"bot-missing_weapon_preset": "Kunne ikke finne forhåndsinstilling for våpen med tpl: %s",
"bot-mod_not_in_slot_filter_list": "Modd: {{modId}} ble ikke funnet i kompatibelt elementfilter for plass: '{{modSlot}}' for element: {{parentName}}, hopper over - {{botRole}}",
"bot-mod_slot_missing_from_item": "Slot '{{modSlot}}' eksisterer ikke for elementet: {{parentId}} {{parentName}} på {{botRole}}",
"bot-no_ammo_found_in_bot_json": "Kan ikke finne ammunisjon for bot type: %s",
"bot-no_bot_cap_found_for_location": "Ingen områdebegrensning funnet for bot: %s, bruker standard",
"bot-no_bot_type_in_cache": "ADVARSEL - Bot mellomlager har ingen kunnskap om type %s",
"bot-no_caliber_data_for_weapon_falling_back_to_default": "Kan ikke finne kaliber-data for {{weaponId}} - {{weaponName}}, faller tilbake til standard ammunisjon: {{defaultAmmo}}",
"bot-no_compatible_camora_ammo_found": "Kunne ikke finne en kompatibel ammunisjon for plass: %s. Fylling av kamora hoppet over",
"bot-no_item_template_found_when_adding_mod": "Kan ikke finne mal for modifikasjonselement med tpl: {{modId}} for plass {{modSlot}}",
"bot-no_spawn_chance_defined_for_equipment_slot": "Ingen fremkallingsmulighet ble definert for utstyr: %s",
"bot-single_bot_generation_not_found_in_cache": "Bot: %s ikke funnet i mellomlager, genererer på nytt, dette kan føre til at spillet lagger",
"bot-unable_to_add_mod_item_invalid": "Mod: {{itemName}} er ikke et gyldig produkt, ikke mulig å legge til i plass: '{{modSlot}}' på produkt: {{parentItemName}}, hopper over",
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot": "Kan ikke legge til modifikasjoner på våpen: {{weaponName}} {{weaponId}} fordi det mangler plasser, patroner eller kammer - {{botRole}}",
"bot-unable_to_edit_limits_of_unknown_map": "Kan ikke redigere bot grenser på kartet: %s fordi det ikke ble funnet",
"bot-unable_to_filter_mod_slot_all_blacklisted": "Kan ikke generere filtrert dynamisk våpen-modifikasjons pool fordi svartelisten har filtrert ut alle moddene for plass: %s, ignorerer svarteliste og regenererer pool",
"bot-unable_to_filter_mods_all_blacklisted": "Kan ikke filtrere modifikasjoner for plass: {{slotName}} på {{itemName}} ettersom de alle var svartelistet, ignorerer svartelisten",
"bot-unable_to_find_ammo_item": "Kan ikke finne ammunisjonsmal med tpl: %s",
"bot-unable_to_find_bot_in_cache": "Kan ikke finne bot i mellomlager med navn: %s",
"bot-unable_to_find_default_magazine_item": "Kunne ikke finne magasin mal: %s i databasen",
"bot-unable_to_find_loot_n_value_for_bot": "Finner ikke loot N verdi for bot: %s, bruker scav n verdi i stedet",
"bot-unable_to_find_magazine_item": "Kunne ikke finne magasin mal: %s i databasen",
"bot-unable_to_find_spawn_limits_fallback_to_defaults": "Kan ikke finne genererings grenser for rolle: %s, faller tilbake til standard",
"bot-unable_to_get_bot_difficulty_fallback_to_assault": "Finner ikke bot: {{botType}} vanskelighetsgrad {{difficulty}}, bruker angreps vanskelighetsgrad som reserveløsning",
"bot-unable_to_get_bot_fallback_to_assault": "Finner ikke bot: JSON, %s JSON, bruker en angreps bot som reserveløsning",
"bot-weapon_generated_incorrect_using_default": "Våpen %s ble generert feil, faller tilbake til våpenets forhåndsinnstilling, se feil ovenfor",
"bot-weapon_missing_magazine_or_chamber": "Våpen med tpl: {{weaponId}} har ingen magasin eller kammer - {{botRole}}",
"bot-weapon_missing_mod_slot": "Slot '{{modSlot}}' eksisterer ikke for elementet: {{weaponId}} {{weaponName}} på {{botRole}}",
"bot-weapons_required_slot_missing_item": "Påkrevd plass '{{modSlot}}' på {{modName}} {{slotId}} var tom på {{botRole}}",
"client_request": "[Client Request] %s",
"customisation-item_already_purchased": "Kles-element {{itemId}} {{itemName}} er allerede kjøpt",
"customisation-unable_to_find_clothing_item_in_inventory": "Kles-element ikke funnet i inventaret med ID: %s",
"customisation-unable_to_find_suit_by_id": "Kan ikke finne kjøpmannens drakttilbud med id: %s",
"dialog-missing_item_template": "Kan ikke finne artikkelmal {{tpl}} i db, kan ikke sende melding av type {{type}}, hopper over",
"event-unhandled_event": "[UNHÅNDTERT HENDELSE] %s",
"executing_startup_callbacks": "Server: Utfører start-callbacks...",
"fence-unable_to_find_assort_by_id": "Kunne ikke finne Fence assortement for id: %s",
"fixer-updated_pockets": "Oppdaterte 'pocket' elementet til ny versjon 18876 med x3 spesialplasser",
"gameevent-bot_not_found": "addEventGearToScavs() - ute av stand til å finne bot av type %s i databasen, hopper over",
"gameevent-no_gear_data": "Ingen utstyrsdata i sesonghendelser.json config for hendelse %s",
"health-healing_item_not_found": "Finner ikke helbredings element %s i spillerens inventar",
"health-unable_to_find_item_to_consume": "Finner ikke et forbruksvare %s i spillerens inventar",
"hideout-missing_recipe_for_area": "Kunne ikke finne oppskrift: %s for områdetype",
"hideout-missing_recipe_in_db": "Kunne ikke lokalisere oppskriften med _id: %s",
"hideout-no_bitcoins_to_collect": "Ingen bitcoins er klar for henting",
"hideout-unable_to_find_area": "Kan ikke finne gjemmestedsområde: %s i profilen",
"hideout-unable_to_find_area_in_database": "Finner ikke området: %s i databasen",
"hideout-unable_to_find_item_in_inventory": "Finner ikke element i inventaret med id %s",
"hideout-unable_to_find_item_to_remove_from_area": "Kan ikke finne noen element å fjerne fra plass i området: %s",
"hideout-unable_to_find_production_in_profile_by_recipie_id": "Kan ikke finne produksjonsoppskrift Id: %s i profilen",
"hideout-unable_to_find_scav_case_recipie_in_database": "Finner ikke Scav Case oppskrift med id: %s i databasen",
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory": "Finner ikke elementet: %s forespurt av ScavCase",
"hideout-unhandled_remove_item_from_area_request": "Uhåndtert forsøk på å fjerne elementet fra gjemmestedsområdet: %s",
"http-unknown_error": "En ukjent feil har oppstått",
"importing_database": "Importerer database...",
"importing_database_finish": "Database import ferdig",
"importing_spt_configs": "Importerer konfigurasjoner...",
"inraid-missing_standing_for_kill": "Finner ikke omdømmenivå for {{victimSide}}:{{victimRole}}",
"insurance-missing_insurance_price_multiplier": "Ingen forsikringsmultiplikator funnet for kjøpmann: %s, sjekk at det finnes i InsuranceConfig.js, faller tilbake til standardverdien på: 0.3",
"inventory-edit_trader_item": "Kan ikke redigere et kjøpmannselement",
"inventory-examine_item_does_not_exist": "examineItem() - Ingen ID med %s funnet",
"inventory-fill_container_failed": "fillContainerMapWithItem() returnerte med en feil %s",
"inventory-get_item_size_item_not_found_by_tpl": "getSizeByInventoryItemHash() Item with tpl: %s ble ikke funnet",
"inventory-invalid_item_missing_from_db": "Kan ikke hente gjenstand: %s fra db",
"inventory-invalid_move_to_container": "Forsøkte å flytte element med plass id: {{slotId}} til {{container}}, profilkorrupsjon ble forhindret",
"inventory-item_missing_props_property": "Gjenstands tpl: {{itemTpl}} name: {{itemName}} mangler rekvisitt-egenskaper. størrelse på denne kan ikke oppnås",
"inventory-item_to_toggle_missing_upd": "Inventar element med _id: %s mangler et upd objekt, legger til",
"inventory-missing_stash_size": "Kan ikke bestemme stash-størrelse fordi det ikke finnes stash i spillerens inventar",
"inventory-no_stash_space": "Ikke nok lagringsplass",
"inventory-return_default_size": "Standardiserer gjenstand %s til størrelse 1x1",
"inventory-stash_not_found": "Kan ikke finne stash %s i db",
"inventory-unable_to_fill_container": "[OOB] for gjenstand: {{id}}; Feilmelding: {{error}}",
"inventory-unable_to_find_item": "getExaminedItemTpl() Kan ikke finne gjenstand med tpl: %s i databasen eller på markedet",
"inventory-unable_to_find_stash": "Finner ingen stash",
"inventory-unable_to_toggle_item_not_found": "Kan ikke bytte inventar med id: %s, gjenstanden ble ikke funnet",
"item-durability_value_invalid_use_default": "getRepairableItemQualityValue() weapon tpl: %s holdbarhetsverdi er ugyldig, gjenoppretter standardverdi til 1",
"item-invalid_tpl_item": "Kan ikke finne et element med tpl på: %s i Db",
"launcher-missing_property": "Profil: %s mangler en descriptionLocaleKey egenskap",
"launcher-profile-edgeofdarkness": "Samme som Prepare To Escape, pluss; større stash-størrelse (10x68), ekstra utstyr/gjenstander, høyere startomdømme med handlere, 1000 dollar, 500 euro",
"launcher-profile_leftbehind": "Samme som standard, pluss; større stash-størrelse (10x38), ekstra utstyr/elementer, 500 dollar",
"launcher-profile_preparetoescape": "Samme som Left Behind, pluss; større stash-størrelse (10x48), ekstra utstyr/gjenstander, høyere omdømme med handelsfolk, 250 euro",
"launcher-profile_sptdeveloper": "Testprofil, startnivå er level 69, masse Rubler/Dollar/Euro, USEC starter med alle oppdrag som er klare for å starte, BEAR starter med alle oppdrag klare til å levere, uovervinnelighets-balaklava",
"launcher-profile_sptzerotohero": "Start med nesten ingenting, ingen Rubler/Dollar/Euro, ingen omdømme hos kjøpmenn, 1 kniv, ingen oppdrag fullført",
"launcher-profile_standard": "Samme som live, grunnleggende stash-størrelse (10x28), 500.000 Rubler",
"linux_use_priviledged_port_non_root": "Ikke-rot-prosesser kan ikke binde til porter under 1024",
"location-containers_generated_success": "Totalt ble %s statiske beholdere generert",
"location-critical_error_see_log": "En kritisk feil oppsto ved generering av loot, se server Logg for detaljer",
"location-dynamic_items_spawned_success": "Totalt er %s dynamiske elementer generert",
"location-generated_success": "Lokasjon generert",
"location-missing_dynamic_template": "Valgt dynamisk startpunkt %s har ingen mal, hopper over",
"location-missing_root_item": "createItem() feilet, rotelement er null, tpl: {{tpl}}, parentId: {{parentId}}",
"location-preset_not_found": "forhåndsinnstilling ikke funnet for {{tpl}}, standardinnstilling: {{defaultId}} navn: {{defaultName}}, parentid: {{parentId}}",
"location-spawn_point_count_requested_vs_found": "{{requested}} spawnpoints var forespurt mens {{found}} er tilgjengelig {{mapName}}",
"location-spawnpoint_missing_items": "Valgt dynamisk spawnpoint %s har ingen gjenstander, hopper over",
"location-unable_to_find_airdrop_drop_config_of_type": "Finner ikke konfigurasjonsinnstillingene for flyslipp for typen: %s, faller tilbake for slipptype: blandet ",
"location-unable_to_fix_broken_waves_missing_base": "%s har ingen base json, hopper over kartbølge rettelser",
"location-unable_to_reparent_item": "createItem() feilet, ikke mulig å gjennomføre re-parent {{tpl}}, parentId: {{parentId}}",
"loot-item_missing_parentid": "Artikkel: %s mangler en parentId verdi. ikke mulig å bruke artikkel som bytte",
"loot-non_item_picked_as_sealed_weapon_crate_reward": "Ugyldig våpen: %s, ble valgt som belønning for forseglet våpenkasse, ikke mulig å opprette bytte",
"mailsend-missing_parent": "Kan ikke finne et element med slotId på: gjemmested for melding til: {{traderId}} sender: {{sender}}",
"mailsend-missing_trader": "Kan ikke sende meldingstype: {{messageType}} til spiller: {{sessionId}}, siden kjøpmanns enum var null",
"mod-send_bundle_url": "[BUNT]: %s",
"modloader-async_mod_error": "ModLoader: Feil ved lasting av async mod: %s",
"modloader-checked": "sjekket",
"modloader-checking_mod": "sjekker: %s",
"modloader-cyclic_dependency": "Syklisk avhengighet oppdaget. Denne feilen må løses. Serveren kan ikke starte før dette er fikset og vil stoppe",
"modloader-dependency_container_not_initalized": "Avhengighetsbeholderen ble forespurt, men den ble ikke initialisert",
"modloader-error_parsing_mod_load_order": "Feil under parsing av mod lastings rekkefølge",
"modloader-incompatibilities_not_string_array": "Mod %s package.json egenskaps 'inkompatibiliteter' bør være en streng array",
"modloader-incompatible_mod_found": "Mod {{author}}{{name}} er inkompatibel med {{incompatibleModName}}",
"modloader-installing_external_dependencies": "Installerer avhengigheter for Mod: {{name}} av: {{author}}",
"modloader-installing_external_dependencies_disabled": "Mod: {{name}} av: {{author}} krever eksterne avhengigheter, men funksjonen er for øyeblikket deaktivert. gå til \"{{configPath}}\", sett \"{{configOption}}\" til sant, og start serveren på nytt.\nVed å aktivere dette godtar du alt ansvar for hva {{name}} laster ned til maskinen din.",
"modloader-invalid_version_property": "Mod %s pakke.json inneholder en ugyldig versjonstreng",
"modloader-is_client_mod": "Mod (%s) er en klientmod og skal plasseres i følgende mappe: /spt/bepinex/plugins",
"modloader-load_order_conflict": "`{{modOneName}}` og `{{modTwoName}}` har motstridende krav for innlastingsrekkefølge. serveren kan ikke starte før dette er fikset og vil slå seg av",
"modloader-loaded_mod": "Mod: {{name}} versjon: {{version}} av: {{author}} lastet inn",
"modloader-loading_mods": "ModLoader: laster inn %s server mods...",
"modloader-main_property_not_js": "Mod %s package.json hovedegenskap må være en .js-fil",
"modloader-main_property_points_to_nothing": "Mod %s package.json hovedegenskap peker til en ikke-eksisterende fil",
"modloader-missing_dependency": "Mod {{mod}} krever at {{modDependency}} er installert.",
"modloader-missing_package_json": "Mod (%s) mangler package.json. Sørg for at du har sjekket mods hub-siden for installasjonsinstruksjoner",
"modloader-missing_package_json_property": "Mod {{modName}} package.json krever {{prop}} egenskap",
"modloader-mod_has_no_main_property": "ModLoader: Mod (%s) er ikke kompatibel. Den mangler en hovedegenskap",
"modloader-mod_order_error": "ModLoader: Feil ble funnet i order.json, VIL BRUKE STANDARD LASTEREKKEFØLGE",
"modloader-mod_order_missing": "ModLoader: order.json mangler, oppretter...",
"modloader-mod_order_missing_from_json": "ModLoader: Mod %s mangler fra order.json, legger til",
"modloader-no_mods_loaded": "Feil ble funnet med mods, INGEN MODS VIL BLI LASTET",
"modloader-not_correct_mod_folder": "En mappe kalt (%s) finnes i din mods-mappe. Du har installert en mod på feil måte. Du kan ha utpakket ut innholdet av en mod direkte inn i mod mappen ved en feil. Se nettstedets FAQ og mods hub-sider angående hvordan du installerer mods riktig",
"modloader-outdated_dependency": "Mod {{mod}} krever {{modDependency}} versjon {{requiredVersion}}. Gjeldende installerte versjon er {{currentVersion}}",
"modloader-skipped_mod": "Hoppet over lasting av Mod: {{mod}}",
"modloader-user_mod_folder_missing": "ModLoader: user/mod mappe mangler, oppretter...",
"modloader-visited": "besøkt",
"modloader-x_duplicates_found": "Du prøver å laste inn mer enn en versjon av %s mod. Hopper over alle.",
"openzone-unable_to_find_map": "Kan ikke legge til soner til plassering: %s fordi den ikke finnes",
"payment-not_enough_money_to_complete_transation": "Profilen hadde ikke nok penger til å fullføre transaksjonen: Nødvendig {{amountToPay}}, har {{amountAvailable}}",
"player-attempt_to_increment_skill_with_negative_value": "Kan ikke øke ferdigheten: %s med et negativt beløp",
"pmc-name_prefix_1": "Engleaktig",
"pmc-name_prefix_10": "Selvsikker",
"pmc-name_prefix_11": "Sjarmerende",
"pmc-name_prefix_12": "Sofistikert",
"pmc-name_prefix_13": "Elegant",
"pmc-name_prefix_14": "Guddommelig",
"pmc-name_prefix_15": "Uærlig",
"pmc-name_prefix_16": "Grådig",
"pmc-name_prefix_17": "Delvis skallet",
"pmc-name_prefix_18": "Attraktiv",
"pmc-name_prefix_19": "Barnslig",
"pmc-name_prefix_20": "Demonisk",
"pmc-name_prefix_21": "Ydmyk",
"pmc-name_prefix_22": "Fantastisk",
"pmc-name_prefix_23": "Letargisk",
"pmc-name_prefix_24": "Nervøs",
"pmc-name_prefix_27": "Rastløs",
"pmc-name_prefix_29": "Fortydelig",
"pmc-name_prefix_3": "Sliten",
"pmc-name_prefix_30": "Jentesjef",
"pmc-name_prefix_31": "Boss-Babe",
"pmc-name_prefix_32": "Konge",
"pmc-name_prefix_33": "Sjef",
"pmc-name_prefix_34": "Vanskelig",
"pmc-name_prefix_35": "Seriøs",
"pmc-name_prefix_36": "Bærbar",
"pmc-name_prefix_37": "Mistenkelig",
"pmc-name_prefix_38": "Kleint",
"pmc-name_prefix_39": "Dank",
"pmc-name_prefix_40": "Geit",
"pmc-name_prefix_41": "Zesty",
"pmc-name_prefix_42": "Falsk",
"pmc-name_prefix_43": "Hevngjerrig",
"pmc-name_prefix_44": "Forvirret",
"pmc-name_prefix_45": "Sensuell",
"pmc-name_prefix_46": "Fet",
"pmc-name_prefix_47": "Stor",
"pmc-name_prefix_48": "Swole",
"pmc-name_prefix_49": "Mistenkelig",
"pmc-name_prefix_5": "Sint",
"pmc-name_prefix_6": "Ærlig",
"pmcresponse-killer_negative_1": "ty 4 gratis bytte",
"pmcresponse-killer_negative_10": "Prøv idet minste å sloss neste gang a...",
"pmcresponse-killer_negative_11": "Jeg tror du trenger å øve litt til",
"pmcresponse-killer_negative_12": "Prøv i det minste å være litt utfordrende neste gang",
"pmcresponse-killer_negative_13": "Rip lille timmy",
"pmcresponse-killer_negative_14": "Enda en skitten liten rotte tatt hånd om",
"pmcresponse-killer_negative_15": "Det der var bare flaut å se på",
"pmcresponse-killer_negative_16": "Jeg forventet i det minste LITT motstand... jaja",
"pmcresponse-killer_negative_17": "Jeg håper ikke du forsikret utstyret ditt, for det får du ikke tilbake",
"pmcresponse-killer_negative_18": "Jeg har en youtube serie om hvordan man kan bli bedre på tarkov hvis du er interessert",
"pmcresponse-killer_negative_19": "Enda et gjenkjenningsmerke til samlingen min",
"pmcresponse-killer_negative_2": "Takk for det nye kittet",
"pmcresponse-killer_negative_20": "Du er så dårlig at du bør spille spt i stedet",
"pmcresponse-killer_negative_3": "Ikke rart du døde, våpenet ditt er søppel",
"pmcresponse-killer_negative_4": "Hvorfor har du på deg den kroppspansringen? lmao",
"pmcresponse-killer_negative_5": "lmaoooo",
"pmcresponse-killer_negative_6": "Ikke bekymre deg, utstyret ditt vil være på markedet snart",
"pmcresponse-killer_negative_7": "Ikke så rart du spiller SPT, så dårlig som du sikter!",
"pmcresponse-killer_negative_8": "Det er som det er.",
"pmcresponse-killer_negative_9": "Takk for at du samlet loot til meg",
"pmcresponse-killer_plead_10": "Typisk {{playerSide}} oppførsel",
"pmcresponse-killer_plead_11": "Jeg trenger {{playerSide}} kills, håper du forstår",
"pmcresponse-killer_plead_4": "Vi sees neste gang",
"pmcresponse-killer_plead_7": "Jeg fant endelig kroppen din og alt du har er søppel",
"pmcresponse-killer_plead_8": "Jeg sverger at du har drept meg før",
"pmcresponse-killer_plead_9": "Typisk oppførsel til en {{playerSide}}",
"pmcresponse-killer_positive_1": "God kamp",
"pmcresponse-killer_positive_10": "Hva enn du matet det våpenet med, så ødela det all kroppspansringen min. God kamp!",
"pmcresponse-killer_positive_11": "Ingenting personlig, må bare få disse Jaeger-oppdragene fullført",
"pmcresponse-killer_positive_12": "Jeg ble ganske bekymret der en stund",
"pmcresponse-killer_positive_13": "Imponerende ferdigheter {{PlayerName}}",
"pmcresponse-killer_positive_14": "Respekt, du ga meg en god kamp",
"pmcresponse-killer_positive_15": "Ren kamp, respekt",
"pmcresponse-killer_positive_16": "Det var en ekte katt og mus kamp, fantastisk",
"pmcresponse-killer_positive_17": "Vi er så tilbake",
"pmcresponse-killer_positive_2": "Du sloss bra",
"pmcresponse-killer_positive_3": "Jeg gjemte utstyret ditt",
"pmcresponse-killer_positive_4": "Du tok emg nesten! flott kamp!",
"pmcresponse-killer_positive_5": "Godt spilt, du hadde meg nesten",
"pmcresponse-killer_positive_6": "Der tok du meg nesten",
"pmcresponse-killer_positive_7": "Hvis jeg ikke hadde overrasket deg, hadde jeg vært dau",
"pmcresponse-killer_positive_8": "God kamp",
"pmcresponse-killer_positive_9": "Godt kjempet",
"pmcresponse-suffix_15": "Kompis",
"pmcresponse-suffix_16": "amigo",
"pmcresponse-suffix_17": "kompis",
"pmcresponse-suffix_18": "dude",
"pmcresponse-suffix_19": "m8",
"pmcresponse-suffix_20": ":)",
"pmcresponse-suffix_21": "(:",
"pmcresponse-suffix_22": ":))))))",
"pmcresponse-suffix_23": "GG",
"pmcresponse-suffix_24": "kjære kompis",
"pmcresponse-suffix_25": "venn",
"pmcresponse-suffix_26": "gutt",
"pmcresponse-suffix_27": "nerd",
"pmcresponse-victim_negative_100": "Jeg tipper du installerte SAIN og måtte fjerne det siden du ble drept så mye",
"pmcresponse-victim_negative_16": "Hvis det var en rettferdig kamp ville jeg vunnet",
"pmcresponse-victim_negative_17": "Jeg håper du selger ditt bytte til feil Kjøpmann",
"pmcresponse-victim_negative_18": "Jeg håper du legger ut byttet ditt på markedet for feil pris",
"pmcresponse-victim_negative_19": "1v1 meg nerd, jeg vinner",
"pmcresponse-victim_negative_20": "Fikk klikk på våpenet mitt, ellers hadde jeg tatt deg",
"pmcresponse-victim_negative_21": "Du er litt av ei rotte",
"pmcresponse-victim_negative_22": "Wow, gjemmer deg i et hjørne som ei rotte? utrolig...",
"pmcresponse-victim_negative_23": "Jeg håper du slår tåa di i bordkanten",
"pmcresponse-victim_negative_24": "Hey! Hvorfor drepte du meg? jeg sier det til mamma",
"pmcresponse-victim_negative_25": "Rapportert",
"pmcresponse-victim_negative_26": "Min mor syntes jeg burde ha vunnet den kampen",
"pmcresponse-victim_negative_27": "Jøss, å knerte en noob som meg må vel gjøre deg skikkelig stolt",
"pmcresponse-victim_negative_28": "Jeg vedder på at du spiller SPT fordi du jukser i live",
"pmcresponse-victim_negative_29": "Du head-eyesd meg, mr hackerman",
"pmcresponse-victim_negative_30": "Flott head-eyes cheat...",
"pmcresponse-victim_negative_31": "Hvis jeg hadde hatt penger nok til et skikkelig kitt hadde det vært du som var dau, og ikke jeg.",
"pmcresponse-victim_negative_32": "du tok meg, men jeg tviler sterkt på at du fant det svarte nøkkelkortet jeg hadde.",
"pmcresponse-victim_negative_33": "Du tok meg kanskje, men jeg tviler på at du fant det termiske siktet jeg hadde.",
"pmcresponse-victim_negative_34": "Jeg sluttet å spille live på grunn av esp og likevel er du her...",
"pmcresponse-victim_negative_35": "Ja ,du tok meg, men jeg tar mer enn deg i benk!",
"pmcresponse-victim_negative_36": "Du er like flink til å navigere som Christopher Columbus",
"pmcresponse-victim_negative_37": "Jeg vedder på at du høres ut som du spiser sigaretter.",
"pmcresponse-victim_negative_38": "Du skyter som en gammal mann. Er det dette du gjør nå som du er pensjonist?",
"pmcresponse-victim_negative_39": "Jeg vedder på at du ser ut som om jeg tegnet deg med venstrehanda mi",
"pmcresponse-victim_negative_40": "Si til mora di at hu må lage mac'n cheese. Jeg kommer straks",
"pmcresponse-victim_negative_41": "Hvis du var noe mer innavlet ville du vært et smørbrød",
"pmcresponse-victim_negative_42": "Din illeluktende lille tusse",
"pmcresponse-victim_negative_43": "Det var ingen behov for vold",
"pmcresponse-victim_negative_44": "1 v 1 meg i dorms når som helst",
"pmcresponse-victim_negative_45": "Typisk {{playerSide}} oppførsel",
"pmcresponse-victim_negative_46": "Jeg forventet bedre fra en på level {{playerLevel}}",
"pmcresponse-victim_negative_47": "Det tapet sugde, men jeg kommer og tar deg i det neste raidet!",
"pmcresponse-victim_negative_48": "Bare vent til neste raid! jeg gjør ikke samme feil igjen",
"pmcresponse-victim_negative_49": "Det der var uventet. Du var heldig denne gangen",
"pmcresponse-victim_negative_50": "Jeg hadde deg nesten! Du slipper ikke unna neste gang",
"pmcresponse-victim_negative_51": "Du var heldig denne gangen! Det blir ikke like lett neste gang",
"pmcresponse-victim_negative_52": "Jeg tok noen dårlige valg denne gangen, men det blir ikke like lett neste gang!",
"pmcresponse-victim_negative_53": "Lykken var på din side, neste gang er det min tur",
"pmcresponse-victim_negative_54": "Jeg kommer tilbake sterkere, kompis. Bare vent og se!",
"pmcresponse-victim_negative_55": "Der kødda jeg det skikkelig til. Det skjer ikke igjen",
"pmcresponse-victim_negative_56": "Skal vedde på at du ikke så LedXen i riggen min",
"pmcresponse-victim_negative_57": "Null sjangs for at du fant labs-kortet jeg hadde i lomma",
"pmcresponse-victim_negative_58": "Håper du glemte å ta med deg de dyre nøklene jeg hadde...",
"pmcresponse-victim_negative_59": "Hadde akkurat nok tid til å putte tingene i gammaen",
"pmcresponse-victim_negative_60": "Hadde akkurat nok tid til å stappe tingene i ræva, så ingenting til deg!",
"pmcresponse-victim_negative_61": "Gammaen min var full av stæsj. Regner med din ikke var det?",
"pmcresponse-victim_negative_62": "Fullstendig rotte-adferd",
"pmcresponse-victim_negative_63": "Du er litt av ei rotte!",
"pmcresponse-victim_negative_64": "Du tok meg bare fordi jeg lagga!",
"pmcresponse-victim_negative_65": "Wow, du misbruker desync ofte du, eller hva?",
"pmcresponse-victim_negative_66": "På tide for deg å ta livet av noen i dag, antar jeg...",
"pmcresponse-victim_negative_67": "Skal vedde på at du satte AI-en på easy!",
"pmcresponse-victim_negative_68": "Du vant bare fordi jeg kødda det til",
"pmcresponse-victim_negative_69": "Lykkeskudd",
"pmcresponse-victim_negative_80": "Har du vurdert å gå ut av huset og se litt grønt gress av og til?",
"pmcresponse-victim_negative_81": "Gratulerer! Du drepte akkurat en kar som var travel med å leve sitt eget liv",
"pmcresponse-victim_negative_82": "Ja du drepte meg kanskje, men tror ikke du skal på date i kveld slik jeg skal (med en dame altså)",
"pmcresponse-victim_negative_83": "Jeg er en moderator på reddig og skal sørge for at du aldri får postet der igjen",
"pmcresponse-victim_negative_84": "Ja du drepte meg kanskje, men det er bare siden jeg er i en telefonsamtale med ei dame",
"pmcresponse-victim_negative_85": "Du tok meg bare på grunn av noen brukte en mikrobølgovn som forstyrrret wifi signalet mitt",
"pmcresponse-victim_negative_86": "Jeg døde kun på grunn av at jeg var på treningstudio tidligere og løfta sinsykt tungt",
"pmcresponse-victim_negative_87": "Jeg er en discord moderator så du burde passe deg, jeg er en storkar rundt her",
"pmcresponse-victim_negative_88": "Du ble reddet av at våpnet mitt låste seg",
"pmcresponse-victim_negative_89": "Du er heldig at fokuset mitt er på en apekatt jpg og ikke dette barnslige spillet, ellers ville du vært lei deg",
"pmcresponse-victim_negative_90": "Hvis hodet mitt ikke var fylt av anti jantelov tanker akkurat nå så ville du vært så død",
"pmcresponse-victim_negative_91": "Jeg lot deg faktisk drepe meg altså",
"pmcresponse-victim_negative_92": "Du gjorde meg en tjeneste, jeg skulle selge dette søppel utstyret til fence uansett",
"pmcresponse-victim_negative_93": "Du er en så rotte at jeg tipper du blir kalt Master Splinter",
"pmcresponse-victim_negative_94": "Jeg tipper du prøvespilte for filmen Stuart Little din rotte",
"pmcresponse-victim_negative_95": "Jeg tipper du bruker radar mod som jeg så på youtube",
"pmcresponse-victim_negative_96": "Prøv en mot en mot meg i dorms, vi vil se hvem som er best",
"pmcresponse-victim_negative_97": "Jeg tipper du er en av de personene som skriver på internett om dårlig fps på streets",
"pmcresponse-victim_negative_98": "Jeg tipper du installerte gamle mods og fikk masse feilmeldinger for så å skrive om det på internett",
"pmcresponse-victim_negative_99": "Datamaskinen din er så dårlig du får 20fps på streets",
"pmcresponse-victim_positive_1": "Bra skudd",
"pmcresponse-victim_positive_10": "Visste jeg skulle ikke ha tittet rundt",
"pmcresponse-victim_positive_11": "Du tok meg på senga",
"pmcresponse-victim_positive_12": "Bra spilt, jeg tar deg neste gang",
"pmcresponse-victim_positive_13": "Du hadde bedre vinkler enn meg",
"pmcresponse-victim_positive_14": "Jeg tar deg neste gang",
"pmcresponse-victim_positive_15": "Du bintet virkelig bogosene mine :alien:",
"pmcresponse-victim_positive_16": "Du er en iskald drapsmaskin. Jeg hadde ikke en sjanse",
"pmcresponse-victim_positive_17": "Okei, greit. det var et bra skudd",
"pmcresponse-victim_positive_18": "Kos deg med kittet mitt",
"pmcresponse-victim_positive_19": "God kamp",
"pmcresponse-victim_positive_2": "Bra skudd",
"pmcresponse-victim_positive_20": "Du er en tøff spiller å slå",
"pmcresponse-victim_positive_21": "Det var en god flanke, bra jobba",
"pmcresponse-victim_positive_22": "Jeg skulle fulgt bedre med. godt jobba",
"pmcresponse-victim_positive_23": "Jeg var for utolmodig. jeg burde gått flankemarsj og ventet",
"pmcresponse-victim_positive_24": "Det her tar jeg lærdom av. bra jobba",
"pmcresponse-victim_positive_25": "Fillern. jeg trodde jeg hadde deg der",
"pmcresponse-victim_positive_26": "Bra jobba. der spilte du bra",
"pmcresponse-victim_positive_27": "Bra kill. der spilte du bra",
"pmcresponse-victim_positive_28": "Solid drap. ser deg i neste raid",
"pmcresponse-victim_positive_29": "For et absolutt Chad Kill. kult",
"pmcresponse-victim_positive_3": "Godt drap",
"pmcresponse-victim_positive_30": "Jeg hadde ikke en sjanse",
"pmcresponse-victim_positive_31": "Det der var manøvre i toppligaen, Chad",
"pmcresponse-victim_positive_32": "Jeg så deg ikke engang komme, kult",
"pmcresponse-victim_positive_33": "Det der var skikkelig solid-snake akrobatikk",
"pmcresponse-victim_positive_34": "Det var et godt kill, vi bør slå oss sammen",
"pmcresponse-victim_positive_35": "Det var et flott drap, la oss spille på lag en gang",
"pmcresponse-victim_positive_36": "Reaksjonsevnene dine er uvirkelige! tøft",
"pmcresponse-victim_positive_37": "Jeg var i dekning, men du fant en god vinkel. bra jobba",
"pmcresponse-victim_positive_38": "Det der var en seriøs Chad-taktikk i det raidet der",
"pmcresponse-victim_positive_39": "Absolutt chad skarpskytter her altså, godt drap",
"pmcresponse-victim_positive_4": "Fortjent kill, godt jobba",
"pmcresponse-victim_positive_40": "Rent drap",
"pmcresponse-victim_positive_41": "Steinkaldt drap",
"pmcresponse-victim_positive_42": "Du klovna meg skikkelig der",
"pmcresponse-victim_positive_43": "Jeg er litt rusten, men det var et greit drap",
"pmcresponse-victim_positive_5": "Heldig kill",
"pmcresponse-victim_positive_6": "God kamp",
"pmcresponse-victim_positive_7": "Det var rettferdig, fint drap",
"pmcresponse-victim_positive_8": "Skal si du er treffsikker",
"pmcresponse-victim_positive_9": "gg",
"port_already_in_use": "Port %s er allerede i bruk, kontroller om serveren allerede kjører",
"profile-unable_to_find_profile_by_id_cannot_delete": "Kan ikke slette profil med id: %s, ingen profil med id funnet",
"profile_save_callback_error": "Feil under utføring avonBeforeSaveCallback: {{callback}}, {{error}}",
"profile_saved": "Endringer på profil lagret",
"quest-no_skill_found": "Ferdighet ikke funnet",
"ragfair-offer_no_longer_exists": "Tilbud finnes ikke lenger",
"server_running": "Serveren kjører, ikke lukk mens du spiller SPT",
"server_start_meme_1": "Lev le kjærlighet",
"server_start_meme_11": "Kan ikke starte miner.exe, vennligst start serveren på nytt",
"server_start_meme_18": "Hvis du kan se denne meldinger, gratulerer, du kan faktisk lese",
"server_start_meme_19": "Gratulerer! Finn din gratis tarkov lisensnøkkel her: https://bit.ly/3TJbUh2",
"server_start_meme_2": "Anime :(",
"server_start_meme_20": "Visste du, at ni av ti brukere ikke kan lese denne meldingen",
"server_start_meme_21": "Har du noengang lurt på, om alle andre ser rødfargen på samme måte du ser det?",
"server_start_meme_22": "bli bedre",
"server_start_meme_23": "SPT holder jomfrudommen din trygg siden 2018",
"server_start_meme_24": "Den hemmelige trygge havnens server er virkelig! Ikke fortell noen!",
"server_start_meme_3": "Hvis du kan høre meg, må du våkne opp",
"server_start_meme_4": "Ikke glem å like og abonnere",
"server_start_meme_5": "Har du sett våres meme-side?",
"server_start_meme_9": "Ste-sanker? H-Hva er det du gjør?",
"server_start_player_active_botreload_skill": "Karakteren din har 'BotReload'-ferdigheten aktivert, dette vil gjøre at du lader våpnene dine unaturlig raskt, ignorer denne meldingen hvis dette er ment",
"server_start_success": "Ha det gøy i spillet",
"started_webserver_success": "Startet webserver på %s",
"trader-missing_durability_threshold_value": "Kan ikke finne holdbarhetsverdi for kjøpmann: {{traderId}}, faller tilbake til standard på: {{value}}",
"trader-missing_trader_details_using_default_refresh_time": "Kjøpmann: {{traderId}} ble ikke funnet, genererer midlertidig oppføring med standard oppdateringstid på: {{updateTime}}",
"trader-price_multipler_is_zero_use_default": "traderPriceMultipler var 0, dette er ugyldig, setter til 0,01",
"trader-unable_to_delete_stale_purchases": "Kan ikke behandle kjøp av kjøpmann i profil: {{profileId}} fordi kjøpmann: {{traderId}} ikke er funnet, hopper over",
"unhandled_response": "[UNHANDLED][%s]",
"unknown_request": "Ukjent forespørsel!",
"validation_error_decode": "Kan ikke dekode checks.dat. Validering av filen ble hoppet over.",
"validation_error_exception": "Unntak funnet under forsøk på å validere fil: %s",
"validation_error_file": "Filvalidering feilet for fil: %s",
"validation_not_found": "Finner ikke filen checks.dat. Fil blir ikke validert.",
"watermark-commercial_use_prohibited": "Kommersiell bruk er forbudt",
"watermark-discord_url": "https://discord.sp-tarkov.com",
"watermark-do_not_report": "IKKE RAPPORTER DET",
"watermark-free_of_charge": "Dette arbeidet er gjort gratis og uten vederlag",
"watermark-modding_disabled": "DETTE BUILDET HAR SERVER MODDING DEAKTIVERT",
"watermark-no_support": "INGEN STØTTE VIL BLI GITT",
"watermark-not_an_issue": "DETTE ER IKKE ET PROBLEM",
"watermark-paid_scammed": "Om du har betalt penger for dette, har du blitt lurt",
"watermark-report_issues_to": "RAPPORTER PROBLEMER TIL",
"watermark-testing_build": "DETTE ER EN TEST BUILD",
"watermark-use_at_own_risk": "BRUKES PÅ EGET ANSVAR",
"websocket-message_send_failed_with_error": "[WS] sendMessage feilet, med feil: %s",
"websocket-message_sent": "[WS] melding sendt",
"websocket-not_ready_message_not_sent": "[WS] Socket er ikke klar for %s, melding ikke sendt",
"websocket-pinging_player": "[WS] Pinger spiller: %s",
"websocket-player_connected": "[WS] Spiller: %s har koblet til",
"websocket-received_message": "[WS] Mottatt melding fra bruker %s ",
"websocket-started": "Startet webserver på %s"
"assort-missing_loyalty_level_object": "stripQuestAssort(): Sortering for Handler {{traderId}} inneholder ikke loyal_level_items data, hopper over fjerning av oppdrags sortering",
"assort-missing_quest_assort_unlock": "Kan ikke finne tilsvarende {{traderName}} oppdrags opplåsing for oppdrag: {{questName}}. Fullføring av dette oppdraget vil ikke belønne en handelselement for kjøp",
"assort-missing_questassort": "stripQuestAssort(): Assort for Trader: %s inneholder ikke et questassort json, hopper over fjerning av nest assorts",
"baseclass-item_not_found": "%s ble ikke funnet i elementets basis-mellomlager, regenererer hurtigbuffer",
"baseclass-item_not_found_failed": "Elementet %s ble fremdeles ikke funnet i base-mellomlager etter regenerering",
"baseclass-missing_db_no_cache": "Databasen var tom, kan ikke generere elementets buffer",
"bleeding_edge_build": "BLEEDINGEDGE",
"bot-compatibility_check_missing_props": "Kan ikke validere element: {{id}} {{name}} i plass:{{slot}} kan brukes, det mangler a _props verdi",
"bot-generation_failed": "bot generering mislykket. se serverloggen for ytterligere detaljer",
"bot-incompatible_ammo_for_weapon_falling_back_to_default": "Inkompatibel ammunisjon {{chosenAmmo}} ble funnet for {{weaponId}} - {{weaponName}}, faller tilbake til standard: {{defaultAmmo}}",
"bot-invalid_item_compatibility_check": "Kan ikke kontrollere elementets kompatibilitet med utstyrte elementer, ønsket element: {{itemTpl}} felt: {{slot}} er ikke et gyldig produkt",
"bot-item_missing_props_property": "Artikkel {{itemTpl}} {{name}} mangler a _props egenskap",
"bot-item_spawn_limit_reached_skipping_item": "{{botRole}} kan ikke fremkalle element {{itemName}} etter {{attempts}} forsøk, ignorerer fremkallingsgrense",
"bot-loot_type_not_found": "Loot mellomlager feilet for loot: {{lootType}} på bot: {{botRole}}, var en pmc: {{isPmc}}",
"bot-missing_application_context": "applikasjonsKontekst kunne ikke finne %s verdi. Har du startet serveren på nytt uten å starte spillet på nytt?",
"bot-missing_cartridge_slot": "Kan ikke legge til patroner til våpenet fordi modPool ikke inneholder patroner for et sylinderMagasin %s, hopper over",
"bot-missing_container_with_tpl": "Kunne ikke finne beholder mal med tpl.: %s",
"bot-missing_equipment_settings": "Bot {{botRole}} mangler utstyrsinnstillingene: ikke mulig å oppnå verdi for: {{setting}}, faller tilbake til standard for: {{defaultValue}}",
"bot-missing_equipment_settings_property": "Bot {{botRole}} mangler utstyrsinnstillingene: ikke mulig å oppnå verdi for: {{setting}}, faller tilbake til standard for: {{defaultValue}}",
"bot-missing_item_template": "Kan ikke finne objektmal med tpl: %s",
"bot-missing_saved_match_info": "getBotCap() kan ikke få lagret match-informasjon, faller tilbake til standard. Restartet du serveren og ikke klienten?",
"bot-missing_weapon_preset": "Kunne ikke finne forhåndsinstilling for våpen med tpl: %s",
"bot-mod_not_in_slot_filter_list": "Modd: {{modId}} ble ikke funnet i kompatibelt elementfilter for plass: '{{modSlot}}' for element: {{parentName}}, hopper over - {{botRole}}",
"bot-mod_slot_missing_from_item": "Slot '{{modSlot}}' eksisterer ikke for elementet: {{parentId}} {{parentName}} på {{botRole}}",
"bot-no_ammo_found_in_bot_json": "Kan ikke finne ammunisjon for bot type: %s",
"bot-no_bot_cap_found_for_location": "Ingen områdebegrensning funnet for bot: %s, bruker standard",
"bot-no_bot_type_in_cache": "ADVARSEL - Bot mellomlager har ingen kunnskap om type %s",
"bot-no_caliber_data_for_weapon_falling_back_to_default": "Kan ikke finne kaliber-data for {{weaponId}} - {{weaponName}}, faller tilbake til standard ammunisjon: {{defaultAmmo}}",
"bot-no_compatible_camora_ammo_found": "Kunne ikke finne en kompatibel ammunisjon for plass: %s. Fylling av kamora hoppet over",
"bot-no_item_template_found_when_adding_mod": "Kan ikke finne mal for modifikasjonselement med tpl: {{modId}} for plass {{modSlot}}",
"bot-no_spawn_chance_defined_for_equipment_slot": "Ingen fremkallingsmulighet ble definert for utstyr: %s",
"bot-single_bot_generation_not_found_in_cache": "Bot: %s ikke funnet i mellomlager, genererer på nytt, dette kan føre til at spillet lagger",
"bot-unable_to_add_mod_item_invalid": "Mod: {{itemName}} er ikke et gyldig produkt, ikke mulig å legge til i plass: '{{modSlot}}' på produkt: {{parentItemName}}, hopper over",
"bot-unable_to_add_mods_to_weapon_missing_ammo_slot": "Kan ikke legge til modifikasjoner på våpen: {{weaponName}} {{weaponId}} fordi det mangler plasser, patroner eller kammer - {{botRole}}",
"bot-unable_to_edit_limits_of_unknown_map": "Kan ikke redigere bot grenser på kartet: %s fordi det ikke ble funnet",
"bot-unable_to_filter_mod_slot_all_blacklisted": "Kan ikke generere filtrert dynamisk våpen-modifikasjons pool fordi svartelisten har filtrert ut alle moddene for plass: %s, ignorerer svarteliste og regenererer pool",
"bot-unable_to_filter_mods_all_blacklisted": "Kan ikke filtrere modifikasjoner for plass: {{slotName}} på {{itemName}} ettersom de alle var svartelistet, ignorerer svartelisten",
"bot-unable_to_find_ammo_item": "Kan ikke finne ammunisjonsmal med tpl: %s",
"bot-unable_to_find_bot_in_cache": "Kan ikke finne bot i mellomlager med navn: %s",
"bot-unable_to_find_default_magazine_item": "Kunne ikke finne magasin mal: %s i databasen",
"bot-unable_to_find_loot_n_value_for_bot": "Finner ikke loot N verdi for bot: %s, bruker scav n verdi i stedet",
"bot-unable_to_find_magazine_item": "Kunne ikke finne magasin mal: %s i databasen",
"bot-unable_to_find_spawn_limits_fallback_to_defaults": "Kan ikke finne genererings grenser for rolle: %s, faller tilbake til standard",
"bot-unable_to_get_bot_difficulty_fallback_to_assault": "Finner ikke bot: {{botType}} vanskelighetsgrad {{difficulty}}, bruker angreps vanskelighetsgrad som reserveløsning",
"bot-unable_to_get_bot_fallback_to_assault": "Finner ikke bot: JSON, %s JSON, bruker en angreps bot som reserveløsning",
"bot-weapon_generated_incorrect_using_default": "Våpen %s ble generert feil, faller tilbake til våpenets forhåndsinnstilling, se feil ovenfor",
"bot-weapon_missing_magazine_or_chamber": "Våpen med tpl: {{weaponId}} har ingen magasin eller kammer - {{botRole}}",
"bot-weapon_missing_mod_slot": "Slot '{{modSlot}}' eksisterer ikke for elementet: {{weaponId}} {{weaponName}} på {{botRole}}",
"bot-weapons_required_slot_missing_item": "Påkrevd plass '{{modSlot}}' på {{modName}} {{slotId}} var tom på {{botRole}}",
"client_request": "[Client Request] %s",
"customisation-item_already_purchased": "Kles-element {{itemId}} {{itemName}} er allerede kjøpt",
"customisation-unable_to_find_clothing_item_in_inventory": "Kles-element ikke funnet i inventaret med ID: %s",
"customisation-unable_to_find_suit_by_id": "Kan ikke finne kjøpmannens drakttilbud med id: %s",
"dialog-missing_item_template": "Kan ikke finne artikkelmal {{tpl}} i db, kan ikke sende melding av type {{type}}, hopper over",
"event-unhandled_event": "[UNHÅNDTERT HENDELSE] %s",
"executing_startup_callbacks": "Server: Utfører start-callbacks...",
"fence-unable_to_find_assort_by_id": "Kunne ikke finne Fence assortement for id: %s",
"fixer-updated_pockets": "Oppdaterte 'pocket' elementet til ny versjon 18876 med x3 spesialplasser",
"gameevent-bot_not_found": "addEventGearToScavs() - ute av stand til å finne bot av type %s i databasen, hopper over",
"gameevent-no_gear_data": "Ingen utstyrsdata i sesonghendelser.json config for hendelse %s",
"health-healing_item_not_found": "Finner ikke helbredings element %s i spillerens inventar",
"health-unable_to_find_item_to_consume": "Finner ikke et forbruksvare %s i spillerens inventar",
"hideout-missing_recipe_for_area": "Kunne ikke finne oppskrift: %s for områdetype",
"hideout-missing_recipe_in_db": "Kunne ikke lokalisere oppskriften med _id: %s",
"hideout-no_bitcoins_to_collect": "Ingen bitcoins er klar for henting",
"hideout-unable_to_find_area": "Kan ikke finne gjemmestedsområde: %s i profilen",
"hideout-unable_to_find_area_in_database": "Finner ikke området: %s i databasen",
"hideout-unable_to_find_item_in_inventory": "Finner ikke element i inventaret med id %s",
"hideout-unable_to_find_item_to_remove_from_area": "Kan ikke finne noen element å fjerne fra plass i området: %s",
"hideout-unable_to_find_production_in_profile_by_recipie_id": "Kan ikke finne produksjonsoppskrift Id: %s i profilen",
"hideout-unable_to_find_scav_case_recipie_in_database": "Finner ikke Scav Case oppskrift med id: %s i databasen",
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory": "Finner ikke elementet: %s forespurt av ScavCase",
"hideout-unhandled_remove_item_from_area_request": "Uhåndtert forsøk på å fjerne elementet fra gjemmestedsområdet: %s",
"http-unknown_error": "En ukjent feil har oppstått",
"importing_database": "Importerer database...",
"importing_database_finish": "Database import ferdig",
"importing_spt_configs": "Importerer konfigurasjoner...",
"inraid-missing_standing_for_kill": "Finner ikke omdømmenivå for {{victimSide}}:{{victimRole}}",
"insurance-missing_insurance_price_multiplier": "Ingen forsikringsmultiplikator funnet for kjøpmann: %s, sjekk at det finnes i InsuranceConfig.js, faller tilbake til standardverdien på: 0.3",
"inventory-edit_trader_item": "Kan ikke redigere et kjøpmannselement",
"inventory-examine_item_does_not_exist": "examineItem() - Ingen ID med %s funnet",
"inventory-fill_container_failed": "fillContainerMapWithItem() returnerte med en feil %s",
"inventory-get_item_size_item_not_found_by_tpl": "getSizeByInventoryItemHash() Item with tpl: %s ble ikke funnet",
"inventory-invalid_item_missing_from_db": "Kan ikke hente gjenstand: %s fra db",
"inventory-invalid_move_to_container": "Forsøkte å flytte element med plass id: {{slotId}} til {{container}}, profilkorrupsjon ble forhindret",
"inventory-item_missing_props_property": "Gjenstands tpl: {{itemTpl}} name: {{itemName}} mangler rekvisitt-egenskaper. størrelse på denne kan ikke oppnås",
"inventory-item_to_toggle_missing_upd": "Inventar element med _id: %s mangler et upd objekt, legger til",
"inventory-missing_stash_size": "Kan ikke bestemme stash-størrelse fordi det ikke finnes stash i spillerens inventar",
"inventory-no_stash_space": "Ikke nok lagringsplass",
"inventory-return_default_size": "Standardiserer gjenstand %s til størrelse 1x1",
"inventory-stash_not_found": "Kan ikke finne stash %s i db",
"inventory-unable_to_fill_container": "[OOB] for gjenstand: {{id}}; Feilmelding: {{error}}",
"inventory-unable_to_find_item": "getExaminedItemTpl() Kan ikke finne gjenstand med tpl: %s i databasen eller på markedet",
"inventory-unable_to_find_stash": "Finner ingen stash",
"inventory-unable_to_toggle_item_not_found": "Kan ikke bytte inventar med id: %s, gjenstanden ble ikke funnet",
"item-durability_value_invalid_use_default": "getRepairableItemQualityValue() weapon tpl: %s holdbarhetsverdi er ugyldig, gjenoppretter standardverdi til 1",
"item-invalid_tpl_item": "Kan ikke finne et element med tpl på: %s i Db",
"launcher-missing_property": "Profil: %s mangler en descriptionLocaleKey egenskap",
"launcher-profile-edgeofdarkness": "Samme som Prepare To Escape, pluss; større stash-størrelse (10x68), ekstra utstyr/gjenstander, høyere startomdømme med handlere, 1000 dollar, 500 euro",
"launcher-profile_leftbehind": "Samme som standard, pluss; større stash-størrelse (10x38), ekstra utstyr/elementer, 500 dollar",
"launcher-profile_preparetoescape": "Samme som Left Behind, pluss; større stash-størrelse (10x48), ekstra utstyr/gjenstander, høyere omdømme med handelsfolk, 250 euro",
"launcher-profile_sptdeveloper": "Testprofil, startnivå er level 69, masse Rubler/Dollar/Euro, USEC starter med alle oppdrag som er klare for å starte, BEAR starter med alle oppdrag klare til å levere, uovervinnelighets-balaklava",
"launcher-profile_sptzerotohero": "Start med nesten ingenting, ingen Rubler/Dollar/Euro, ingen omdømme hos kjøpmenn, 1 kniv, ingen oppdrag fullført",
"launcher-profile_standard": "Samme som live, grunnleggende stash-størrelse (10x28), 500.000 Rubler",
"linux_use_priviledged_port_non_root": "Ikke-rot-prosesser kan ikke binde til porter under 1024",
"location-containers_generated_success": "Totalt ble %s statiske beholdere generert",
"location-critical_error_see_log": "En kritisk feil oppsto ved generering av loot, se server Logg for detaljer",
"location-dynamic_items_spawned_success": "Totalt er %s dynamiske elementer generert",
"location-generated_success": "Lokasjon generert",
"location-missing_dynamic_template": "Valgt dynamisk startpunkt %s har ingen mal, hopper over",
"location-missing_root_item": "createItem() feilet, rotelement er null, tpl: {{tpl}}, parentId: {{parentId}}",
"location-preset_not_found": "forhåndsinnstilling ikke funnet for {{tpl}}, standardinnstilling: {{defaultId}} navn: {{defaultName}}, parentid: {{parentId}}",
"location-spawn_point_count_requested_vs_found": "{{requested}} spawnpoints var forespurt mens {{found}} er tilgjengelig {{mapName}}",
"location-spawnpoint_missing_items": "Valgt dynamisk spawnpoint %s har ingen gjenstander, hopper over",
"location-unable_to_find_airdrop_drop_config_of_type": "Finner ikke konfigurasjonsinnstillingene for flyslipp for typen: %s, faller tilbake for slipptype: blandet ",
"location-unable_to_fix_broken_waves_missing_base": "%s har ingen base json, hopper over kartbølge rettelser",
"location-unable_to_reparent_item": "createItem() feilet, ikke mulig å gjennomføre re-parent {{tpl}}, parentId: {{parentId}}",
"loot-item_missing_parentid": "Artikkel: %s mangler en parentId verdi. ikke mulig å bruke artikkel som bytte",
"loot-non_item_picked_as_sealed_weapon_crate_reward": "Ugyldig våpen: %s, ble valgt som belønning for forseglet våpenkasse, ikke mulig å opprette bytte",
"mailsend-missing_parent": "Kan ikke finne et element med slotId på: gjemmested for melding til: {{traderId}} sender: {{sender}}",
"mailsend-missing_trader": "Kan ikke sende meldingstype: {{messageType}} til spiller: {{sessionId}}, siden kjøpmanns enum var null",
"mod-send_bundle_url": "[BUNT]: %s",
"modloader-async_mod_error": "ModLoader: Feil ved lasting av async mod: %s",
"modloader-checked": "sjekket",
"modloader-checking_mod": "sjekker: %s",
"modloader-cyclic_dependency": "Syklisk avhengighet oppdaget. Denne feilen må løses. Serveren kan ikke starte før dette er fikset og vil stoppe",
"modloader-dependency_container_not_initalized": "Avhengighetsbeholderen ble forespurt, men den ble ikke initialisert",
"modloader-error_parsing_mod_load_order": "Feil under parsing av mod lastings rekkefølge",
"modloader-incompatibilities_not_string_array": "Mod %s package.json egenskaps 'inkompatibiliteter' bør være en streng array",
"modloader-incompatible_mod_found": "Mod {{author}}{{name}} er inkompatibel med {{incompatibleModName}}",
"modloader-installing_external_dependencies": "Installerer avhengigheter for Mod: {{name}} av: {{author}}",
"modloader-installing_external_dependencies_disabled": "Mod: {{name}} av: {{author}} krever eksterne avhengigheter, men funksjonen er for øyeblikket deaktivert. gå til \"{{configPath}}\", sett \"{{configOption}}\" til sant, og start serveren på nytt.\nVed å aktivere dette godtar du alt ansvar for hva {{name}} laster ned til maskinen din.",
"modloader-invalid_version_property": "Mod %s pakke.json inneholder en ugyldig versjonstreng",
"modloader-is_client_mod": "Mod (%s) er en klientmod og skal plasseres i følgende mappe: /spt/bepinex/plugins",
"modloader-load_order_conflict": "`{{modOneName}}` og `{{modTwoName}}` har motstridende krav for innlastingsrekkefølge. serveren kan ikke starte før dette er fikset og vil slå seg av",
"modloader-loaded_mod": "Mod: {{name}} versjon: {{version}} av: {{author}} lastet inn",
"modloader-loading_mods": "ModLoader: laster inn %s server mods...",
"modloader-main_property_not_js": "Mod %s package.json hovedegenskap må være en .js-fil",
"modloader-main_property_points_to_nothing": "Mod %s package.json hovedegenskap peker til en ikke-eksisterende fil",
"modloader-missing_dependency": "Mod {{mod}} krever at {{modDependency}} er installert.",
"modloader-missing_package_json": "Mod (%s) mangler package.json. Sørg for at du har sjekket mods hub-siden for installasjonsinstruksjoner",
"modloader-missing_package_json_property": "Mod {{modName}} package.json krever {{prop}} egenskap",
"modloader-mod_has_no_main_property": "ModLoader: Mod (%s) er ikke kompatibel. Den mangler en hovedegenskap",
"modloader-mod_order_error": "ModLoader: Feil ble funnet i order.json, VIL BRUKE STANDARD LASTEREKKEFØLGE",
"modloader-mod_order_missing": "ModLoader: order.json mangler, oppretter...",
"modloader-mod_order_missing_from_json": "ModLoader: Mod %s mangler fra order.json, legger til",
"modloader-no_mods_loaded": "Feil ble funnet med mods, INGEN MODS VIL BLI LASTET",
"modloader-not_correct_mod_folder": "En mappe kalt (%s) finnes i din mods-mappe. Du har installert en mod på feil måte. Du kan ha utpakket ut innholdet av en mod direkte inn i mod mappen ved en feil. Se nettstedets FAQ og mods hub-sider angående hvordan du installerer mods riktig",
"modloader-outdated_dependency": "Mod {{mod}} krever {{modDependency}} versjon {{requiredVersion}}. Gjeldende installerte versjon er {{currentVersion}}",
"modloader-skipped_mod": "Hoppet over lasting av Mod: {{mod}}",
"modloader-user_mod_folder_missing": "ModLoader: user/mod mappe mangler, oppretter...",
"modloader-visited": "besøkt",
"modloader-x_duplicates_found": "Du prøver å laste inn mer enn en versjon av %s mod. Hopper over alle.",
"openzone-unable_to_find_map": "Kan ikke legge til soner til plassering: %s fordi den ikke finnes",
"payment-not_enough_money_to_complete_transation": "Profilen hadde ikke nok penger til å fullføre transaksjonen: Nødvendig {{amountToPay}}, har {{amountAvailable}}",
"player-attempt_to_increment_skill_with_negative_value": "Kan ikke øke ferdigheten: %s med et negativt beløp",
"pmc-name_prefix_1": "Engleaktig",
"pmc-name_prefix_10": "Selvsikker",
"pmc-name_prefix_11": "Sjarmerende",
"pmc-name_prefix_12": "Sofistikert",
"pmc-name_prefix_13": "Elegant",
"pmc-name_prefix_14": "Guddommelig",
"pmc-name_prefix_15": "Uærlig",
"pmc-name_prefix_16": "Grådig",
"pmc-name_prefix_17": "Delvis skallet",
"pmc-name_prefix_18": "Attraktiv",
"pmc-name_prefix_19": "Barnslig",
"pmc-name_prefix_20": "Demonisk",
"pmc-name_prefix_21": "Ydmyk",
"pmc-name_prefix_22": "Fantastisk",
"pmc-name_prefix_23": "Letargisk",
"pmc-name_prefix_24": "Nervøs",
"pmc-name_prefix_27": "Rastløs",
"pmc-name_prefix_29": "Fortydelig",
"pmc-name_prefix_3": "Sliten",
"pmc-name_prefix_30": "Jentesjef",
"pmc-name_prefix_31": "Boss-Babe",
"pmc-name_prefix_32": "Konge",
"pmc-name_prefix_33": "Sjef",
"pmc-name_prefix_34": "Vanskelig",
"pmc-name_prefix_35": "Seriøs",
"pmc-name_prefix_36": "Bærbar",
"pmc-name_prefix_37": "Mistenkelig",
"pmc-name_prefix_38": "Kleint",
"pmc-name_prefix_39": "Dank",
"pmc-name_prefix_40": "Geit",
"pmc-name_prefix_41": "Zesty",
"pmc-name_prefix_42": "Falsk",
"pmc-name_prefix_43": "Hevngjerrig",
"pmc-name_prefix_44": "Forvirret",
"pmc-name_prefix_45": "Sensuell",
"pmc-name_prefix_46": "Fet",
"pmc-name_prefix_47": "Stor",
"pmc-name_prefix_48": "Swole",
"pmc-name_prefix_49": "Mistenkelig",
"pmc-name_prefix_5": "Sint",
"pmc-name_prefix_6": "Ærlig",
"pmcresponse-killer_negative_1": "ty 4 gratis bytte",
"pmcresponse-killer_negative_10": "Prøv idet minste å sloss neste gang a...",
"pmcresponse-killer_negative_11": "Jeg tror du trenger å øve litt til",
"pmcresponse-killer_negative_12": "Prøv i det minste å være litt utfordrende neste gang",
"pmcresponse-killer_negative_13": "Rip lille timmy",
"pmcresponse-killer_negative_14": "Enda en skitten liten rotte tatt hånd om",
"pmcresponse-killer_negative_15": "Det der var bare flaut å se på",
"pmcresponse-killer_negative_16": "Jeg forventet i det minste LITT motstand... jaja",
"pmcresponse-killer_negative_17": "Jeg håper ikke du forsikret utstyret ditt, for det får du ikke tilbake",
"pmcresponse-killer_negative_18": "Jeg har en youtube serie om hvordan man kan bli bedre på tarkov hvis du er interessert",
"pmcresponse-killer_negative_19": "Enda et gjenkjenningsmerke til samlingen min",
"pmcresponse-killer_negative_2": "Takk for det nye kittet",
"pmcresponse-killer_negative_20": "Du er så dårlig at du bør spille spt i stedet",
"pmcresponse-killer_negative_3": "Ikke rart du døde, våpenet ditt er søppel",
"pmcresponse-killer_negative_4": "Hvorfor har du på deg den kroppspansringen? lmao",
"pmcresponse-killer_negative_5": "lmaoooo",
"pmcresponse-killer_negative_6": "Ikke bekymre deg, utstyret ditt vil være på markedet snart",
"pmcresponse-killer_negative_7": "Ikke så rart du spiller SPT, så dårlig som du sikter!",
"pmcresponse-killer_negative_8": "Det er som det er.",
"pmcresponse-killer_negative_9": "Takk for at du samlet loot til meg",
"pmcresponse-killer_plead_10": "Typisk {{playerSide}} oppførsel",
"pmcresponse-killer_plead_11": "Jeg trenger {{playerSide}} kills, håper du forstår",
"pmcresponse-killer_plead_4": "Vi sees neste gang",
"pmcresponse-killer_plead_7": "Jeg fant endelig kroppen din og alt du har er søppel",
"pmcresponse-killer_plead_8": "Jeg sverger at du har drept meg før",
"pmcresponse-killer_plead_9": "Typisk oppførsel til en {{playerSide}}",
"pmcresponse-killer_positive_1": "God kamp",
"pmcresponse-killer_positive_10": "Hva enn du matet det våpenet med, så ødela det all kroppspansringen min. God kamp!",
"pmcresponse-killer_positive_11": "Ingenting personlig, må bare få disse Jaeger-oppdragene fullført",
"pmcresponse-killer_positive_12": "Jeg ble ganske bekymret der en stund",
"pmcresponse-killer_positive_13": "Imponerende ferdigheter {{PlayerName}}",
"pmcresponse-killer_positive_14": "Respekt, du ga meg en god kamp",
"pmcresponse-killer_positive_15": "Ren kamp, respekt",
"pmcresponse-killer_positive_16": "Det var en ekte katt og mus kamp, fantastisk",
"pmcresponse-killer_positive_17": "Vi er så tilbake",
"pmcresponse-killer_positive_2": "Du sloss bra",
"pmcresponse-killer_positive_3": "Jeg gjemte utstyret ditt",
"pmcresponse-killer_positive_4": "Du tok emg nesten! flott kamp!",
"pmcresponse-killer_positive_5": "Godt spilt, du hadde meg nesten",
"pmcresponse-killer_positive_6": "Der tok du meg nesten",
"pmcresponse-killer_positive_7": "Hvis jeg ikke hadde overrasket deg, hadde jeg vært dau",
"pmcresponse-killer_positive_8": "God kamp",
"pmcresponse-killer_positive_9": "Godt kjempet",
"pmcresponse-suffix_15": "Kompis",
"pmcresponse-suffix_16": "amigo",
"pmcresponse-suffix_17": "kompis",
"pmcresponse-suffix_18": "dude",
"pmcresponse-suffix_19": "m8",
"pmcresponse-suffix_20": ":)",
"pmcresponse-suffix_21": "(:",
"pmcresponse-suffix_22": ":))))))",
"pmcresponse-suffix_23": "GG",
"pmcresponse-suffix_24": "kjære kompis",
"pmcresponse-suffix_25": "venn",
"pmcresponse-suffix_26": "gutt",
"pmcresponse-suffix_27": "nerd",
"pmcresponse-victim_negative_100": "Jeg tipper du installerte SAIN og måtte fjerne det siden du ble drept så mye",
"pmcresponse-victim_negative_16": "Hvis det var en rettferdig kamp ville jeg vunnet",
"pmcresponse-victim_negative_17": "Jeg håper du selger ditt bytte til feil Kjøpmann",
"pmcresponse-victim_negative_18": "Jeg håper du legger ut byttet ditt på markedet for feil pris",
"pmcresponse-victim_negative_19": "1v1 meg nerd, jeg vinner",
"pmcresponse-victim_negative_20": "Fikk klikk på våpenet mitt, ellers hadde jeg tatt deg",
"pmcresponse-victim_negative_21": "Du er litt av ei rotte",
"pmcresponse-victim_negative_22": "Wow, gjemmer deg i et hjørne som ei rotte? utrolig...",
"pmcresponse-victim_negative_23": "Jeg håper du slår tåa di i bordkanten",
"pmcresponse-victim_negative_24": "Hey! Hvorfor drepte du meg? jeg sier det til mamma",
"pmcresponse-victim_negative_25": "Rapportert",
"pmcresponse-victim_negative_26": "Min mor syntes jeg burde ha vunnet den kampen",
"pmcresponse-victim_negative_27": "Jøss, å knerte en noob som meg må vel gjøre deg skikkelig stolt",
"pmcresponse-victim_negative_28": "Jeg vedder på at du spiller SPT fordi du jukser i live",
"pmcresponse-victim_negative_29": "Du head-eyesd meg, mr hackerman",
"pmcresponse-victim_negative_30": "Flott head-eyes cheat...",
"pmcresponse-victim_negative_31": "Hvis jeg hadde hatt penger nok til et skikkelig kitt hadde det vært du som var dau, og ikke jeg.",
"pmcresponse-victim_negative_32": "du tok meg, men jeg tviler sterkt på at du fant det svarte nøkkelkortet jeg hadde.",
"pmcresponse-victim_negative_33": "Du tok meg kanskje, men jeg tviler på at du fant det termiske siktet jeg hadde.",
"pmcresponse-victim_negative_34": "Jeg sluttet å spille live på grunn av esp og likevel er du her...",
"pmcresponse-victim_negative_35": "Ja ,du tok meg, men jeg tar mer enn deg i benk!",
"pmcresponse-victim_negative_36": "Du er like flink til å navigere som Christopher Columbus",
"pmcresponse-victim_negative_37": "Jeg vedder på at du høres ut som du spiser sigaretter.",
"pmcresponse-victim_negative_38": "Du skyter som en gammal mann. Er det dette du gjør nå som du er pensjonist?",
"pmcresponse-victim_negative_39": "Jeg vedder på at du ser ut som om jeg tegnet deg med venstrehanda mi",
"pmcresponse-victim_negative_40": "Si til mora di at hu må lage mac'n cheese. Jeg kommer straks",
"pmcresponse-victim_negative_41": "Hvis du var noe mer innavlet ville du vært et smørbrød",
"pmcresponse-victim_negative_42": "Din illeluktende lille tusse",
"pmcresponse-victim_negative_43": "Det var ingen behov for vold",
"pmcresponse-victim_negative_44": "1 v 1 meg i dorms når som helst",
"pmcresponse-victim_negative_45": "Typisk {{playerSide}} oppførsel",
"pmcresponse-victim_negative_46": "Jeg forventet bedre fra en på level {{playerLevel}}",
"pmcresponse-victim_negative_47": "Det tapet sugde, men jeg kommer og tar deg i det neste raidet!",
"pmcresponse-victim_negative_48": "Bare vent til neste raid! jeg gjør ikke samme feil igjen",
"pmcresponse-victim_negative_49": "Det der var uventet. Du var heldig denne gangen",
"pmcresponse-victim_negative_50": "Jeg hadde deg nesten! Du slipper ikke unna neste gang",
"pmcresponse-victim_negative_51": "Du var heldig denne gangen! Det blir ikke like lett neste gang",
"pmcresponse-victim_negative_52": "Jeg tok noen dårlige valg denne gangen, men det blir ikke like lett neste gang!",
"pmcresponse-victim_negative_53": "Lykken var på din side, neste gang er det min tur",
"pmcresponse-victim_negative_54": "Jeg kommer tilbake sterkere, kompis. Bare vent og se!",
"pmcresponse-victim_negative_55": "Der kødda jeg det skikkelig til. Det skjer ikke igjen",
"pmcresponse-victim_negative_56": "Skal vedde på at du ikke så LedXen i riggen min",
"pmcresponse-victim_negative_57": "Null sjangs for at du fant labs-kortet jeg hadde i lomma",
"pmcresponse-victim_negative_58": "Håper du glemte å ta med deg de dyre nøklene jeg hadde...",
"pmcresponse-victim_negative_59": "Hadde akkurat nok tid til å putte tingene i gammaen",
"pmcresponse-victim_negative_60": "Hadde akkurat nok tid til å stappe tingene i ræva, så ingenting til deg!",
"pmcresponse-victim_negative_61": "Gammaen min var full av stæsj. Regner med din ikke var det?",
"pmcresponse-victim_negative_62": "Fullstendig rotte-adferd",
"pmcresponse-victim_negative_63": "Du er litt av ei rotte!",
"pmcresponse-victim_negative_64": "Du tok meg bare fordi jeg lagga!",
"pmcresponse-victim_negative_65": "Wow, du misbruker desync ofte du, eller hva?",
"pmcresponse-victim_negative_66": "På tide for deg å ta livet av noen i dag, antar jeg...",
"pmcresponse-victim_negative_67": "Skal vedde på at du satte AI-en på easy!",
"pmcresponse-victim_negative_68": "Du vant bare fordi jeg kødda det til",
"pmcresponse-victim_negative_69": "Lykkeskudd",
"pmcresponse-victim_negative_80": "Har du vurdert å gå ut av huset og se litt grønt gress av og til?",
"pmcresponse-victim_negative_81": "Gratulerer! Du drepte akkurat en kar som var travel med å leve sitt eget liv",
"pmcresponse-victim_negative_82": "Ja du drepte meg kanskje, men tror ikke du skal på date i kveld slik jeg skal (med en dame altså)",
"pmcresponse-victim_negative_83": "Jeg er en moderator på reddig og skal sørge for at du aldri får postet der igjen",
"pmcresponse-victim_negative_84": "Ja du drepte meg kanskje, men det er bare siden jeg er i en telefonsamtale med ei dame",
"pmcresponse-victim_negative_85": "Du tok meg bare på grunn av noen brukte en mikrobølgovn som forstyrrret wifi signalet mitt",
"pmcresponse-victim_negative_86": "Jeg døde kun på grunn av at jeg var på treningstudio tidligere og løfta sinsykt tungt",
"pmcresponse-victim_negative_87": "Jeg er en discord moderator så du burde passe deg, jeg er en storkar rundt her",
"pmcresponse-victim_negative_88": "Du ble reddet av at våpnet mitt låste seg",
"pmcresponse-victim_negative_89": "Du er heldig at fokuset mitt er på en apekatt jpg og ikke dette barnslige spillet, ellers ville du vært lei deg",
"pmcresponse-victim_negative_90": "Hvis hodet mitt ikke var fylt av anti jantelov tanker akkurat nå så ville du vært så død",
"pmcresponse-victim_negative_91": "Jeg lot deg faktisk drepe meg altså",
"pmcresponse-victim_negative_92": "Du gjorde meg en tjeneste, jeg skulle selge dette søppel utstyret til fence uansett",
"pmcresponse-victim_negative_93": "Du er en så rotte at jeg tipper du blir kalt Master Splinter",
"pmcresponse-victim_negative_94": "Jeg tipper du prøvespilte for filmen Stuart Little din rotte",
"pmcresponse-victim_negative_95": "Jeg tipper du bruker radar mod som jeg så på youtube",
"pmcresponse-victim_negative_96": "Prøv en mot en mot meg i dorms, vi vil se hvem som er best",
"pmcresponse-victim_negative_97": "Jeg tipper du er en av de personene som skriver på internett om dårlig fps på streets",
"pmcresponse-victim_negative_98": "Jeg tipper du installerte gamle mods og fikk masse feilmeldinger for så å skrive om det på internett",
"pmcresponse-victim_negative_99": "Datamaskinen din er så dårlig du får 20fps på streets",
"pmcresponse-victim_positive_1": "Bra skudd",
"pmcresponse-victim_positive_10": "Visste jeg skulle ikke ha tittet rundt",
"pmcresponse-victim_positive_11": "Du tok meg på senga",
"pmcresponse-victim_positive_12": "Bra spilt, jeg tar deg neste gang",
"pmcresponse-victim_positive_13": "Du hadde bedre vinkler enn meg",
"pmcresponse-victim_positive_14": "Jeg tar deg neste gang",
"pmcresponse-victim_positive_15": "Du bintet virkelig bogosene mine :alien:",
"pmcresponse-victim_positive_16": "Du er en iskald drapsmaskin. Jeg hadde ikke en sjanse",
"pmcresponse-victim_positive_17": "Okei, greit. det var et bra skudd",
"pmcresponse-victim_positive_18": "Kos deg med kittet mitt",
"pmcresponse-victim_positive_19": "God kamp",
"pmcresponse-victim_positive_2": "Bra skudd",
"pmcresponse-victim_positive_20": "Du er en tøff spiller å slå",
"pmcresponse-victim_positive_21": "Det var en god flanke, bra jobba",
"pmcresponse-victim_positive_22": "Jeg skulle fulgt bedre med. godt jobba",
"pmcresponse-victim_positive_23": "Jeg var for utolmodig. jeg burde gått flankemarsj og ventet",
"pmcresponse-victim_positive_24": "Det her tar jeg lærdom av. bra jobba",
"pmcresponse-victim_positive_25": "Fillern. jeg trodde jeg hadde deg der",
"pmcresponse-victim_positive_26": "Bra jobba. der spilte du bra",
"pmcresponse-victim_positive_27": "Bra kill. der spilte du bra",
"pmcresponse-victim_positive_28": "Solid drap. ser deg i neste raid",
"pmcresponse-victim_positive_29": "For et absolutt Chad Kill. kult",
"pmcresponse-victim_positive_3": "Godt drap",
"pmcresponse-victim_positive_30": "Jeg hadde ikke en sjanse",
"pmcresponse-victim_positive_31": "Det der var manøvre i toppligaen, Chad",
"pmcresponse-victim_positive_32": "Jeg så deg ikke engang komme, kult",
"pmcresponse-victim_positive_33": "Det der var skikkelig solid-snake akrobatikk",
"pmcresponse-victim_positive_34": "Det var et godt kill, vi bør slå oss sammen",
"pmcresponse-victim_positive_35": "Det var et flott drap, la oss spille på lag en gang",
"pmcresponse-victim_positive_36": "Reaksjonsevnene dine er uvirkelige! tøft",
"pmcresponse-victim_positive_37": "Jeg var i dekning, men du fant en god vinkel. bra jobba",
"pmcresponse-victim_positive_38": "Det der var en seriøs Chad-taktikk i det raidet der",
"pmcresponse-victim_positive_39": "Absolutt chad skarpskytter her altså, godt drap",
"pmcresponse-victim_positive_4": "Fortjent kill, godt jobba",
"pmcresponse-victim_positive_40": "Rent drap",
"pmcresponse-victim_positive_41": "Steinkaldt drap",
"pmcresponse-victim_positive_42": "Du klovna meg skikkelig der",
"pmcresponse-victim_positive_43": "Jeg er litt rusten, men det var et greit drap",
"pmcresponse-victim_positive_5": "Heldig kill",
"pmcresponse-victim_positive_6": "God kamp",
"pmcresponse-victim_positive_7": "Det var rettferdig, fint drap",
"pmcresponse-victim_positive_8": "Skal si du er treffsikker",
"pmcresponse-victim_positive_9": "gg",
"port_already_in_use": "Port %s er allerede i bruk, kontroller om serveren allerede kjører",
"profile-unable_to_find_profile_by_id_cannot_delete": "Kan ikke slette profil med id: %s, ingen profil med id funnet",
"profile_save_callback_error": "Feil under utføring avonBeforeSaveCallback: {{callback}}, {{error}}",
"profile_saved": "Endringer på profil lagret",
"quest-no_skill_found": "Ferdighet ikke funnet",
"ragfair-offer_no_longer_exists": "Tilbud finnes ikke lenger",
"server_running": "Serveren kjører, ikke lukk mens du spiller SPT",
"server_start_meme_1": "Lev le kjærlighet",
"server_start_meme_11": "Kan ikke starte miner.exe, vennligst start serveren på nytt",
"server_start_meme_18": "Hvis du kan se denne meldinger, gratulerer, du kan faktisk lese",
"server_start_meme_19": "Gratulerer! Finn din gratis tarkov lisensnøkkel her: https://bit.ly/3TJbUh2",
"server_start_meme_2": "Anime :(",
"server_start_meme_20": "Visste du, at ni av ti brukere ikke kan lese denne meldingen",
"server_start_meme_21": "Har du noengang lurt på, om alle andre ser rødfargen på samme måte du ser det?",
"server_start_meme_22": "bli bedre",
"server_start_meme_23": "SPT holder jomfrudommen din trygg siden 2018",
"server_start_meme_24": "Den hemmelige trygge havnens server er virkelig! Ikke fortell noen!",
"server_start_meme_3": "Hvis du kan høre meg, må du våkne opp",
"server_start_meme_4": "Ikke glem å like og abonnere",
"server_start_meme_5": "Har du sett våres meme-side?",
"server_start_meme_9": "Ste-sanker? H-Hva er det du gjør?",
"server_start_player_active_botreload_skill": "Karakteren din har 'BotReload'-ferdigheten aktivert, dette vil gjøre at du lader våpnene dine unaturlig raskt, ignorer denne meldingen hvis dette er ment",
"server_start_success": "Ha det gøy i spillet",
"started_webserver_success": "Startet webserver på %s",
"trader-missing_durability_threshold_value": "Kan ikke finne holdbarhetsverdi for kjøpmann: {{traderId}}, faller tilbake til standard på: {{value}}",
"trader-missing_trader_details_using_default_refresh_time": "Kjøpmann: {{traderId}} ble ikke funnet, genererer midlertidig oppføring med standard oppdateringstid på: {{updateTime}}",
"trader-price_multipler_is_zero_use_default": "traderPriceMultipler var 0, dette er ugyldig, setter til 0,01",
"trader-unable_to_delete_stale_purchases": "Kan ikke behandle kjøp av kjøpmann i profil: {{profileId}} fordi kjøpmann: {{traderId}} ikke er funnet, hopper over",
"unhandled_response": "[UNHANDLED][%s]",
"unknown_request": "Ukjent forespørsel!",
"validation_error_decode": "Kan ikke dekode checks.dat. Validering av filen ble hoppet over.",
"validation_error_exception": "Unntak funnet under forsøk på å validere fil: %s",
"validation_error_file": "Filvalidering feilet for fil: %s",
"validation_not_found": "Finner ikke filen checks.dat. Fil blir ikke validert.",
"watermark-commercial_use_prohibited": "Kommersiell bruk er forbudt",
"watermark-discord_url": "https://discord.sp-tarkov.com",
"watermark-do_not_report": "IKKE RAPPORTER DET",
"watermark-free_of_charge": "Dette arbeidet er gjort gratis og uten vederlag",
"watermark-modding_disabled": "DETTE BUILDET HAR SERVER MODDING DEAKTIVERT",
"watermark-no_support": "INGEN STØTTE VIL BLI GITT",
"watermark-not_an_issue": "DETTE ER IKKE ET PROBLEM",
"watermark-paid_scammed": "Om du har betalt penger for dette, har du blitt lurt",
"watermark-report_issues_to": "RAPPORTER PROBLEMER TIL",
"watermark-testing_build": "DETTE ER EN TEST BUILD",
"watermark-use_at_own_risk": "BRUKES PÅ EGET ANSVAR",
"websocket-message_send_failed_with_error": "[WS] sendMessage feilet, med feil: %s",
"websocket-message_sent": "[WS] melding sendt",
"websocket-not_ready_message_not_sent": "[WS] Socket er ikke klar for %s, melding ikke sendt",
"websocket-pinging_player": "[WS] Pinger spiller: %s",
"websocket-player_connected": "[WS] Spiller: {{sessionId}} {{contextId}} har koblet til",
"websocket-received_message": "[WS] Mottatt melding fra bruker %s ",
"websocket-started": "Startet webserver på %s"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -18,7 +18,29 @@
"Area": 0,
"AveragePlayTime": 35,
"AveragePlayerLevel": 20,
"Banners": [],
"Banners": [
{
"id": "67d1ac822b87c1a5a30e5ae8",
"pic": {
"path": "CONTENT/banners/banner_Sandbox.jpg",
"rcid": ""
}
},
{
"id": "63a9c1f5662a5b0a2c7fbde5",
"pic": {
"path": "CONTENT/banners/banner_City_final.jpg",
"rcid": ""
}
},
{
"id": "64c0acf85174e095610734a0",
"pic": {
"path": "CONTENT/banners/banner_sherpa.jpg",
"rcid": ""
}
}
],
"BossLocationSpawn": [
{
"BossChance": 50,
@@ -15,17 +15,17 @@ public class AchievementCallbacks(
/// Handle client/achievement/list
/// </summary>
/// <returns></returns>
public string GetAchievements(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetAchievements(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_achievementController.GetAchievements(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_achievementController.GetAchievements(sessionID)));
}
/// <summary>
/// Handle client/achievement/statistic
/// </summary>
/// <returns></returns>
public string Statistic(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> Statistic(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_achievementController.GetAchievementStatics(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_achievementController.GetAchievementStatics(sessionID)));
}
}
@@ -1,9 +1,7 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Controllers;
using SPTarkov.Server.Core.Models.Eft.Bot;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Match;
using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
@@ -11,8 +9,7 @@ namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class BotCallbacks(
BotController _botController,
HttpResponseUtil _httpResponseUtil,
ApplicationContext _applicationContext
HttpResponseUtil _httpResponseUtil
)
{
/// <summary>
@@ -20,68 +17,65 @@ public class BotCallbacks(
/// Is called by client to define each bot roles wave limit
/// </summary>
/// <returns></returns>
public string GetBotLimit(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBotLimit(string url, EmptyRequestData _, string sessionID)
{
var splitUrl = url.Split('/');
var type = splitUrl[^1];
return _httpResponseUtil.NoBody(_botController.GetBotPresetGenerationLimit(type));
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotPresetGenerationLimit(type)));
}
/// <summary>
/// Handle singleplayer/settings/bot/difficulty
/// </summary>
/// <returns></returns>
public string GetBotDifficulty(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBotDifficulty(string url, EmptyRequestData _, string sessionID)
{
var splitUrl = url.Split('/');
var type = splitUrl[^2].ToLower();
var difficulty = splitUrl[^1];
if (difficulty == "core")
{
return _httpResponseUtil.NoBody(_botController.GetBotCoreDifficulty());
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotCoreDifficulty()));
}
var raidConfig = _applicationContext.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.GetValue<GetRaidConfigurationRequestData>();
return _httpResponseUtil.NoBody(_botController.GetBotDifficulty(type, difficulty, raidConfig));
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotDifficulty(sessionID, type, difficulty)));
}
/// <summary>
/// Handle singleplayer/settings/bot/difficulties
/// </summary>
/// <returns></returns>
public string GetAllBotDifficulties(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetAllBotDifficulties(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_botController.GetAllBotDifficulties());
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetAllBotDifficulties()));
}
/// <summary>
/// Handle client/game/bot/generate
/// </summary>
/// <returns></returns>
public string GenerateBots(string url, GenerateBotsRequestData info, string sessionID)
public ValueTask<string> GenerateBots(string url, GenerateBotsRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_botController.Generate(sessionID, info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_botController.Generate(sessionID, info)));
}
/// <summary>
/// Handle singleplayer/settings/bot/maxCap
/// </summary>
/// <returns></returns>
public string GetBotCap(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBotCap(string url, EmptyRequestData _, string sessionID)
{
var splitUrl = url.Split('/');
var location = splitUrl[^1];
return _httpResponseUtil.NoBody(_botController.GetBotCap(location));
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotCap(location)));
}
/// <summary>
/// Handle singleplayer/settings/bot/getBotBehaviours
/// </summary>
/// <returns></returns>
public string GetBotBehaviours()
public ValueTask<string> GetBotBehaviours()
{
return _httpResponseUtil.NoBody(_botController.GetAiBotBrainTypes());
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetAiBotBrainTypes()));
}
}
@@ -17,9 +17,9 @@ public class BuildsCallbacks(
/// Handle client/builds/list
/// </summary>
/// <returns></returns>
public string GetBuilds(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBuilds(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_buildController.GetUserBuilds(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_buildController.GetUserBuilds(sessionID)));
}
/// <summary>
@@ -29,39 +29,39 @@ public class BuildsCallbacks(
/// <param name="request"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string CreateMagazineTemplate(string url, SetMagazineRequest request, string sessionID)
public ValueTask<string> CreateMagazineTemplate(string url, SetMagazineRequest request, string sessionID)
{
_buildController.CreateMagazineTemplate(sessionID, request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/builds/weapon/save
/// </summary>
/// <returns></returns>
public string SetWeapon(string url, PresetBuildActionRequestData request, string sessionID)
public ValueTask<string> SetWeapon(string url, PresetBuildActionRequestData request, string sessionID)
{
_buildController.SaveWeaponBuild(sessionID, request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/builds/equipment/save
/// </summary>
/// <returns></returns>
public string SetEquipment(string url, PresetBuildActionRequestData request, string sessionID)
public ValueTask<string> SetEquipment(string url, PresetBuildActionRequestData request, string sessionID)
{
_buildController.SaveEquipmentBuild(sessionID, request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/builds/delete
/// </summary>
/// <returns></returns>
public string DeleteBuild(string url, RemoveBuildRequestData request, string sessionID)
public ValueTask<string> DeleteBuild(string url, RemoveBuildRequestData request, string sessionID)
{
_buildController.RemoveBuild(sessionID, request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
}
@@ -14,17 +14,17 @@ public class BundleCallbacks(
/// Handle singleplayer/bundles
/// </summary>
/// <returns></returns>
public string GetBundles(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBundles(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_bundleLoader.GetBundles());
return new ValueTask<string>(_httpResponseUtil.NoBody(_bundleLoader.GetBundles()));
}
/// <summary>
/// TODO: what does it do
/// </summary>
/// <returns></returns>
public string GetBundle(string url, object info, string sessionID)
public ValueTask<string> GetBundle(string url, object info, string sessionID)
{
return "BUNDLE";
return new ValueTask<string>("BUNDLE");
}
}
@@ -15,24 +15,24 @@ public class ClientLogCallbacks(
ClientLogController _clientLogController,
ConfigServer _configServer,
LocalisationService _localisationService
// ModLoadOrder _modLoadOrder // TODO: needs implementing
// ModLoadOrder _modLoadOrder // TODO: needs implementing
)
{
/// <summary>
/// Handle /singleplayer/log
/// </summary>
/// <returns></returns>
public string ClientLog(string url, ClientLogRequest request, string sessionID)
public ValueTask<string> ClientLog(string url, ClientLogRequest request, string sessionID)
{
_clientLogController.ClientLog(request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle /singleplayer/release
/// </summary>
/// <returns></returns>
public string ReleaseNotes()
public ValueTask<string> ReleaseNotes()
{
var data = _configServer.GetConfig<CoreConfig>().Release;
@@ -52,16 +52,16 @@ public class ClientLogCallbacks(
data.IsModdable = ProgramStatics.MODS();
data.IsModded = false; // TODO
return _httpResponseUtil.NoBody(data);
return new ValueTask<string>(_httpResponseUtil.NoBody(data));
}
/// <summary>
/// Handle /singleplayer/enableBSGlogging
/// </summary>
/// <returns></returns>
public string BsgLogging()
public ValueTask<string> BsgLogging()
{
var data = _configServer.GetConfig<CoreConfig>().BsgLogging;
return _httpResponseUtil.NoBody(data);
return new ValueTask<string>(_httpResponseUtil.NoBody(data));
}
}
@@ -19,21 +19,21 @@ public class CustomizationCallbacks(
/// Handle client/trading/customization/storage
/// </summary>
/// <returns></returns>
public string GetCustomisationUnlocks(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetCustomisationUnlocks(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_saveServer.GetProfile(sessionID).CustomisationUnlocks);
return new ValueTask<string>(_httpResponseUtil.GetBody(_saveServer.GetProfile(sessionID).CustomisationUnlocks));
}
/// <summary>
/// Handle client/trading/customization
/// </summary>
/// <returns></returns>
public string GetTraderSuits(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTraderSuits(string url, EmptyRequestData _, string sessionID)
{
var splitUrl = url.Split('/');
var traderId = splitUrl[^3];
return _httpResponseUtil.GetBody(_customizationController.GetTraderSuits(traderId, sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetTraderSuits(traderId, sessionID)));
}
/// <summary>
@@ -49,18 +49,18 @@ public class CustomizationCallbacks(
/// Handle client/hideout/customization/offer/list
/// </summary>
/// <returns></returns>
public string GetHideoutCustomisation(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetHideoutCustomisation(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_customizationController.GetHideoutCustomisation(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetHideoutCustomisation(sessionID)));
}
/// <summary>
/// Handle client/customization/storage
/// </summary>
/// <returns></returns>
public string GetStorage(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetStorage(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_customizationController.GetCustomisationStorage(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetCustomisationStorage(sessionID)));
}
/// <summary>
@@ -19,101 +19,101 @@ public class DataCallbacks(
/// Handle client/settings
/// </summary>
/// <returns></returns>
public string GetSettings(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetSettings(string url, EmptyRequestData _, string sessionID)
{
var returns = _httpResponseUtil.GetBody(_databaseService.GetSettings());
return returns;
return new ValueTask<string>(returns);
}
/// <summary>
/// Handle client/globals
/// </summary>
/// <returns></returns>
public string GetGlobals(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetGlobals(string url, EmptyRequestData _, string sessionID)
{
var globals = _databaseService.GetGlobals();
var returns = _httpResponseUtil.GetBody(globals);
return returns;
return new ValueTask<string>(returns);
}
/// <summary>
/// Handle client/items
/// </summary>
/// <returns></returns>
public string GetTemplateItems(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTemplateItems(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetUnclearedBody(_databaseService.GetItems());
return new ValueTask<string>(_httpResponseUtil.GetUnclearedBody(_databaseService.GetItems()));
}
/// <summary>
/// Handle client/handbook/templates
/// </summary>
/// <returns></returns>
public string GetTemplateHandbook(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTemplateHandbook(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetHandbook());
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHandbook()));
}
/// <summary>
/// Handle client/customization
/// </summary>
/// <returns></returns>
public string GetTemplateSuits(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTemplateSuits(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetTemplates().Customization);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetTemplates().Customization));
}
/// <summary>
/// Handle client/account/customization
/// </summary>
/// <returns></returns>
public string GetTemplateCharacter(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTemplateCharacter(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetTemplates().Character);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetTemplates().Character));
}
/// <summary>
/// Handle client/hideout/settings
/// </summary>
/// <returns></returns>
public string GetHideoutSettings(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetHideoutSettings(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetHideout().Settings);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Settings));
}
/// <summary>
/// Handle client/hideout/areas
/// </summary>
/// <returns></returns>
public string GetHideoutAreas(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetHideoutAreas(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetHideout().Areas);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Areas));
}
/// <summary>
/// Handle client/hideout/production/recipes
/// </summary>
/// <returns></returns>
public string GetHideoutProduction(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetHideoutProduction(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetHideout().Production);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Production));
}
/// <summary>
/// Handle client/languages
/// </summary>
/// <returns></returns>
public string GetLocalesLanguages(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetLocalesLanguages(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetLocales().Languages);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetLocales().Languages));
}
/// <summary>
/// Handle client/menu/locale
/// </summary>
/// <returns></returns>
public string GetLocalesMenu(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetLocalesMenu(string url, EmptyRequestData _, string sessionID)
{
var localeId = url.Replace("/client/menu/locale/", "");
var locales = _databaseService.GetLocales();
@@ -124,38 +124,38 @@ public class DataCallbacks(
throw new Exception($"Unable to determine locale for request with {localeId}");
}
return _httpResponseUtil.GetBody(result);
return new ValueTask<string>(_httpResponseUtil.GetBody(result));
}
/// <summary>
/// Handle client/locale
/// </summary>
/// <returns></returns>
public string GetLocalesGlobal(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetLocalesGlobal(string url, EmptyRequestData _, string sessionID)
{
var localeId = url.Replace("/client/locale/", "");
var locales = _localeService.GetLocaleDb(localeId);
return _httpResponseUtil.GetUnclearedBody(locales);
return new ValueTask<string>(_httpResponseUtil.GetUnclearedBody(locales));
}
/// <summary>
/// Handle client/hideout/qte/list
/// </summary>
/// <returns></returns>
public string GetQteList(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetQteList(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetUnclearedBody(_hideoutController.GetQteList(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetUnclearedBody(_hideoutController.GetQteList(sessionID)));
}
/// <summary>
/// Handle client/items/prices/
/// </summary>
/// <returns></returns>
public string GetItemPrices(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetItemPrices(string url, EmptyRequestData _, string sessionID)
{
var traderId = url.Replace("/client/items/prices/", "");
return _httpResponseUtil.GetBody(_traderController.GetItemPrices(sessionID, traderId));
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetItemPrices(sessionID, traderId)));
}
}
@@ -23,25 +23,20 @@ public class DialogueCallbacks(
return true;
}
public string GetRoute()
{
return "spt-dialogue";
}
/// <summary>
/// Handle client/friend/list
/// </summary>
/// <returns></returns>
public virtual string GetFriendList(string url, EmptyRequestData _, string sessionID)
public virtual ValueTask<string> GetFriendList(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.GetFriendList(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetFriendList(sessionID)));
}
/// <summary>
/// Handle client/chatServer/list
/// </summary>
/// <returns></returns>
public virtual string GetChatServerList(string url, GetChatServerListRequestData request, string sessionID)
public virtual ValueTask<string> GetChatServerList(string url, GetChatServerListRequestData request, string sessionID)
{
var chatServer = new List<ChatServer>
{
@@ -66,7 +61,7 @@ public class DialogueCallbacks(
}
};
return _httpResponseUtil.GetBody(chatServer);
return new ValueTask<string>(_httpResponseUtil.GetBody(chatServer));
}
/// <summary>
@@ -74,9 +69,9 @@ public class DialogueCallbacks(
/// TODO: request properties are not handled
/// </summary>
/// <returns></returns>
public virtual string GetMailDialogList(string url, GetMailDialogListRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogList(string url, GetMailDialogListRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.GenerateDialogueList(sessionID), 0, null, false);
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GenerateDialogueList(sessionID), 0, null, false));
}
/// <summary>
@@ -86,191 +81,191 @@ public class DialogueCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public virtual string GetMailDialogView(string url, GetMailDialogViewRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogView(string url, GetMailDialogViewRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.GenerateDialogueView(request, sessionID), 0, null, false);
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GenerateDialogueView(request, sessionID), 0, null, false));
}
/// <summary>
/// Handle client/mail/dialog/info
/// </summary>
/// <returns></returns>
public virtual string GetMailDialogInfo(string url, GetMailDialogInfoRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogInfo(string url, GetMailDialogInfoRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.GetDialogueInfo(request.DialogId, sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetDialogueInfo(request.DialogId, sessionID)));
}
/// <summary>
/// Handle client/mail/dialog/remove
/// </summary>
/// <returns></returns>
public virtual string RemoveDialog(string url, RemoveDialogRequestData request, string sessionID)
public virtual ValueTask<string> RemoveDialog(string url, RemoveDialogRequestData request, string sessionID)
{
_dialogueController.RemoveDialogue(request.DialogId, sessionID);
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/mail/dialog/pin
/// </summary>
/// <returns></returns>
public virtual string PinDialog(string url, PinDialogRequestData request, string sessionID)
public virtual ValueTask<string> PinDialog(string url, PinDialogRequestData request, string sessionID)
{
_dialogueController.SetDialoguePin(request.DialogId, true, sessionID);
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/mail/dialog/unpin
/// </summary>
/// <returns></returns>
public virtual string UnpinDialog(string url, PinDialogRequestData request, string sessionID)
public virtual ValueTask<string> UnpinDialog(string url, PinDialogRequestData request, string sessionID)
{
_dialogueController.SetDialoguePin(request.DialogId, false, sessionID);
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/mail/dialog/read
/// </summary>
/// <returns></returns>
public virtual string SetRead(string url, SetDialogReadRequestData request, string sessionID)
public virtual ValueTask<string> SetRead(string url, SetDialogReadRequestData request, string sessionID)
{
_dialogueController.SetRead(request.Dialogs, sessionID);
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/mail/dialog/getAllAttachments
/// </summary>
/// <returns></returns>
public virtual string GetAllAttachments(string url, GetAllAttachmentsRequestData request, string sessionID)
public virtual ValueTask<string> GetAllAttachments(string url, GetAllAttachmentsRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.GetAllAttachments(request.DialogId, sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetAllAttachments(request.DialogId, sessionID)));
}
/// <summary>
/// Handle client/mail/msg/send
/// </summary>
/// <returns></returns>
public virtual string SendMessage(string url, SendMessageRequest request, string sessionID)
public virtual ValueTask<string> SendMessage(string url, SendMessageRequest request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.SendMessage(sessionID, request));
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.SendMessage(sessionID, request)));
}
/// <summary>
/// Handle client/friend/request/list/outbox
/// </summary>
/// <returns></returns>
public virtual string ListOutbox(string url, EmptyRequestData _, string sessionID)
public virtual ValueTask<string> ListOutbox(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/friend/request/list/inbox
/// </summary>
/// <returns></returns>
public virtual string ListInbox(string url, EmptyRequestData _, string sessionID)
public virtual ValueTask<string> ListInbox(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/friend/request/send
/// </summary>
/// <returns></returns>
public virtual string SendFriendRequest(string url, FriendRequestData request, string sessionID)
public virtual ValueTask<string> SendFriendRequest(string url, FriendRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(_dialogueController.SendFriendRequest(sessionID, request));
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.SendFriendRequest(sessionID, request)));
}
/// <summary>
/// Handle client/friend/request/accept-all
/// </summary>
/// <returns></returns>
public virtual string AcceptAllFriendRequests(string url, EmptyRequestData _, string sessionID)
public virtual ValueTask<string> AcceptAllFriendRequests(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/friend/request/accept
/// </summary>
/// <returns></returns>
public virtual string AcceptFriendRequest(string url, AcceptFriendRequestData request, string sessionID)
public virtual ValueTask<string> AcceptFriendRequest(string url, AcceptFriendRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/friend/request/decline
/// </summary>
/// <returns></returns>
public virtual string DeclineFriendRequest(string url, DeclineFriendRequestData request, string sessionID)
public virtual ValueTask<string> DeclineFriendRequest(string url, DeclineFriendRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/friend/request/cancel
/// </summary>
/// <returns></returns>
public virtual string CancelFriendRequest(string url, CancelFriendRequestData request, string sessionID)
public virtual ValueTask<string> CancelFriendRequest(string url, CancelFriendRequestData request, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/friend/delete
/// </summary>
/// <returns></returns>
public virtual string DeleteFriend(string url, DeleteFriendRequest request, string sessionID)
public virtual ValueTask<string> DeleteFriend(string url, DeleteFriendRequest request, string sessionID)
{
_dialogueController.DeleteFriend(sessionID, request);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/friend/ignore/set
/// </summary>
/// <returns></returns>
public virtual string IgnoreFriend(string url, UIDRequestData request, string sessionID)
public virtual ValueTask<string> IgnoreFriend(string url, UIDRequestData request, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/friend/ignore/remove
/// </summary>
/// <returns></returns>
public virtual string UnIgnoreFriend(string url, UIDRequestData request, string sessionID)
public virtual ValueTask<string> UnIgnoreFriend(string url, UIDRequestData request, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
public virtual string ClearMail(string url, ClearMailMessageRequest request, string sessionID)
public virtual ValueTask<string> ClearMail(string url, ClearMailMessageRequest request, string sessionID)
{
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
public virtual string CreateGroupMail(string url, CreateGroupMailRequest request, string sessionID)
public virtual ValueTask<string> CreateGroupMail(string url, CreateGroupMailRequest request, string sessionID)
{
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
public virtual string ChangeMailGroupOwner(string url, ChangeGroupMailOwnerRequest request, string sessionID)
public virtual ValueTask<string> ChangeMailGroupOwner(string url, ChangeGroupMailOwnerRequest request, string sessionID)
{
return "Not Implemented!"; // Not implemented in Node
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
public virtual string AddUserToMail(string url, AddUserGroupMailRequest request, string sessionID)
public virtual ValueTask<string> AddUserToMail(string url, AddUserGroupMailRequest request, string sessionID)
{
return "Not Implemented!"; // Not implemented in Node
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
public virtual string RemoveUserFromMail(string url, RemoveUserGroupMailRequest request, string sessionID)
public virtual ValueTask<string> RemoveUserFromMail(string url, RemoveUserGroupMailRequest request, string sessionID)
{
return "Not Implemented!"; // Not implemented in Node
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
}
@@ -24,34 +24,29 @@ public class GameCallbacks(
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-game";
}
/// <summary>
/// Handle client/game/version/validate
/// </summary>
/// <returns></returns>
public string VersionValidate(string url, VersionValidateRequestData info, string sessionID)
public ValueTask<string> VersionValidate(string url, VersionValidateRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/game/start
/// </summary>
/// <returns></returns>
public string GameStart(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GameStart(string url, EmptyRequestData _, string sessionID)
{
var startTimestampSec = _timeUtil.GetTimeStamp();
_gameController.GameStart(url, sessionID, startTimestampSec);
return _httpResponseUtil.GetBody(
return new ValueTask<string>(_httpResponseUtil.GetBody(
new GameStartResponse
{
UtcTime = startTimestampSec
}
);
));
}
/// <summary>
@@ -59,128 +54,128 @@ public class GameCallbacks(
/// Save profiles on game close
/// </summary>
/// <returns></returns>
public string GameLogout(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GameLogout(string url, EmptyRequestData _, string sessionID)
{
_saveServer.SaveProfile(sessionID);
return _httpResponseUtil.GetBody(
return new ValueTask<string>(_httpResponseUtil.GetBody(
new GameLogoutResponseData
{
Status = "ok"
}
);
));
}
/// <summary>
/// Handle client/game/config
/// </summary>
/// <returns></returns>
public string GetGameConfig(string url, GameEmptyCrcRequestData info, string sessionID)
public ValueTask<string> GetGameConfig(string url, GameEmptyCrcRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetGameConfig(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetGameConfig(sessionID)));
}
/// <summary>
/// Handle client/game/mode
/// </summary>
/// <returns></returns>
public string GetGameMode(string url, GameModeRequestData info, string sessionID)
public ValueTask<string> GetGameMode(string url, GameModeRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetGameMode(sessionID, info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetGameMode(sessionID, info)));
}
/// <summary>
/// Handle client/server/list
/// </summary>
/// <returns></returns>
public string GetServer(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetServer(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetServer(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetServer(sessionID)));
}
/// <summary>
/// Handle client/match/group/current
/// </summary>
/// <returns></returns>
public string GetCurrentGroup(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetCurrentGroup(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetCurrentGroup(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetCurrentGroup(sessionID)));
}
/// <summary>
/// Handle client/checkVersion
/// </summary>
/// <returns></returns>
public string ValidateGameVersion(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> ValidateGameVersion(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetValidGameVersion(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetValidGameVersion(sessionID)));
}
/// <summary>
/// Handle client/game/keepalive
/// </summary>
/// <returns></returns>
public string GameKeepalive(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GameKeepalive(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetKeepAlive(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetKeepAlive(sessionID)));
}
/// <summary>
/// Handle singleplayer/settings/version
/// </summary>
/// <returns></returns>
public string GetVersion(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetVersion(string url, EmptyRequestData _, string sessionID)
{
// change to be a proper type
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new
{
Version = _watermark.GetInGameVersionLabel()
}
);
));
}
/// <summary>
/// Handle /client/report/send & /client/reports/lobby/send
/// </summary>
/// <returns></returns>
public string ReportNickname(string url, UIDRequestData request, string sessionID)
public ValueTask<string> ReportNickname(string url, UIDRequestData request, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle singleplayer/settings/getRaidTime
/// </summary>
/// <returns></returns>
public string GetRaidTime(string url, GetRaidTimeRequest request, string sessionID)
public ValueTask<string> GetRaidTime(string url, GetRaidTimeRequest request, string sessionID)
{
return _httpResponseUtil.NoBody(_gameController.GetRaidTime(sessionID, request));
return new ValueTask<string>(_httpResponseUtil.NoBody(_gameController.GetRaidTime(sessionID, request)));
}
/// <summary>
/// Handle /client/survey
/// </summary>
/// <returns></returns>
public string GetSurvey(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetSurvey(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_gameController.GetSurvey(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetSurvey(sessionID)));
}
/// <summary>
/// Handle client/survey/view
/// </summary>
/// <returns></returns>
public string GetSurveyView(string url, SendSurveyOpinionRequest request, string sessionID)
public ValueTask<string> GetSurveyView(string url, SendSurveyOpinionRequest request, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/survey/opinion
/// </summary>
/// <returns></returns>
public string SendSurveyOpinion(string url, SendSurveyOpinionRequest request, string sessionID)
public ValueTask<string> SendSurveyOpinion(string url, SendSurveyOpinionRequest request, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
}
@@ -12,9 +12,4 @@ public class HandbookCallbacks(HandBookController _handBookController) : IOnLoad
_handBookController.Load();
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-handbook";
}
}
@@ -22,10 +22,10 @@ public class HealthCallbacks(
/// <param name="info">HealthListener.Instance.CurrentHealth class</param>
/// <param name="sessionID">session id</param>
/// <returns>empty response, no data sent back to client</returns>
public string HandleWorkoutEffects(string url, WorkoutData info, string sessionID)
public ValueTask<string> HandleWorkoutEffects(string url, WorkoutData info, string sessionID)
{
_healthController.ApplyWorkoutChanges(_profileHelper.GetPmcProfile(sessionID), info, sessionID);
return _httpResponseUtil.EmptyResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyResponse());
}
/// <summary>
@@ -28,11 +28,6 @@ public class HideoutCallbacks(
return false;
}
public string GetRoute()
{
return "spt-hideout";
}
/// <summary>
/// Handle HideoutUpgrade event
/// </summary>
@@ -1,26 +1,18 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Servers;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable(InjectionType.Singleton, TypePriority = OnLoadOrder.HttpCallbacks)]
public class HttpCallbacks(HttpServer _httpServer, ApplicationContext _applicationContext) : IOnLoad
public class HttpCallbacks(HttpServer _httpServer) : IOnLoad
{
public Task OnLoad()
{
_httpServer.Load(_applicationContext.GetLatestValue(ContextVariableType.APP_BUILDER)?.GetValue<WebApplicationBuilder>());
_applicationContext.ClearValues(ContextVariableType.APP_BUILDER);
_httpServer.Load();
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-http";
}
public string GetImage()
{
return "";
@@ -20,10 +20,10 @@ public class InraidCallbacks(
/// <param name="info">register player request</param>
/// <param name="sessionID">Session id</param>
/// <returns>Null http response</returns>
public string RegisterPlayer(string url, RegisterPlayerRequestData info, string sessionID)
public ValueTask<string> RegisterPlayer(string url, RegisterPlayerRequestData info, string sessionID)
{
_inRaidController.AddPlayer(sessionID, info);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
@@ -33,36 +33,36 @@ public class InraidCallbacks(
/// <param name="info">Save progress request</param>
/// <param name="sessionID">Session id</param>
/// <returns>Null http response</returns>
public string SaveProgress(string url, ScavSaveRequestData info, string sessionID)
public ValueTask<string> SaveProgress(string url, ScavSaveRequestData info, string sessionID)
{
_inRaidController.SavePostRaidProfileForScav(info, sessionID);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle singleplayer/settings/raid/menu
/// </summary>
/// <returns>JSON as string</returns>
public string GetRaidMenuSettings()
public ValueTask<string> GetRaidMenuSettings()
{
return _httpResponseUtil.NoBody(_inRaidController.GetInRaidConfig().RaidMenuSettings);
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetInRaidConfig().RaidMenuSettings));
}
/// <summary>
/// Handle singleplayer/scav/traitorscavhostile
/// </summary>
/// <returns></returns>
public string GetTraitorScavHostileChance(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTraitorScavHostileChance(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_inRaidController.GetTraitorScavHostileChance(url, sessionID));
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetTraitorScavHostileChance(url, sessionID)));
}
/// <summary>
/// Handle singleplayer/bosstypes
/// </summary>
/// <returns></returns>
public string GetBossTypes(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetBossTypes(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_inRaidController.GetBossTypes(url, sessionID));
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetBossTypes(url, sessionID)));
}
}
@@ -33,11 +33,6 @@ public class InsuranceCallbacks(
return false;
}
public string GetRoute()
{
return "spt-insurance";
}
/// <summary>
/// Handle client/insurance/items/list/cost
/// </summary>
@@ -45,9 +40,9 @@ public class InsuranceCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetInsuranceCost(string url, GetInsuranceCostRequestData info, string sessionID)
public ValueTask<string> GetInsuranceCost(string url, GetInsuranceCostRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID)));
}
/// <summary>
@@ -9,14 +9,14 @@ namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRouter _itemEventRouter)
{
public string HandleEvents(string url, ItemEventRouterRequest info, string sessionID)
public ValueTask<string> HandleEvents(string url, ItemEventRouterRequest info, string sessionID)
{
var eventResponse = _itemEventRouter.HandleEvents(info, sessionID);
var result = IsCriticalError(eventResponse.Warnings)
? _httpResponseUtil.GetBody(eventResponse, GetErrorCode(eventResponse.Warnings), eventResponse.Warnings[0].ErrorMessage)
: _httpResponseUtil.GetBody(eventResponse);
return result;
return new ValueTask<string>(result);
}
/// <summary>
@@ -15,74 +15,74 @@ public class LauncherCallbacks(
Watermark _watermark
)
{
public string Connect()
public ValueTask<string> Connect()
{
return _httpResponseUtil.NoBody(_launcherController.Connect());
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.Connect()));
}
public string Login(string url, LoginRequestData info, string sessionID)
public ValueTask<string> Login(string url, LoginRequestData info, string sessionID)
{
var output = _launcherController.Login(info);
return output ?? "FAILED";
return new ValueTask<string>(output ?? "FAILED");
}
public string Register(string url, RegisterData info, string sessionID)
public ValueTask<string> Register(string url, RegisterData info, string sessionID)
{
var output = _launcherController.Register(info);
return string.IsNullOrEmpty(output) ? "FAILED" : "OK";
return new ValueTask<string>(string.IsNullOrEmpty(output) ? "FAILED" : "OK");
}
public string Get(string url, LoginRequestData info, string sessionID)
public ValueTask<string> Get(string url, LoginRequestData info, string sessionID)
{
var output = _launcherController.Find(_launcherController.Login(info));
return _httpResponseUtil.NoBody(output);
return new ValueTask<string>(_httpResponseUtil.NoBody(output));
}
public string ChangeUsername(string url, ChangeRequestData info, string sessionID)
public ValueTask<string> ChangeUsername(string url, ChangeRequestData info, string sessionID)
{
var output = _launcherController.ChangeUsername(info);
return string.IsNullOrEmpty(output) ? "FAILED" : "OK";
return new ValueTask<string>(string.IsNullOrEmpty(output) ? "FAILED" : "OK");
}
public string ChangePassword(string url, ChangeRequestData info, string sessionID)
public ValueTask<string> ChangePassword(string url, ChangeRequestData info, string sessionID)
{
var output = _launcherController.ChangePassword(info);
return string.IsNullOrEmpty(output) ? "FAILED" : "OK";
return new ValueTask<string>(string.IsNullOrEmpty(output) ? "FAILED" : "OK");
}
public string Wipe(string url, RegisterData info, string sessionID)
public ValueTask<string> Wipe(string url, RegisterData info, string sessionID)
{
var output = _launcherController.Wipe(info);
return string.IsNullOrEmpty(output) ? "FAILED" : "OK";
return new ValueTask<string>(string.IsNullOrEmpty(output) ? "FAILED" : "OK");
}
public string GetServerVersion()
public ValueTask<string> GetServerVersion()
{
return _httpResponseUtil.NoBody(_watermark.GetVersionTag());
return new ValueTask<string>(_httpResponseUtil.NoBody(_watermark.GetVersionTag()));
}
public string Ping(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> Ping(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody("pong!");
return new ValueTask<string>(_httpResponseUtil.NoBody("pong!"));
}
public string RemoveProfile(string url, RemoveProfileData info, string sessionID)
public ValueTask<string> RemoveProfile(string url, RemoveProfileData info, string sessionID)
{
return _httpResponseUtil.NoBody(_saveServer.RemoveProfile(sessionID));
return new ValueTask<string>(_httpResponseUtil.NoBody(_saveServer.RemoveProfile(sessionID)));
}
public string GetCompatibleTarkovVersion()
public ValueTask<string> GetCompatibleTarkovVersion()
{
return _httpResponseUtil.NoBody(_launcherController.GetCompatibleTarkovVersion());
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetCompatibleTarkovVersion()));
}
public string GetLoadedServerMods()
public ValueTask<string> GetLoadedServerMods()
{
return _httpResponseUtil.NoBody(_launcherController.GetLoadedServerMods());
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetLoadedServerMods()));
}
public string GetServerModsProfileUsed(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetServerModsProfileUsed(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_launcherController.GetServerModsProfileUsed(sessionID));
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetServerModsProfileUsed(sessionID)));
}
}
@@ -13,72 +13,72 @@ public class LauncherV2Callbacks(
ProfileController _profileController
)
{
public string Ping()
public ValueTask<string> Ping()
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2PingResponse
{
Response = _launcherV2Controller.Ping()
}
);
));
}
public string Types()
public ValueTask<string> Types()
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2TypesResponse
{
Response = _launcherV2Controller.Types()
}
);
));
}
public string Login(LoginRequestData info)
public ValueTask<string> Login(LoginRequestData info)
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2LoginResponse
{
Response = _launcherV2Controller.Login(info)
}
);
));
}
public string Register(RegisterData info)
public ValueTask<string> Register(RegisterData info)
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2RegisterResponse
{
Response = _launcherV2Controller.Register(info),
Profiles = _profileController.GetMiniProfiles()
}
);
));
}
public string PasswordChange(ChangeRequestData info)
public ValueTask<string> PasswordChange(ChangeRequestData info)
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2PasswordChangeResponse
{
Response = _launcherV2Controller.PasswordChange(info),
Profiles = _profileController.GetMiniProfiles()
}
);
));
}
public string Remove(LoginRequestData info)
public ValueTask<string> Remove(LoginRequestData info)
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2RemoveResponse
{
Response = _launcherV2Controller.Remove(info),
Profiles = _profileController.GetMiniProfiles()
}
);
));
}
public string CompatibleVersion()
public ValueTask<string> CompatibleVersion()
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2VersionResponse
{
Response = new LauncherV2CompatibleVersion
@@ -87,36 +87,36 @@ public class LauncherV2Callbacks(
EftVersion = _launcherV2Controller.EftVersion()
}
}
);
));
}
public string Mods()
public ValueTask<string> Mods()
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ModsResponse
{
Response = _launcherV2Controller.LoadedMods()
}
);
));
}
public string Profiles()
public ValueTask<string> Profiles()
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ProfilesResponse
{
Response = _profileController.GetMiniProfiles()
}
);
));
}
public object Profile(string? sessionId)
public ValueTask<string> Profile(string? sessionId)
{
return _httpResponseUtil.NoBody(
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ProfileResponse
{
Response = _launcherV2Controller.GetProfile(sessionId)
}
);
));
}
}
@@ -16,17 +16,17 @@ public class LocationCallbacks(
/// Handle client/locations
/// </summary>
/// <returns></returns>
public string GetLocationData(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetLocationData(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_locationController.GenerateAll(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_locationController.GenerateAll(sessionID)));
}
/// <summary>
/// Handle client/airdrop/loot
/// </summary>
/// <returns></returns>
public string GetAirdropLoot(string url, GetAirdropLootRequest info, string sessionID)
public ValueTask<string> GetAirdropLoot(string url, GetAirdropLootRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(_locationController.GetAirDropLoot(info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_locationController.GetAirDropLoot(info)));
}
}
@@ -22,9 +22,9 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string UpdatePing(string url, UpdatePingRequestData info, string sessionID)
public ValueTask<string> UpdatePing(string url, UpdatePingRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
@@ -34,73 +34,73 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string ExitMatch(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> ExitMatch(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/group/exit_from_menu
/// </summary>
/// <returns></returns>
public string ExitFromMenu(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> ExitFromMenu(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/group/current
/// </summary>
/// <returns></returns>
public string GroupCurrent(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GroupCurrent(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(
return new ValueTask<string>(_httpResponseUtil.GetBody(
new MatchGroupCurrentResponse
{
Squad = []
}
);
));
}
/// <summary>
/// Handle client/match/group/looking/start
/// </summary>
/// <returns></returns>
public string StartGroupSearch(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> StartGroupSearch(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/group/looking/stop
/// </summary>
/// <returns></returns>
public string StopGroupSearch(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> StopGroupSearch(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/group/invite/send
/// </summary>
/// <returns></returns>
public string SendGroupInvite(string url, MatchGroupInviteSendRequest info, string sessionID)
public ValueTask<string> SendGroupInvite(string url, MatchGroupInviteSendRequest info, string sessionID)
{
return _httpResponseUtil.GetBody("2427943f23698ay9f2863735");
return new ValueTask<string>(_httpResponseUtil.GetBody("2427943f23698ay9f2863735"));
}
/// <summary>
/// Handle client/match/group/invite/accept
/// </summary>
/// <returns></returns>
public string AcceptGroupInvite(string url, RequestIdRequest info, string sessionID)
public ValueTask<string> AcceptGroupInvite(string url, RequestIdRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(
return new ValueTask<string>(_httpResponseUtil.GetBody(
new List<GroupCharacter>
{
new()
}
);
));
}
/// <summary>
@@ -110,9 +110,9 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string DeclineGroupInvite(string url, RequestIdRequest info, string sessionID)
public ValueTask<string> DeclineGroupInvite(string url, RequestIdRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
@@ -122,9 +122,9 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string CancelGroupInvite(string url, RequestIdRequest info, string sessionID)
public ValueTask<string> CancelGroupInvite(string url, RequestIdRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
@@ -134,63 +134,63 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string TransferGroup(string url, MatchGroupTransferRequest info, string sessionID)
public ValueTask<string> TransferGroup(string url, MatchGroupTransferRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/match/group/invite/cancel-all
/// </summary>
/// <returns></returns>
public string CancelAllGroupInvite(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> CancelAllGroupInvite(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/putMetrics
/// </summary>
/// <returns></returns>
public string PutMetrics(string url, PutMetricsRequestData info, string sessionID)
public ValueTask<string> PutMetrics(string url, PutMetricsRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/analytics/event-disconnect
/// </summary>
/// <returns></returns>
public string EventDisconnect(string url, PutMetricsRequestData info, string sessionID)
public ValueTask<string> EventDisconnect(string url, PutMetricsRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/available
/// </summary>
/// <returns></returns>
public string ServerAvailable(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> ServerAvailable(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_matchController.GetEnabled());
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.GetEnabled()));
}
/// <summary>
/// Handle match/group/start_game
/// </summary>
/// <returns></returns>
public string JoinMatch(string url, MatchGroupStartGameRequest info, string sessionID)
public ValueTask<string> JoinMatch(string url, MatchGroupStartGameRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(_matchController.JoinMatch(info, sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.JoinMatch(info, sessionID)));
}
/// <summary>
/// Handle client/getMetricsConfig
/// </summary>
/// <returns></returns>
public string GetMetrics(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetMetrics(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_databaseService.GetMatch().Metrics);
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetMatch().Metrics));
}
/// <summary>
@@ -198,66 +198,66 @@ public class MatchCallbacks(
/// Handle client/match/group/status
/// </summary>
/// <returns></returns>
public string GetGroupStatus(string url, MatchGroupStatusRequest info, string sessionID)
public ValueTask<string> GetGroupStatus(string url, MatchGroupStatusRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(_matchController.GetGroupStatus(info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.GetGroupStatus(info)));
}
/// <summary>
/// Handle client/match/group/delete
/// </summary>
/// <returns></returns>
public string DeleteGroup(string url, DeleteGroupRequest info, string sessionID)
public ValueTask<string> DeleteGroup(string url, DeleteGroupRequest info, string sessionID)
{
_matchController.DeleteGroup(info);
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/match/group/leave
/// </summary>
/// <returns></returns>
public string LeaveGroup(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> LeaveGroup(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/match/group/player/remove
/// </summary>
/// <returns></returns>
public string RemovePlayerFromGroup(string url, MatchGroupPlayerRemoveRequest info, string sessionID)
public ValueTask<string> RemovePlayerFromGroup(string url, MatchGroupPlayerRemoveRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
/// Handle client/match/local/start
/// </summary>
/// <returns></returns>
public string StartLocalRaid(string url, StartLocalRaidRequestData info, string sessionID)
public ValueTask<string> StartLocalRaid(string url, StartLocalRaidRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_matchController.StartLocalRaid(sessionID, info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.StartLocalRaid(sessionID, info)));
}
/// <summary>
/// Handle client/match/local/end
/// </summary>
/// <returns></returns>
public string EndLocalRaid(string url, EndLocalRaidRequestData info, string sessionID)
public ValueTask<string> EndLocalRaid(string url, EndLocalRaidRequestData info, string sessionID)
{
_matchController.EndLocalRaid(sessionID, info);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/raid/configuration
/// </summary>
/// <returns></returns>
public string GetRaidConfiguration(string url, GetRaidConfigurationRequestData info, string sessionID)
public ValueTask<string> GetRaidConfiguration(string url, GetRaidConfigurationRequestData info, string sessionID)
{
_matchController.ConfigureOfflineRaid(info, sessionID);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
@@ -267,18 +267,18 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetConfigurationByProfile(string url, GetRaidConfigurationRequestData info, string sessionID)
public ValueTask<string> GetConfigurationByProfile(string url, GetRaidConfigurationRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle client/match/group/raid/ready
/// </summary>
/// <returns></returns>
public string RaidReady(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> RaidReady(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
/// <summary>
@@ -288,8 +288,8 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string NotRaidReady(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> NotRaidReady(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(true);
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
}
@@ -44,39 +44,39 @@ public class NotifierCallbacks(
/// Handle push/notifier/getwebsocket
/// </summary>
/// <returns></returns>
public string GetNotifier(string url, IRequestData info, string sessionID)
public ValueTask<string> GetNotifier(string url, IRequestData info, string sessionID)
{
return _httpResponseUtil.EmptyArrayResponse();
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle client/notifier/channel/create
/// </summary>
/// <returns></returns>
public string CreateNotifierChannel(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> CreateNotifierChannel(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_notifierController.GetChannel(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_notifierController.GetChannel(sessionID)));
}
/// <summary>
/// Handle client/game/profile/select
/// </summary>
/// <returns></returns>
public string SelectProfile(string url, UIDRequestData info, string sessionID)
public ValueTask<string> SelectProfile(string url, UIDRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(
return new ValueTask<string>(_httpResponseUtil.GetBody(
new SelectProfileResponse
{
Status = "ok"
}
);
));
}
/// <summary>
/// </summary>
/// <returns></returns>
public string Notify(string url, object info, string sessionID)
public ValueTask<string> Notify(string url, object info, string sessionID)
{
return "NOTIFY";
return new ValueTask<string>("NOTIFY");
}
}
@@ -12,9 +12,4 @@ public class PresetCallbacks(PresetController _presetController) : IOnLoad
_presetController.Initialize();
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-presets";
}
}
@@ -19,9 +19,9 @@ public class PrestigeCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetPrestige(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetPrestige(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_prestigeController.GetPrestige(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_prestigeController.GetPrestige(sessionID)));
}
/// <summary>
@@ -31,10 +31,10 @@ public class PrestigeCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string ObtainPrestige(string url, ObtainPrestigeRequestList info, string sessionID)
public ValueTask<string> ObtainPrestige(string url, ObtainPrestigeRequestList info, string sessionID)
{
_prestigeController.ObtainPrestige(sessionID, info);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
}
@@ -22,15 +22,15 @@ public class ProfileCallbacks(
/// Handle client/game/profile/create
/// </summary>
/// <returns></returns>
public string CreateProfile(string url, ProfileCreateRequestData info, string sessionID)
public ValueTask<string> CreateProfile(string url, ProfileCreateRequestData info, string sessionID)
{
var id = _profileController.CreateProfile(info, sessionID);
return _httpResponse.GetBody(
return new ValueTask<string>(_httpResponse.GetBody(
new CreateProfileResponse
{
UserId = id
}
);
));
}
/// <summary>
@@ -38,9 +38,9 @@ public class ProfileCallbacks(
/// Get the complete player profile (scav + pmc character)
/// </summary>
/// <returns></returns>
public string GetProfileData(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetProfileData(string url, EmptyRequestData _, string sessionID)
{
return _httpResponse.GetBody(_profileController.GetCompleteProfile(sessionID));
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetCompleteProfile(sessionID)));
}
/// <summary>
@@ -49,24 +49,24 @@ public class ProfileCallbacks(
/// Occurs post-raid and when profile first created immediately after character details are confirmed by player
/// </summary>
/// <returns></returns>
public string RegenerateScav(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> RegenerateScav(string url, EmptyRequestData _, string sessionID)
{
return _httpResponse.GetBody(
return new ValueTask<string>(_httpResponse.GetBody(
new List<PmcData>
{
_profileController.GeneratePlayerScav(sessionID)
}
);
));
}
/// <summary>
/// Handle client/game/profile/voice/change event
/// </summary>
/// <returns></returns>
public string ChangeVoice(string url, ProfileChangeVoiceRequestData info, string sessionID)
public ValueTask<string> ChangeVoice(string url, ProfileChangeVoiceRequestData info, string sessionID)
{
_profileController.ChangeVoice(info, sessionID);
return _httpResponse.NullResponse();
return new ValueTask<string>(_httpResponse.NullResponse());
}
/// <summary>
@@ -74,21 +74,21 @@ public class ProfileCallbacks(
/// Client allows player to adjust their profile name
/// </summary>
/// <returns>Client response as string</returns>
public string ChangeNickname(string url, ProfileChangeNicknameRequestData info, string sessionId)
public ValueTask<string> ChangeNickname(string url, ProfileChangeNicknameRequestData info, string sessionId)
{
var output = _profileController.ChangeNickname(info, sessionId);
return output switch
{
NicknameValidationResult.Taken => _httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - "),
NicknameValidationResult.Short => _httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - "),
_ => _httpResponse.GetBody<object>(
NicknameValidationResult.Taken => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - ")),
NicknameValidationResult.Short => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - ")),
_ => new ValueTask<string>(_httpResponse.GetBody<object>(
new
{
status = 0,
NicknameChangeDate = _timeUtil.GetTimeStamp()
}
)
))
};
}
@@ -96,18 +96,18 @@ public class ProfileCallbacks(
/// Handle client/game/profile/nickname/validate
/// </summary>
/// <returns>Client response as string</returns>
public string ValidateNickname(string url, ValidateNicknameRequestData info, string sessionId)
public ValueTask<string> ValidateNickname(string url, ValidateNicknameRequestData info, string sessionId)
{
return _profileController.ValidateNickname(info, sessionId) switch
{
NicknameValidationResult.Taken => _httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - "),
NicknameValidationResult.Short => _httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - "),
_ => _httpResponse.GetBody(
NicknameValidationResult.Taken => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - ")),
NicknameValidationResult.Short => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - ")),
_ => new ValueTask<string>(_httpResponse.GetBody(
new
{
status = "ok"
}
)
))
};
}
@@ -115,16 +115,16 @@ public class ProfileCallbacks(
/// Handle client/game/profile/nickname/reserved
/// </summary>
/// <returns></returns>
public string GetReservedNickname(string url, EmptyRequestData _, string sessionId)
public ValueTask<string> GetReservedNickname(string url, EmptyRequestData _, string sessionId)
{
var fullProfile = _profileHelper.GetFullProfile(sessionId);
if (fullProfile?.ProfileInfo?.Username is not null)
{
// Send players name back to them
return _httpResponse.GetBody(fullProfile?.ProfileInfo?.Username);
return new ValueTask<string>(_httpResponse.GetBody(fullProfile?.ProfileInfo?.Username));
}
return _httpResponse.GetBody("SPTarkov");
return new ValueTask<string>(_httpResponse.GetBody("SPTarkov"));
}
/// <summary>
@@ -132,9 +132,9 @@ public class ProfileCallbacks(
/// Called when creating a character when choosing a character face/voice
/// </summary>
/// <returns></returns>
public string GetProfileStatus(string url, EmptyRequestData _, string sessionId)
public ValueTask<string> GetProfileStatus(string url, EmptyRequestData _, string sessionId)
{
return _httpResponse.GetBody(_profileController.GetProfileStatus(sessionId));
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetProfileStatus(sessionId)));
}
/// <summary>
@@ -142,44 +142,44 @@ public class ProfileCallbacks(
/// Called when viewing another players profile
/// </summary>
/// <returns></returns>
public string GetOtherProfile(string url, GetOtherProfileRequest request, string sessionID)
public ValueTask<string> GetOtherProfile(string url, GetOtherProfileRequest request, string sessionID)
{
return _httpResponse.GetBody(_profileController.GetOtherProfile(sessionID, request));
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetOtherProfile(sessionID, request)));
}
/// <summary>
/// Handle client/profile/settings
/// </summary>
/// <returns></returns>
public string GetProfileSettings(string url, GetProfileSettingsRequest info, string sessionID)
public ValueTask<string> GetProfileSettings(string url, GetProfileSettingsRequest info, string sessionID)
{
return _httpResponse.GetBody(_profileController.SetChosenProfileIcon(sessionID, info));
return new ValueTask<string>(_httpResponse.GetBody(_profileController.SetChosenProfileIcon(sessionID, info)));
}
/// <summary>
/// Handle client/game/profile/search
/// </summary>
/// <returns></returns>
public string SearchProfiles(string url, SearchProfilesRequestData info, string sessionID)
public ValueTask<string> SearchProfiles(string url, SearchProfilesRequestData info, string sessionID)
{
return _httpResponse.GetBody(_profileController.SearchProfiles(info, sessionID));
return new ValueTask<string>(_httpResponse.GetBody(_profileController.SearchProfiles(info, sessionID)));
}
/// <summary>
/// Handle launcher/profile/info
/// </summary>
/// <returns></returns>
public string GetMiniProfile(string url, GetMiniProfileRequestData info, string sessionID)
public ValueTask<string> GetMiniProfile(string url, GetMiniProfileRequestData info, string sessionID)
{
return _httpResponse.NoBody(_profileController.GetMiniProfile(sessionID));
return new ValueTask<string>(_httpResponse.NoBody(_profileController.GetMiniProfile(sessionID)));
}
/// <summary>
/// Handle /launcher/profiles
/// </summary>
/// <returns></returns>
public string GetAllMiniProfiles(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetAllMiniProfiles(string url, EmptyRequestData _, string sessionID)
{
return _httpResponse.NoBody(_profileController.GetMiniProfiles());
return new ValueTask<string>(_httpResponse.NoBody(_profileController.GetMiniProfiles()));
}
}
@@ -37,7 +37,7 @@ public class QuestCallbacks(
{
if (info.Type == "repeatable")
{
return _questController.AcceptRepeatableQuest(pmcData, info, sessionID);
return _repeatableQuestController.AcceptRepeatableQuest(pmcData, info, sessionID);
}
return _questController.AcceptQuest(pmcData, info, sessionID);
@@ -74,9 +74,9 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string ListQuests(string url, ListQuestsRequestData info, string sessionID)
public ValueTask<string> ListQuests(string url, ListQuestsRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_questController.GetClientQuests(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_questController.GetClientQuests(sessionID)));
}
/// <summary>
@@ -86,8 +86,8 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string ActivityPeriods(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> ActivityPeriods(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_repeatableQuestController.GetClientRepeatableQuests(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_repeatableQuestController.GetClientRepeatableQuests(sessionID)));
}
}
@@ -30,11 +30,6 @@ public class RagfairCallbacks(
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-ragfair";
}
public bool OnUpdate(long timeSinceLastRun)
{
if (timeSinceLastRun > _ragfairConfig.RunIntervalSeconds)
@@ -62,9 +57,9 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string Search(string url, SearchRequestData info, string sessionID)
public ValueTask<string> Search(string url, SearchRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_ragfairController.GetOffers(sessionID, info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetOffers(sessionID, info)));
}
/// <summary>
@@ -74,9 +69,9 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetMarketPrice(string url, GetMarketPriceRequestData info, string sessionID)
public ValueTask<string> GetMarketPrice(string url, GetMarketPriceRequestData info, string sessionID)
{
return _httpResponseUtil.GetBody(_ragfairController.GetItemMinAvgMaxFleaPriceValues(info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetItemMinAvgMaxFleaPriceValues(info)));
}
/// <summary>
@@ -123,9 +118,9 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetFleaPrices(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetFleaPrices(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_ragfairController.GetAllFleaPrices());
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetAllFleaPrices()));
}
/// <summary>
@@ -135,15 +130,15 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string SendReport(string url, SendRagfairReportRequestData info, string sessionID)
public ValueTask<string> SendReport(string url, SendRagfairReportRequestData info, string sessionID)
{
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
public string StorePlayerOfferTaxAmount(string url, StorePlayerOfferTaxAmountRequestData info, string sessionID)
public ValueTask<string> StorePlayerOfferTaxAmount(string url, StorePlayerOfferTaxAmountRequestData info, string sessionID)
{
_ragfairTaxService.StoreClientOfferTaxValue(sessionID, info);
return _httpResponseUtil.NullResponse();
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
/// <summary>
@@ -153,8 +148,8 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetFleaOfferById(string url, GetRagfairOfferByIdRequest info, string sessionID)
public ValueTask<string> GetFleaOfferById(string url, GetRagfairOfferByIdRequest info, string sessionID)
{
return _httpResponseUtil.GetBody(_ragfairController.GetOfferByInternalId(sessionID, info));
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetOfferByInternalId(sessionID, info)));
}
}
@@ -22,11 +22,6 @@ public class SaveCallbacks(
_saveServer.Load();
}
public string GetRoute()
{
return "spt-save";
}
public bool OnUpdate(long timeSinceLastRun)
{
if (timeSinceLastRun > _coreConfig.ProfileSaveIntervalInSeconds)
@@ -23,11 +23,6 @@ public class TraderCallbacks(
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-traders";
}
public bool OnUpdate(long _)
{
return _traderController.Update();
@@ -40,9 +35,9 @@ public class TraderCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetTraderSettings(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTraderSettings(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_traderController.GetAllTraders(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetAllTraders(sessionID)));
}
/// <summary>
@@ -52,10 +47,10 @@ public class TraderCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetTrader(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTrader(string url, EmptyRequestData _, string sessionID)
{
var traderID = url.Replace("/client/trading/api/getTrader/", "");
return _httpResponseUtil.GetBody(_traderController.GetTrader(sessionID, traderID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetTrader(sessionID, traderID)));
}
/// <summary>
@@ -65,10 +60,10 @@ public class TraderCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetAssort(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetAssort(string url, EmptyRequestData _, string sessionID)
{
var traderID = url.Replace("/client/trading/api/getTraderAssort/", "");
return _httpResponseUtil.GetBody(_traderController.GetAssort(sessionID, traderID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetAssort(sessionID, traderID)));
}
/// <summary>
@@ -78,8 +73,8 @@ public class TraderCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetModdedTraderData(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetModdedTraderData(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.NoBody(_traderConfig.ModdedTraders);
return new ValueTask<string>(_httpResponseUtil.NoBody(_traderConfig.ModdedTraders));
}
}
@@ -18,9 +18,9 @@ public class WeatherCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetWeather(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetWeather(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_weatherController.Generate());
return new ValueTask<string>(_httpResponseUtil.GetBody(_weatherController.Generate()));
}
/// <summary>
@@ -30,8 +30,8 @@ public class WeatherCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public string GetLocalWeather(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetLocalWeather(string url, EmptyRequestData _, string sessionID)
{
return _httpResponseUtil.GetBody(_weatherController.GenerateLocal(sessionID));
return new ValueTask<string>(_httpResponseUtil.GetBody(_weatherController.GenerateLocal(sessionID)));
}
}
@@ -1,90 +0,0 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Context;
[Injectable(InjectionType.Singleton)]
public class ApplicationContext
{
private const short MaxSavedValues = 10;
private static ApplicationContext? _applicationContext;
private readonly ISptLogger<ApplicationContext> _logger;
private readonly Dictionary<ContextVariableType, LinkedList<ContextVariable>> _variables = new();
private readonly Lock _lockObject = new();
/// <summary>
/// When ApplicationContext gets created by the DI container we store the singleton reference so we can provide it
/// statically for harmony patches!
/// </summary>
public ApplicationContext(ISptLogger<ApplicationContext> logger)
{
_logger = logger;
_applicationContext = this;
}
public static ApplicationContext? GetInstance()
{
return _applicationContext;
}
public ContextVariable? GetLatestValue(ContextVariableType type)
{
lock (_lockObject)
{
if (_variables.TryGetValue(type, out var savedValues))
{
return savedValues.Last!.Value;
}
return null;
}
}
public ICollection<ContextVariable> GetValues(ContextVariableType type)
{
lock (_lockObject)
{
var values = new List<ContextVariable>();
if (_variables.TryGetValue(type, out var savedValues))
{
values.AddRange(savedValues);
}
return values;
}
}
public void AddValue(ContextVariableType type, object value)
{
lock (_lockObject)
{
if (!_variables.TryGetValue(type, out var savedValues))
{
savedValues = [];
if (!_variables.TryAdd(type, savedValues))
{
_logger.Error($"Unable to add context variable type: {type}");
}
}
if (savedValues.Count >= MaxSavedValues)
{
savedValues.RemoveFirst();
}
savedValues.AddLast(new ContextVariable(value, type));
}
}
public void ClearValues(ContextVariableType type)
{
lock (_lockObject)
{
if (!_variables.Remove(type, out _))
{
_logger.Error($"Unable to clear context variable type: {type}");
}
}
}
}
@@ -1,21 +0,0 @@
namespace SPTarkov.Server.Core.Context;
public class ContextVariable(object value, ContextVariableType contextVariableInternalType)
{
private readonly DateTime _timestamp = DateTime.UtcNow;
public T GetValue<T>()
{
return (T) value;
}
public DateTime GetTimestamp()
{
return _timestamp;
}
public ContextVariableType GetContextType()
{
return contextVariableInternalType;
}
}
@@ -1,24 +0,0 @@
namespace SPTarkov.Server.Core.Context;
public enum ContextVariableType
{
// Logged-in users session id
SESSION_ID = 0,
// Currently active raid information
RAID_CONFIGURATION = 1,
// SessionID + Timestamp when client first connected, has _ between values
CLIENT_START_TIMESTAMP = 2,
// When player is loading into map and loot is requested
REGISTER_PLAYER_REQUEST = 3,
RAID_ADJUSTMENTS = 4,
// Data returned from client request object from endLocalRaid()
TRANSIT_INFO = 5,
APP_BUILDER = 6,
LOADED_MOD_ASSEMBLIES = 7,
WEB_APPLICATION = 8,
SERVICE_PROVIDER = 9
}
@@ -2,7 +2,6 @@ using System.Diagnostics;
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Constants;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Generators;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
@@ -33,7 +32,7 @@ public class BotController(
MatchBotDetailsCacheService _matchBotDetailsCacheService,
ProfileHelper _profileHelper,
ConfigServer _configServer,
ApplicationContext _applicationContext,
ProfileActivityService _profileActivityService,
RandomUtil _randomUtil,
ICloner _cloner
)
@@ -73,15 +72,18 @@ public class BotController(
/// Get bot difficulty settings
/// Adjust PMC settings to ensure they engage the correct bot types
/// </summary>
/// <param name="sessionId">Which user is requesting his bot settings</param>
/// <param name="type">what bot the server is requesting settings for</param>
/// <param name="diffLevel">difficulty level server requested settings for</param>
/// <param name="raidConfig">OPTIONAL - applicationContext Data stored at start of raid</param>
/// <param name="ignoreRaidSettings">OPTIONAL - should raid settings chosen pre-raid be ignored</param>
/// <returns>Difficulty object</returns>
public DifficultyCategories GetBotDifficulty(string type, string diffLevel, GetRaidConfigurationRequestData? raidConfig, bool ignoreRaidSettings = false)
public DifficultyCategories GetBotDifficulty(string sessionId, string type, string diffLevel, bool ignoreRaidSettings = false)
{
var difficulty = diffLevel.ToLower();
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
if (!(raidConfig != null || ignoreRaidSettings))
{
_logger.Error(_localisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
@@ -152,7 +154,7 @@ public class BotController(
}
// Store all difficulty values in dict keyed by difficulty type e.g. easy/normal/impossible
result[botNameKey].Add(difficultyName, GetBotDifficulty(botNameKey, difficultyName, null, true));
result[botNameKey].Add(difficultyName, GetBotDifficulty(string.Empty, botNameKey, difficultyName, true));
}
}
@@ -182,7 +184,7 @@ public class BotController(
protected List<BotBase> GenerateBotWaves(GenerateBotsRequestData request, PmcData? pmcProfile, string sessionId)
{
var generatedBotList = new List<BotBase>();
var raidSettings = GetMostRecentRaidSettings();
var raidSettings = GetMostRecentRaidSettings(sessionId);
var allPmcsHaveSameNameAsPlayer = _randomUtil.GetChance100(
_pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance
);
@@ -287,18 +289,16 @@ public class BotController(
/// Pull raid settings from Application context
/// </summary>
/// <returns>GetRaidConfigurationRequestData if it exists</returns>
protected GetRaidConfigurationRequestData? GetMostRecentRaidSettings()
protected GetRaidConfigurationRequestData? GetMostRecentRaidSettings(string sessionId)
{
var raidSettings = _applicationContext
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.GetValue<GetRaidConfigurationRequestData>();
var raidConfiguration = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
if (raidSettings is null)
if (raidConfiguration is null)
{
_logger.Warning(_localisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
}
return raidSettings;
return raidConfiguration;
}
/// <summary>
@@ -1,5 +1,4 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Game;
@@ -22,6 +21,7 @@ namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class GameController(
ISptLogger<GameController> _logger,
IReadOnlyList<SptMod> _loadedMods,
ConfigServer _configServer,
DatabaseService _databaseService,
TimeUtil _timeUtil,
@@ -41,7 +41,6 @@ public class GameController(
RaidTimeAdjustmentService _raidTimeAdjustmentService,
ProfileActivityService _profileActivityService,
CreateProfileService _createProfileService,
ApplicationContext _applicationContext,
ICloner _cloner
)
{
@@ -60,8 +59,7 @@ public class GameController(
/// <param name="startTimeStampMs"></param>
public void GameStart(string url, string? sessionId, long startTimeStampMs)
{
// Store client start time in app context
_applicationContext.AddValue(ContextVariableType.CLIENT_START_TIMESTAMP, $"{sessionId}_{startTimeStampMs}");
_profileActivityService.AddActiveProfile(sessionId, startTimeStampMs);
if (sessionId is null)
{
@@ -472,13 +470,8 @@ public class GameController(
protected void SaveActiveModsToProfile(SptProfile fullProfile)
{
fullProfile.SptData!.Mods ??= [];
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES)?.GetValue<List<SptMod>>();
if (mods == null)
{
return;
}
foreach (var mod in mods)
foreach (var mod in _loadedMods)
{
if (
fullProfile.SptData.Mods.Any(m =>
@@ -1,5 +1,4 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.InRaid;
using SPTarkov.Server.Core.Models.Spt.Config;
@@ -12,7 +11,7 @@ namespace SPTarkov.Server.Core.Controllers;
public class InRaidController(
ISptLogger<InRaidController> _logger,
ProfileHelper _profileHelper,
ApplicationContext _applicationContext,
//ApplicationContext _applicationContext,
ConfigServer _configServer
)
{
@@ -26,7 +25,7 @@ public class InRaidController(
/// <param name="info">Register player request</param>
public void AddPlayer(string sessionId, RegisterPlayerRequestData info)
{
_applicationContext.AddValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info);
// _applicationContext.AddValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info);
}
/// <summary>
@@ -1,6 +1,5 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Launcher;
@@ -18,6 +17,7 @@ namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class LauncherController(
ISptLogger<LauncherController> _logger,
IReadOnlyList<SptMod> _loadedMods,
HashUtil _hashUtil,
TimeUtil _timeUtil,
RandomUtil _randomUtil,
@@ -26,8 +26,7 @@ public class LauncherController(
ProfileHelper _profileHelper,
DatabaseService _databaseService,
LocalisationService _localisationService,
ConfigServer _configServer,
ApplicationContext _applicationContext
ConfigServer _configServer
)
{
protected CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
@@ -242,13 +241,7 @@ public class LauncherController(
/// <returns>Dictionary of mod name and mod details</returns>
public Dictionary<string, AbstractModMetadata> GetLoadedServerMods()
{
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES)?.GetValue<List<SptMod>>();
if (mods == null)
{
return [];
}
return mods.ToDictionary(sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD", sptMod => sptMod.ModMetadata);
return _loadedMods.ToDictionary(sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD", sptMod => sptMod.ModMetadata);
}
/// <summary>
@@ -1,6 +1,5 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Launcher;
using SPTarkov.Server.Core.Models.Eft.Profile;
@@ -17,6 +16,7 @@ namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class LauncherV2Controller(
ISptLogger<LauncherV2Controller> _logger,
IReadOnlyList<SptMod> _loadedMods,
HashUtil _hashUtil,
TimeUtil _timeUtil,
RandomUtil _randomUtil,
@@ -24,8 +24,7 @@ public class LauncherV2Controller(
DatabaseService _databaseService,
LocalisationService _localisationService,
ConfigServer _configServer,
Watermark _watermark,
ApplicationContext _applicationContext
Watermark _watermark
)
{
protected CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
@@ -158,10 +157,9 @@ public class LauncherV2Controller(
/// <returns></returns>
public Dictionary<string, AbstractModMetadata> LoadedMods()
{
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES).GetValue<List<SptMod>>();
var result = new Dictionary<string, AbstractModMetadata>();
foreach (var sptMod in mods)
foreach (var sptMod in _loadedMods)
{
result.Add(sptMod.ModMetadata.Name, sptMod.ModMetadata);
}
@@ -1,5 +1,4 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.Match;
using SPTarkov.Server.Core.Models.Spt.Config;
@@ -17,8 +16,8 @@ public class MatchController(
SaveServer _saveServer,
MatchLocationService _matchLocationService,
ConfigServer _configServer,
ApplicationContext _applicationContext,
LocationLifecycleService _locationLifecycleService,
ProfileActivityService _profileActivityService,
WeatherHelper _weatherHelper,
ICloner _cloner
)
@@ -104,7 +103,7 @@ public class MatchController(
request.IsNightRaid = _weatherHelper.IsNightTime(request.TimeVariant, request.Location);
// Store request data for access during bot generation
_applicationContext.AddValue(ContextVariableType.RAID_CONFIGURATION, request);
_profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration = request;
// TODO: add code to strip PMC of equipment now they've started the raid
@@ -39,7 +39,6 @@ public class QuestController(
ICloner _cloner
)
{
protected static readonly List<string> _questTypes = ["PickUp", "Exploration", "Elimination"];
protected QuestConfig _questConfig = _configServer.GetConfig<QuestConfig>();
/// <summary>
@@ -97,7 +96,7 @@ public class QuestController(
pmcData,
acceptedQuest.QuestId);
}
// Get messageId of text to send to player as text message in game
var messageId = _questHelper.GetMessageIdForQuestStart(
@@ -169,81 +168,6 @@ public class QuestController(
}
}
/// <summary>
/// TODO: Move this code into RepeatableQuestController
/// Handle the client accepting a repeatable quest and starting it
/// Send starting rewards if any to player and
/// Send start notification if any to player
/// </summary>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="acceptedQuest">Repeatable quest accepted</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AcceptRepeatableQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, string sessionID)
{
// Create and store quest status object inside player profile
var newRepeatableQuest = _questHelper.GetQuestReadyForProfile(
pmcData,
QuestStatusEnum.Started,
acceptedQuest
);
pmcData.Quests.Add(newRepeatableQuest);
// Look for the generated quest cache in profile.RepeatableQuests
var repeatableQuestProfile = GetRepeatableQuestFromProfile(pmcData, acceptedQuest.QuestId);
if (repeatableQuestProfile is null)
{
_logger.Error(
_localisationService.GetText(
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
acceptedQuest.QuestId
)
);
throw new Exception(_localisationService.GetText("repeatable-unable_to_accept_quest_see_log"));
}
// Some scav quests need to be added to scav profile for them to show up in-raid
if (repeatableQuestProfile.Side == "Scav" && _questTypes.Contains(repeatableQuestProfile.Type.ToString()))
{
var fullProfile = _profileHelper.GetFullProfile(sessionID);
fullProfile.CharacterData.ScavData.Quests ??= [];
fullProfile.CharacterData.ScavData.Quests.Add(newRepeatableQuest);
}
var response = _eventOutputHolder.GetOutput(sessionID);
return response;
}
/// <summary>
/// Look for an accepted quest inside player profile, return quest that matches
/// </summary>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="questId">Quest id to return</param>
/// <returns>RepeatableQuest</returns>
protected RepeatableQuest GetRepeatableQuestFromProfile(PmcData pmcData, string questId)
{
foreach (var repeatableQuest in pmcData.RepeatableQuests)
{
var matchingQuest = repeatableQuest.ActiveQuests.FirstOrDefault(x => x.Id == questId);
if (matchingQuest is not null)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}");
}
matchingQuest.SptRepatableGroupName = repeatableQuest.Name;
return matchingQuest;
}
}
return null;
}
/// <summary>
/// Handle QuestComplete event
/// Update completed quest in profile
@@ -41,8 +41,56 @@ public class RepeatableQuestController(
ICloner _cloner
)
{
protected static readonly List<string> _questTypes = ["PickUp", "Exploration", "Elimination"];
protected QuestConfig _questConfig = _configServer.GetConfig<QuestConfig>();
/// <summary>
/// Handle the client accepting a repeatable quest and starting it
/// Send starting rewards if any to player and
/// Send start notification if any to player
/// </summary>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="acceptedQuest">Repeatable quest accepted</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AcceptRepeatableQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, string sessionID)
{
// Create and store quest status object inside player profile
var newRepeatableQuest = _questHelper.GetQuestReadyForProfile(
pmcData,
QuestStatusEnum.Started,
acceptedQuest
);
pmcData.Quests.Add(newRepeatableQuest);
// Look for the generated quest cache in profile.RepeatableQuests
var repeatableQuestProfile = GetRepeatableQuestFromProfile(pmcData, acceptedQuest.QuestId);
if (repeatableQuestProfile is null)
{
_logger.Error(
_localisationService.GetText(
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
acceptedQuest.QuestId
)
);
throw new Exception(_localisationService.GetText("repeatable-unable_to_accept_quest_see_log"));
}
// Some scav quests need to be added to scav profile for them to show up in-raid
if (repeatableQuestProfile.Side == "Scav" && _questTypes.Contains(repeatableQuestProfile.Type.ToString()))
{
var fullProfile = _profileHelper.GetFullProfile(sessionID);
fullProfile.CharacterData.ScavData.Quests ??= [];
fullProfile.CharacterData.ScavData.Quests.Add(newRepeatableQuest);
}
var response = _eventOutputHolder.GetOutput(sessionID);
return response;
}
/// <summary>
/// Handle RepeatableQuestChange event
/// </summary>
@@ -180,6 +228,33 @@ public class RepeatableQuestController(
return output;
}
/// <summary>
/// Look for an accepted quest inside player profile, return quest that matches
/// </summary>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="questId">Quest id to return</param>
/// <returns>RepeatableQuest</returns>
protected RepeatableQuest? GetRepeatableQuestFromProfile(PmcData pmcData, string questId)
{
foreach (var repeatableQuest in pmcData.RepeatableQuests)
{
var matchingQuest = repeatableQuest.ActiveQuests.FirstOrDefault(x => x.Id == questId);
if (matchingQuest is not null)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}");
}
matchingQuest.SptRepatableGroupName = repeatableQuest.Name;
return matchingQuest;
}
}
return null;
}
/// <summary>
/// Some accounts have access to free repeatable quest refreshes
/// Track the usage of them inside players profile
@@ -3,5 +3,4 @@ namespace SPTarkov.Server.Core.DI;
public interface IOnLoad
{
Task OnLoad();
string GetRoute();
}
@@ -3,5 +3,4 @@ namespace SPTarkov.Server.Core.DI;
public interface IOnUpdate
{
bool OnUpdate(long timeSinceLastRun);
string GetRoute();
}
@@ -2,6 +2,6 @@ namespace SPTarkov.Server.Core.DI;
public interface ISerializer
{
public void Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body);
public Task Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body);
public bool CanHandle(string route);
}
@@ -2,19 +2,17 @@ namespace SPTarkov.Server.Core.DI;
public static class OnLoadOrder
{
public const int PreSPTDatabase = 0;
public const int Database = 1;
public const int PostSptDatabase = 2;
public const int GameCallbacks = 100;
public const int PostDBModLoader = 200;
public const int TraderRegistration = 250;
public const int HandbookCallbacks = 300;
public const int HttpCallbacks = 400;
public const int SaveCallbacks = 500;
public const int TraderCallbacks = 600;
public const int PostSptModLoader = 700;
public const int PresetCallbacks = 800;
public const int RagfairPriceService = 900;
public const int RagfairCallbacks = 1000;
public const int PostServerLoad = 9999;
public const int Watermark = 0;
public const int PreSptModLoader = 1000;
public const int Database = 2000;
public const int GameCallbacks = 3000;
public const int PostDBModLoader = 4000;
public const int TraderRegistration = 5000;
public const int HandbookCallbacks = 6000;
public const int HttpCallbacks = 7000;
public const int SaveCallbacks = 8000;
public const int TraderCallbacks = 9000;
public const int PresetCallbacks = 10000;
public const int RagfairCallbacks = 11000;
public const int PostSptModLoader = 12000;
}
@@ -2,12 +2,7 @@ namespace SPTarkov.Server.Core.DI;
public static class OnUpdateOrder
{
public const int PreSptUpdate = 0;
public const int DialogueCallbacks = 1;
public const int HideoutCallbacks = 100;
public const int TraderCallbacks = 200;
public const int RagfairCallbacks = 300;
public const int InsuranceCallbacks = 400;
public const int SaveCallbacks = 500;
public const int PostSptUpdate = 9999;
public const int DialogueCallbacks = 1000;
public const int HideoutCallbacks = 2000;
public const int InsuranceCallbacks = 3000;
}
+5 -5
View File
@@ -54,7 +54,7 @@ public abstract class StaticRouter : Router
_jsonUtil = jsonUtil;
}
public object HandleStatic(string url, string? body, string sessionID, string output)
public async ValueTask<object> HandleStatic(string url, string? body, string sessionID, string output)
{
var action = _actions.Single(route => route.url == url);
var type = action.bodyType;
@@ -64,7 +64,7 @@ public abstract class StaticRouter : Router
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
}
return action.action(url, info, sessionID, output);
return await action.action(url, info, sessionID, output);
}
protected override List<HandledRoute> GetHandledRoutes()
@@ -84,7 +84,7 @@ public abstract class DynamicRouter : Router
_jsonUtil = jsonUtil;
}
public object HandleDynamic(string url, string? body, string sessionID, string output)
public async ValueTask<object> HandleDynamic(string url, string? body, string sessionID, string output)
{
var action = actions.First(r => url.Contains(r.url));
var type = action.bodyType;
@@ -94,7 +94,7 @@ public abstract class DynamicRouter : Router
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
}
return action.action(url, info, sessionID, output);
return await action.action(url, info, sessionID, output);
}
protected override List<HandledRoute> GetHandledRoutes()
@@ -123,7 +123,7 @@ public record HandledRoute(string route, bool dynamic);
public record RouteAction(
string url,
Func<string, IRequestData?, string?, string?, object> action,
Func<string, IRequestData?, string?, string?, ValueTask<object>> action,
Type? bodyType = null
);
//public action: (url: string, info: any, sessionID: string, output: string) => Promise<any>,
@@ -0,0 +1,22 @@
namespace SPTarkov.Server.Core.DI
{
/// <summary>
/// A service locator designed specifically for Harmony patches and other
/// parts of the application that do not have direct access to the Dependency Injection (DI) system.
///
/// This should not be used at all when having direct access to DI.
/// </summary>
public static class ServiceLocator
{
public static IServiceProvider ServiceProvider
{
get;
private set;
}
internal static void SetServiceProvider(IServiceProvider provider)
{
ServiceProvider = provider;
}
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Virtuosity/>
</Weavers>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Virtuosity" minOccurs="0" maxOccurs="1" type="xs:anyType" />
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
@@ -1,6 +1,5 @@
using System.Collections.Frozen;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Match;
@@ -21,7 +20,7 @@ public class BotInventoryGenerator(
HashUtil _hashUtil,
RandomUtil _randomUtil,
DatabaseService _databaseService,
ApplicationContext _applicationContext,
ProfileActivityService _profileActivityService,
BotWeaponGenerator _botWeaponGenerator,
BotLootGenerator _botLootGenerator,
BotGeneratorHelper _botGeneratorHelper,
@@ -75,9 +74,7 @@ public class BotInventoryGenerator(
var botInventory = GenerateInventoryBase();
// Get generated raid details bot will be spawned in
var raidConfig = _applicationContext
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.GetValue<GetRaidConfigurationRequestData>();
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration;
GenerateAndAddEquipmentToBot(
sessionId,
@@ -232,6 +232,7 @@ public class LootGenerator(
// Get all items that match the blacklisted types and fold into item blacklist
var itemTypeBlacklist = _itemFilterService.GetItemRewardBaseTypeBlacklist();
var itemsMatchingTypeBlacklist = itemsDb
.Where(templateItem => !string.IsNullOrEmpty(templateItem.Parent)) // Ignore items without parents
.Where(templateItem => _itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist))
.Select(templateItem => templateItem.Id);
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
@@ -23,9 +22,13 @@ public class PMCLootGenerator
private readonly SeasonalEventService _seasonalEventService;
private readonly WeightedRandomHelper _weightedRandomHelper;
private ConcurrentDictionary<string, double>? _backpackLootPool;
private ConcurrentDictionary<string, double>? _pocketLootPool;
private ConcurrentDictionary<string, double>? _vestLootPool;
private Dictionary<string, double>? _backpackLootPool;
private Dictionary<string, double>? _pocketLootPool;
private Dictionary<string, double>? _vestLootPool;
protected readonly Lock BackpackLock = new();
protected readonly Lock PocketLock = new();
protected readonly Lock VestLock = new();
public PMCLootGenerator(
ISptLogger<PMCLootGenerator> logger,
@@ -55,12 +58,17 @@ public class PMCLootGenerator
/// </summary>
/// <param name="botRole"></param>
/// <returns>Dictionary of string and number</returns>
public ConcurrentDictionary<string, double> GeneratePMCPocketLootPool(string botRole)
public Dictionary<string, double> GeneratePMCPocketLootPool(string botRole)
{
// Hydrate loot dictionary if empty
if (_pocketLootPool is null)
lock (PocketLock)
{
_pocketLootPool = new ConcurrentDictionary<string, double>();
// Hydrate loot dictionary if empty
if (_pocketLootPool is not null)
{
return _pocketLootPool;
}
_pocketLootPool = new Dictionary<string, double>();
var items = _databaseService.GetItems();
var pmcPriceOverrides =
_databaseService.GetBots().Types[string.Equals(botRole, "pmcbear", StringComparison.OrdinalIgnoreCase) ? "bear" : "usec"].BotInventory.Items
@@ -102,9 +110,10 @@ public class PMCLootGenerator
}
_weightedRandomHelper.ReduceWeightValues(_pocketLootPool);
}
return _pocketLootPool;
return _pocketLootPool;
}
}
protected HashSet<string> GetLootBlacklist()
@@ -124,12 +133,17 @@ public class PMCLootGenerator
/// </summary>
/// <param name="botRole"></param>
/// <returns>Dictionary of string and number</returns>
public ConcurrentDictionary<string, double> GeneratePMCVestLootPool(string botRole)
public Dictionary<string, double> GeneratePMCVestLootPool(string botRole)
{
// Hydrate loot dictionary if empty
if (_vestLootPool is null)
lock (VestLock)
{
_vestLootPool = new ConcurrentDictionary<string, double>();
// Hydrate loot dictionary if empty
if (_vestLootPool is not null)
{
return _vestLootPool;
}
_vestLootPool = new Dictionary<string, double>();
var items = _databaseService.GetItems();
var pmcPriceOverrides =
_databaseService.GetBots().Types[string.Equals(botRole, "pmcbear", StringComparison.OrdinalIgnoreCase) ? "bear" : "usec"].BotInventory.Items
@@ -171,9 +185,9 @@ public class PMCLootGenerator
}
_weightedRandomHelper.ReduceWeightValues(_vestLootPool);
}
return _vestLootPool;
return _vestLootPool;
}
}
/// <summary>
@@ -207,12 +221,17 @@ public class PMCLootGenerator
/// </summary>
/// <param name="botRole"></param>
/// <returns>Dictionary of string and number</returns>
public ConcurrentDictionary<string, double> GeneratePMCBackpackLootPool(string botRole)
public Dictionary<string, double> GeneratePMCBackpackLootPool(string botRole)
{
// Hydrate loot dictionary if empty
if (_backpackLootPool is null)
lock (BackpackLock)
{
_backpackLootPool = new ConcurrentDictionary<string, double>();
// Hydrate loot dictionary if empty
if (_backpackLootPool is not null)
{
return _backpackLootPool;
}
_backpackLootPool = new Dictionary<string, double>();
var items = _databaseService.GetItems();
var pmcPriceOverrides =
_databaseService.GetBots().Types[string.Equals(botRole, "pmcbear", StringComparison.OrdinalIgnoreCase) ? "bear" : "usec"].BotInventory.Items
@@ -253,8 +272,8 @@ public class PMCLootGenerator
}
_weightedRandomHelper.ReduceWeightValues(_backpackLootPool);
}
return _backpackLootPool;
return _backpackLootPool;
}
}
}
@@ -1,7 +1,6 @@
using System.Collections.Frozen;
using SPTarkov.Server.Core.Constants;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Context;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Match;
@@ -24,7 +23,7 @@ public class BotGeneratorHelper(
ItemHelper _itemHelper,
InventoryHelper _inventoryHelper,
ContainerHelper _containerHelper,
ApplicationContext _applicationContext,
ProfileActivityService _profileActivityService,
LocalisationService _localisationService,
ConfigServer _configServer
) : IOnLoad
@@ -49,11 +48,6 @@ public class BotGeneratorHelper(
return Task.CompletedTask;
}
public string GetRoute()
{
return "spt-botGeneratorHelper";
}
/// <summary>
/// Adds properties to an item
/// e.g. Repairable / HasHinge / Foldable / MaxDurability
@@ -64,9 +58,7 @@ public class BotGeneratorHelper(
public Upd GenerateExtraPropertiesForItem(TemplateItem? itemTemplate, string? botRole = null)
{
// Get raid settings, if no raid, default to day
var raidSettings = _applicationContext
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.GetValue<GetRaidConfigurationRequestData>();
var raidSettings = _profileActivityService.GetFirstProfileActivityRaidData()?.RaidConfiguration;
RandomisedResourceDetails randomisationSettings = null;
if (botRole is not null)
@@ -153,7 +145,7 @@ public class BotGeneratorHelper(
if (itemTemplate?.Parent == BaseClasses.FLASHLIGHT)
{
// Get chance from botconfig for bot type
var lightLaserActiveChance = raidSettings.IsNightRaid
var lightLaserActiveChance = raidSettings?.IsNightRaid ?? false
? GetBotEquipmentSettingFromConfig(botRole, "lightIsActiveNightChancePercent", 50)
: GetBotEquipmentSettingFromConfig(botRole, "lightIsActiveDayChancePercent", 25);
itemProperties.Light = new UpdLight
@@ -182,7 +174,7 @@ public class BotGeneratorHelper(
if (itemTemplate?.Parent == BaseClasses.NIGHTVISION)
{
// Get chance from botconfig for bot type
var nvgActiveChance = raidSettings.IsNightRaid
var nvgActiveChance = raidSettings?.IsNightRaid ?? false
? GetBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceNightPercent", 90)
: GetBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChanceDayPercent", 15);
itemProperties.Togglable = new UpdTogglable
@@ -1,4 +1,6 @@
using SPTarkov.DI.Annotations;
using System.Net;
using System.Net.Sockets;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Servers;
@@ -54,6 +56,30 @@ public class HttpServerHelper(ConfigServer configServer)
return $"wss://{BuildUrl()}";
}
/// <summary>
/// Method to determine if another version of the server is already running
/// </summary>
/// <returns>bool isAlreadyRunning</returns>
public bool IsAlreadyRunning()
{
TcpListener? listener = null;
try
{
listener = new(IPAddress.Parse(_httpConfig.Ip), _httpConfig.Port);
listener.Start();
return false;
}
catch (Exception)
{
return true;
}
finally
{
listener?.Stop();
}
}
public void SendTextJson(HttpResponse resp, object output)
{
resp.Headers.Append("Content-Type", mime["json"]);
@@ -77,7 +77,7 @@ public class InRaidHelper(
// Do after above filtering code to reduce work done
if (!isSurvived && !isTransfer && !_inRaidConfig.AlwaysKeepFoundInRaidOnRaidEnd)
{
RemoveFiRStatusFromCertainItems(postRaidProfile.Inventory.Items);
RemoveFiRStatusFromItems(postRaidProfile.Inventory.Items);
}
// Add items from client profile into server profile
@@ -94,7 +94,7 @@ public class InRaidHelper(
/// Remove FiR status from items.
/// </summary>
/// <param name="items">Items to process</param>
protected void RemoveFiRStatusFromCertainItems(List<Item> items)
protected void RemoveFiRStatusFromItems(List<Item> items)
{
var dbItems = _databaseService.GetItems();
@@ -124,24 +124,20 @@ public class InRaidHelper(
/// </summary>
/// <param name="itemsToAdd">Items we want to add</param>
/// <param name="serverInventoryItems">Location to add items to</param>
protected void AddItemsToInventory(List<Item> itemsToAdd, List<Item> serverInventoryItems)
protected void AddItemsToInventory(IEnumerable<Item> itemsToAdd, List<Item> serverInventoryItems)
{
foreach (var itemToAdd in itemsToAdd)
{
// Try to find index of item to determine if we should add or replace
var existingItemIndex = serverInventoryItems.FindIndex(inventoryItem => inventoryItem.Id == itemToAdd.Id
);
if (existingItemIndex == -1)
var existingItemIndex = serverInventoryItems.FindIndex(inventoryItem => inventoryItem.Id == itemToAdd.Id);
if (existingItemIndex != -1)
{
// Not found, add
serverInventoryItems.Add(itemToAdd);
}
else
{
// Replace item with one from client
// Replace existing item
serverInventoryItems.RemoveAt(existingItemIndex);
serverInventoryItems.Add(itemToAdd);
}
// Add new item
serverInventoryItems.Add(itemToAdd);
}
}
@@ -190,12 +186,9 @@ public class InRaidHelper(
}
}
foreach (var item in itemsInsideContainer)
foreach (var item in itemsInsideContainer.Where(item => item.Upd?.SpawnedInSession ?? false))
{
if (item.Upd.SpawnedInSession ?? false)
{
item.Upd.SpawnedInSession = false;
}
item.Upd.SpawnedInSession = false;
}
}
@@ -2,20 +2,24 @@ using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Eft.Ws;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Servers.Ws;
using SPTarkov.Server.Core.Services;
using SPTarkov.Server.Core.Utils;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class NotificationSendHelper(
ISptLogger<NotificationSendHelper> _logger,
SptWebSocketConnectionHandler _sptWebSocketConnectionHandler,
HashUtil _hashUtil,
SaveServer _saveServer,
NotificationService _notificationService,
TimeUtil _timeUtil
TimeUtil _timeUtil,
JsonUtil _jsonUtil
)
{
/// <summary>
@@ -25,12 +29,24 @@ public class NotificationSendHelper(
/// <param name="notificationMessage"></param>
public void SendMessage(string sessionID, WsNotificationEvent notificationMessage)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Send message for {sessionID} started, message: {_jsonUtil.Serialize(notificationMessage)}");
}
if (_sptWebSocketConnectionHandler.IsWebSocketConnected(sessionID))
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Send message for {sessionID} websocket available, message being sent");
}
_sptWebSocketConnectionHandler.SendMessage(sessionID, notificationMessage);
}
else
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Send message for {sessionID} websocket not available, queuing into profile");
}
_notificationService.Add(sessionID, notificationMessage);
}
}
@@ -546,58 +546,15 @@ public class QuestHelper(
return GetQuestsWithOnlyLevelRequirementStartCondition(quests);
}
/**
* Sets the item stack to new value, or delete the item if value
* <
* =
* 0
* /
* /
* TODO
* maybe
* merge
* this
* function
* and
* the
* one
* from
* customization
* @
* param
* pmcData
* Profile
* @
* param
* itemId
* id
* of
* item
* to
* adjust
* stack
* size
* of
* @
* param
* newStackSize
* Stack
* size
* to
* adjust
* to
* @
* param
* sessionID
* Session
* id
* @
* param
* output
* ItemEvent
* router
* response
*/
/// <summary>
/// Sets the item stack to new value, or delete the item if value <= 0
/// TODO: maybe merge this function and the one from customization
/// </summary>
/// <param name="pmcData">Profile</param>
/// <param name="itemId">Id of item to adjust stack size of</param>
/// <param name="newStackSize">Stack size to adjust to</param>
/// <param name="sessionID">Session id</param>
/// <param name="output">ItemEvent router response</param>
public void ChangeItemStack(
PmcData pmcData,
string itemId,
@@ -59,22 +59,16 @@ public class RagfairHelper(
if (!string.IsNullOrEmpty(request.LinkedSearchId))
{
var data = ragfairLinkedItemService.GetLinkedItems(request.LinkedSearchId);
result = data == null ? [] : [..data];
result = [..data];
}
// Case: category
if (!string.IsNullOrEmpty(request.HandbookId))
{
var handbook = GetCategoryList(request.HandbookId);
if (result.Count != null && result.Count > 0)
{
result = utilityHelper.ArrayIntersect(result, handbook);
}
else
{
result = handbook;
}
result = result?.Count > 0
? utilityHelper.ArrayIntersect(result, handbook)
: handbook;
}
return result;
@@ -167,8 +167,9 @@ public class RagfairOfferHelper(
/// <returns>Matching RagfairOffer objects</returns>
public List<RagfairOffer> GetOffersThatRequireItem(SearchRequestData searchRequest, PmcData pmcData)
{
// Get all offers that require the desired item and filter out offers from non traders if player below ragifar unlock
// Get all offers that require the desired item and filter out offers from non traders if player below ragfair unlock
var requiredOffers = _ragfairRequiredItemsService.GetRequiredItemsById(searchRequest.NeededSearchId);
var tieredFlea = _ragfairConfig.TieredFlea;
var tieredFleaLimitTypes = tieredFlea.UnlocksType;
return requiredOffers.Where(offer =>
@@ -346,7 +347,7 @@ public class RagfairOfferHelper(
bool playerIsFleaBanned = false)
{
var offerRootItem = offer.Items[0];
/** Currency offer is sold for */
// Currency offer is sold for
var moneyTypeTpl = offer.Requirements[0].Template;
var isTraderOffer = _databaseService.GetTraders().ContainsKey(offer.User.Id);
@@ -32,6 +32,8 @@ public class TradeHelper(
ICloner _cloner
)
{
protected static Lock buyLock = new();
/// <summary>
/// Buy item from flea or trader
/// </summary>
@@ -48,203 +50,207 @@ public class TradeHelper(
ItemEventRouterResponse output
)
{
List<Item> offerItems = [];
Action<int>? buyCallback;
if (string.Equals(buyRequestData.TransactionId, "ragfair", StringComparison.OrdinalIgnoreCase))
lock (buyLock)
{
// Called when player purchases PMC offer from ragfair
buyCallback = buyCount =>
List<Item> offerItems = [];
Action<int>? buyCallback;
if (string.Equals(buyRequestData.TransactionId, "ragfair", StringComparison.OrdinalIgnoreCase))
{
var allOffers = _ragfairServer.GetOffers();
// We store ragfair offerId in buyRequestData.item_id
var offerWithItem = allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId);
var itemPurchased = offerWithItem.Items.FirstOrDefault();
// Ensure purchase does not exceed trader item limit
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
// Called when player purchases PMC offer from ragfair
buyCallback = buyCount =>
{
CheckPurchaseIsWithinTraderItemLimit(
sessionID,
pmcData,
buyRequestData.TransactionId,
itemPurchased,
buyRequestData.ItemId,
buyCount
);
var allOffers = _ragfairServer.GetOffers();
// We store ragfair offerId in buyRequestData.item_id
var offerWithItem = allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId);
var itemPurchased = offerWithItem.Items.FirstOrDefault();
// Ensure purchase does not exceed trader item limit
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
{
CheckPurchaseIsWithinTraderItemLimit(
sessionID,
pmcData,
buyRequestData.TransactionId,
itemPurchased,
buyRequestData.ItemId,
buyCount
);
// Decrement trader item count
var itemPurchaseDetails = new PurchaseDetails
{
Items =
[
new PurchaseItems
{
ItemId = buyRequestData.ItemId,
Count = buyCount
}
],
TraderId = buyRequestData.TransactionId
};
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDetails, itemPurchased);
}
};
// buyCallback = BuyCallback1;
// Get raw offer from ragfair, clone to prevent altering offer itself
var allOffers = _ragfairServer.GetOffers();
var offerWithItemCloned = _cloner.Clone(allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId));
offerItems = offerWithItemCloned.Items;
}
else if (buyRequestData.TransactionId == Traders.FENCE)
{
buyCallback = buyCount =>
{
// Update assort/flea item values
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
var itemPurchased = traderAssorts.FirstOrDefault(assort => assort.Id == buyRequestData.ItemId);
// Decrement trader item count
var itemPurchaseDetails = new PurchaseDetails
{
Items =
[
new PurchaseItems
{
ItemId = buyRequestData.ItemId,
Count = buyCount
}
],
TraderId = buyRequestData.TransactionId
};
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDetails, itemPurchased);
}
};
itemPurchased.Upd.StackObjectsCount -= buyCount;
// buyCallback = BuyCallback1;
// Get raw offer from ragfair, clone to prevent altering offer itself
var allOffers = _ragfairServer.GetOffers();
var offerWithItemCloned = _cloner.Clone(allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId));
offerItems = offerWithItemCloned.Items;
}
else if (buyRequestData.TransactionId == Traders.FENCE)
{
buyCallback = buyCount =>
{
// Update assort/flea item values
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
var itemPurchased = traderAssorts.FirstOrDefault(assort => assort.Id == buyRequestData.ItemId);
_fenceService.AmendOrRemoveFenceOffer(buyRequestData.ItemId, buyCount);
};
// Decrement trader item count
itemPurchased.Upd.StackObjectsCount -= buyCount;
_fenceService.AmendOrRemoveFenceOffer(buyRequestData.ItemId, buyCount);
};
var fenceItems = _fenceService.GetRawFenceAssorts().Items;
var rootItemIndex = fenceItems.FindIndex(item => item.Id == buyRequestData.ItemId);
if (rootItemIndex == -1)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
var fenceItems = _fenceService.GetRawFenceAssorts().Items;
var rootItemIndex = fenceItems.FindIndex(item => item.Id == buyRequestData.ItemId);
if (rootItemIndex == -1)
{
_logger.Debug($"Tried to buy item {buyRequestData.ItemId} from fence that no longer exists");
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Tried to buy item {buyRequestData.ItemId} from fence that no longer exists");
}
var message = _localisationService.GetText("ragfair-offer_no_longer_exists");
_httpResponseUtil.AppendErrorToOutput(output, message);
return;
}
var message = _localisationService.GetText("ragfair-offer_no_longer_exists");
_httpResponseUtil.AppendErrorToOutput(output, message);
offerItems = _itemHelper.FindAndReturnChildrenAsItems(fenceItems, buyRequestData.ItemId);
}
else
{
buyCallback = buyCount =>
{
// Update assort/flea item values
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
var itemPurchased = traderAssorts.FirstOrDefault(item => item.Id == buyRequestData.ItemId);
// Ensure purchase does not exceed trader item limit
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
// Will throw error if check fails
{
CheckPurchaseIsWithinTraderItemLimit(
sessionID,
pmcData,
buyRequestData.TransactionId,
itemPurchased,
buyRequestData.ItemId,
buyCount
);
}
// Check if trader has enough stock
if (itemPurchased.Upd.StackObjectsCount < buyCount)
{
throw new Exception(
$"Unable to purchase {buyCount} items, this would exceed the remaining stock left {itemPurchased.Upd.StackObjectsCount} from the traders assort: {buyRequestData.TransactionId} this refresh"
);
}
// Decrement trader item count
itemPurchased.Upd.StackObjectsCount -= buyCount;
if (assortHasBuyRestrictions)
{
var itemPurchaseDat = new PurchaseDetails
{
Items =
[
new PurchaseItems
{
ItemId = buyRequestData.ItemId,
Count = buyCount
}
],
TraderId = buyRequestData.TransactionId
};
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat, itemPurchased);
}
};
// Get all trader assort items
var traderItems = _traderAssortHelper.GetAssort(sessionID, buyRequestData.TransactionId).Items;
// Get item + children for purchase
var relevantItems = _itemHelper.FindAndReturnChildrenAsItems(traderItems, buyRequestData.ItemId);
if (relevantItems.Count == 0)
{
_logger.Error(
$"Purchased trader: {buyRequestData.TransactionId} offer: {buyRequestData.ItemId} has no items");
}
offerItems.AddRange(relevantItems);
}
// Get item details from db
var itemDbDetails = _itemHelper.GetItem(offerItems.FirstOrDefault().Template).Value;
var itemMaxStackSize = itemDbDetails.Properties.StackMaxSize;
var itemsToSendTotalCount = buyRequestData.Count;
var itemsToSendRemaining = itemsToSendTotalCount;
// Construct array of items to send to player
List<List<Item>> itemsToSendToPlayer = [];
while (itemsToSendRemaining > 0)
{
var offerClone = _cloner.Clone(offerItems);
// Handle stackable items that have a max stack size limit
var itemCountToSend = Math.Min(itemMaxStackSize ?? 0, itemsToSendRemaining ?? 0);
offerClone.FirstOrDefault().Upd.StackObjectsCount = itemCountToSend;
// Prevent any collisions
_itemHelper.RemapRootItemId(offerClone);
if (offerClone.Count > 1)
{
_itemHelper.ReparentItemAndChildren(offerClone.FirstOrDefault(), offerClone);
}
itemsToSendToPlayer.Add(offerClone);
// Remove amount of items added to player stash
itemsToSendRemaining -= itemCountToSend;
}
// Construct request
var request = new AddItemsDirectRequest
{
ItemsWithModsToAdd = itemsToSendToPlayer,
FoundInRaid = foundInRaid,
Callback = buyCallback,
UseSortingTable = false
};
// Add items + their children to stash
_inventoryHelper.AddItemsToStash(sessionID, request, pmcData, output);
if (output.Warnings?.Count > 0)
{
return;
}
offerItems = _itemHelper.FindAndReturnChildrenAsItems(fenceItems, buyRequestData.ItemId);
}
else
{
buyCallback = buyCount =>
/// Pay for purchase
_paymentService.PayMoney(pmcData, buyRequestData, sessionID, output);
if (output.Warnings?.Count > 0)
{
// Update assort/flea item values
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
var itemPurchased = traderAssorts.FirstOrDefault(item => item.Id == buyRequestData.ItemId);
// Ensure purchase does not exceed trader item limit
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
// Will throw error if check fails
{
CheckPurchaseIsWithinTraderItemLimit(
sessionID,
pmcData,
buyRequestData.TransactionId,
itemPurchased,
buyRequestData.ItemId,
buyCount
);
}
// Check if trader has enough stock
if (itemPurchased.Upd.StackObjectsCount < buyCount)
{
throw new Exception(
$"Unable to purchase {buyCount} items, this would exceed the remaining stock left {itemPurchased.Upd.StackObjectsCount} from the traders assort: {buyRequestData.TransactionId} this refresh"
);
}
// Decrement trader item count
itemPurchased.Upd.StackObjectsCount -= buyCount;
if (assortHasBuyRestrictions)
{
var itemPurchaseDat = new PurchaseDetails
{
Items =
[
new PurchaseItems
{
ItemId = buyRequestData.ItemId,
Count = buyCount
}
],
TraderId = buyRequestData.TransactionId
};
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat, itemPurchased);
}
};
// Get all trader assort items
var traderItems = _traderAssortHelper.GetAssort(sessionID, buyRequestData.TransactionId).Items;
// Get item + children for purchase
var relevantItems = _itemHelper.FindAndReturnChildrenAsItems(traderItems, buyRequestData.ItemId);
if (relevantItems.Count == 0)
{
_logger.Error($"Purchased trader: {buyRequestData.TransactionId} offer: {buyRequestData.ItemId} has no items");
var errorMessage = $"Transaction failed: {output.Warnings.FirstOrDefault().ErrorMessage}";
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.UnknownTradingError);
}
offerItems.AddRange(relevantItems);
}
// Get item details from db
var itemDbDetails = _itemHelper.GetItem(offerItems.FirstOrDefault().Template).Value;
var itemMaxStackSize = itemDbDetails.Properties.StackMaxSize;
var itemsToSendTotalCount = buyRequestData.Count;
var itemsToSendRemaining = itemsToSendTotalCount;
// Construct array of items to send to player
List<List<Item>> itemsToSendToPlayer = [];
while (itemsToSendRemaining > 0)
{
var offerClone = _cloner.Clone(offerItems);
// Handle stackable items that have a max stack size limit
var itemCountToSend = Math.Min(itemMaxStackSize ?? 0, itemsToSendRemaining ?? 0);
offerClone.FirstOrDefault().Upd.StackObjectsCount = itemCountToSend;
// Prevent any collisions
_itemHelper.RemapRootItemId(offerClone);
if (offerClone.Count > 1)
{
_itemHelper.ReparentItemAndChildren(offerClone.FirstOrDefault(), offerClone);
}
itemsToSendToPlayer.Add(offerClone);
// Remove amount of items added to player stash
itemsToSendRemaining -= itemCountToSend;
}
// Construct request
var request = new AddItemsDirectRequest
{
ItemsWithModsToAdd = itemsToSendToPlayer,
FoundInRaid = foundInRaid,
Callback = buyCallback,
UseSortingTable = false
};
// Add items + their children to stash
_inventoryHelper.AddItemsToStash(sessionID, request, pmcData, output);
if (output.Warnings?.Count > 0)
{
return;
}
/// Pay for purchase
_paymentService.PayMoney(pmcData, buyRequestData, sessionID, output);
if (output.Warnings?.Count > 0)
{
var errorMessage = $"Transaction failed: {output.Warnings.FirstOrDefault().ErrorMessage}";
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.UnknownTradingError);
}
}
@@ -6,10 +6,11 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Loaders;
[Obsolete("This mod loader is obsolete and will be removed in 4.1.0. See documentation in IPostDBLoadModAsync for more information.")]
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader)]
public class PostDBModLoader(
ISptLogger<PostDBModLoader> _logger,
IEnumerable<IPostDBLoadMod> _postDbLoadMods
IEnumerable<IPostDBLoadModAsync> _postDbLoadMods
) : IOnLoad
{
public async Task OnLoad()
@@ -19,15 +20,10 @@ public class PostDBModLoader(
_logger.Info("Loading PostDBMods...");
foreach (var postDbLoadMod in _postDbLoadMods)
{
postDbLoadMod.PostDBLoad();
await postDbLoadMod.PostDBLoadAsync();
}
_logger.Info("Finished loading PostDBMods...");
}
}
public string GetRoute()
{
return "spt-post-db-mods";
}
}
@@ -6,10 +6,11 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Loaders;
[Obsolete("This mod loader is obsolete and will be removed in 4.1.0. See documentation in IPostSptLoadModAsync for more information.")]
[Injectable(TypePriority = OnLoadOrder.PostSptModLoader)]
public class PostSptModLoader(
ISptLogger<PostSptModLoader> _logger,
IEnumerable<IPostSptLoadMod> _postSptLoadMods
IEnumerable<IPostSptLoadModAsync> _postSptLoadMods
) : IOnLoad
{
public async Task OnLoad()
@@ -19,15 +20,10 @@ public class PostSptModLoader(
_logger.Info("Loading PostSptMods...");
foreach (var postSptLoadMod in _postSptLoadMods)
{
postSptLoadMod.PostSptLoad();
await postSptLoadMod.PostSptLoadAsync();
}
_logger.Info("Finished loading PostSptMods...");
}
}
public string GetRoute()
{
return "spt-post-spt-mods";
}
}
@@ -0,0 +1,30 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Models.External;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Loaders;
[Obsolete(
"This mod loader is obsolete and will be removed in 4.1.0. See documentation in IPreSptLoadModAsync for more information.")]
[Injectable(InjectionType.Singleton, TypePriority = OnLoadOrder.PreSptModLoader)]
public class PreSptModLoader(
ISptLogger<PreSptModLoader> _logger,
IEnumerable<IPreSptLoadModAsync> _preSptLoadMods
) : IOnLoad
{
public async Task OnLoad()
{
if (ProgramStatics.MODS())
{
_logger.Info("Loading PreSptMods...");
foreach (var postSptLoadMod in _preSptLoadMods)
{
await postSptLoadMod.PreSptLoadAsync();
}
_logger.Info("Finished loading PreSptMods...");
}
}
}
@@ -616,6 +616,7 @@ public record Ban
}
}
[EftEnumConverter]
public enum BanType
{
Chat,
@@ -408,11 +408,12 @@ public record LockableComponent
public LockableKeyComponent? KeyComponent { get; set; }
}
[EftEnumConverter]
public enum PinLockState
{
Free,
Locked,
Pinned
Pinned,
Locked
}
public record UpdBuff
@@ -67,7 +67,7 @@ public record TemplateItem
}
set
{
_type = string.Intern(value);
_type = value != null ? string.Intern(value) : null;
}
}
@@ -1,5 +1,8 @@
using SPTarkov.Server.Core.Utils.Json.Converters;
namespace SPTarkov.Server.Core.Models.Eft.Ws;
[EftEnumConverter]
public enum NotificationEventType
{
AssortmentUnlockRule,
@@ -1,3 +1,5 @@
using SPTarkov.Server.Core.Utils.Json.Converters;
namespace SPTarkov.Server.Core.Models.Enums;
public enum AirdropTypeEnum
@@ -8,6 +10,7 @@ public enum AirdropTypeEnum
Weapon
}
[EftEnumConverter]
public enum SptAirdropTypeEnum
{
mixed,
@@ -1,5 +1,8 @@
using SPTarkov.Server.Core.Utils.Json.Converters;
namespace SPTarkov.Server.Core.Models.Enums;
[EftEnumConverter]
public enum ArmorMaterial
{
UHMWPE,
@@ -1,5 +1,8 @@
namespace SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Utils.Json.Converters;
namespace SPTarkov.Server.Core.Models.Enums;
[EftEnumConverter]
public enum BodyPartColliderType
{
None = -1,

Some files were not shown because too many files have changed in this diff Show More