+2
-2
@@ -1,13 +1,13 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- SPT specific -->
|
||||
<SptVersion Condition="'$(SptVersion)' == ''">4.0.0</SptVersion>
|
||||
<SptVersion Condition="'$(SptVersion)' == ''">4.0.2</SptVersion>
|
||||
<SptCommit Condition="'$(SptCommit)' == ''">a12b34</SptCommit>
|
||||
<SptBuildTime Condition="'$(SptBuildTime)' == ''">0000000000</SptBuildTime>
|
||||
<SptBuildType Condition="'$(SptBuildType)' == ''">LOCAL</SptBuildType>
|
||||
<!-- BuildType options - LOCAL, DEBUG, RELEASE, BLEEDINGEDGE, BLEEDINGEDGEMODS - *must be all caps*-->
|
||||
<!-- SemVer-compliant version -->
|
||||
<!-- Format: 4.0.0-LOCAL+a12b34.0000000000 -->
|
||||
<!-- Format: 4.0.1-LOCAL+a12b34.0000000000 -->
|
||||
<Version>$(SptVersion)-$(SptBuildType)+$(SptCommit).$(SptBuildTime)</Version>
|
||||
<AssemblyVersion>$(SptVersion)</AssemblyVersion>
|
||||
<FileVersion>$(SptVersion)</FileVersion>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"maxBackups": 15,
|
||||
"backupCooldown": 30,
|
||||
"directory": "./user/profiles/backups",
|
||||
"backupInterval": {
|
||||
"enabled": false,
|
||||
|
||||
@@ -2501,7 +2501,12 @@
|
||||
"5a0c27731526d80618476ac4": 1,
|
||||
"5c99f98d86f7745c314214b3": 1,
|
||||
"60b0f6c058e0b0481a09ad11": 1,
|
||||
"62a09d3bcf4a99369e262447": 1
|
||||
"62a09d3bcf4a99369e262447": 1,
|
||||
"67e183377c6c2011970f3149": 0,
|
||||
"679baa2c61f588ae2b062a24": 0,
|
||||
"679baa4f59b8961f370dd683": 0,
|
||||
"679baa5a59b8961f370dd685": 0,
|
||||
"679baa9091966fe40408f149": 0
|
||||
},
|
||||
"assaultgroup": {},
|
||||
"bossboar": {},
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"projectName": "SPT",
|
||||
"compatibleTarkovVersion": "0.16.0.40087",
|
||||
"compatibleTarkovVersion": "0.16.9.40087",
|
||||
"serverName": "SPT Server",
|
||||
"profileSaveIntervalSeconds": 15,
|
||||
"profileSaveIntervalSeconds": 60,
|
||||
"sptFriendNickname": "SPT",
|
||||
"allowProfileWipe": true,
|
||||
"enableNoGCRegions": true,
|
||||
"noGCRegionMaxMemoryGB": 4,
|
||||
"noGCRegionMaxLOHMemoryGB": 3,
|
||||
"bsgLogging": {
|
||||
"verbosity": 6,
|
||||
"sendToServer": false
|
||||
@@ -37,7 +40,7 @@
|
||||
"question_1_answer_7": "Tag staff to inform them of your displeasure of a mod you don't like being released",
|
||||
"question_1_answer_8": "DM the author informing them of your displeasure at their mods existence",
|
||||
"question_1_answer_9": "DM the author and inform them you are going to sue them",
|
||||
"question_1_answer_10": "Stalk the author and create dozens of hub accounts to message them",
|
||||
"question_1_answer_10": "Stalk the author and create dozens of forge accounts to message them",
|
||||
|
||||
"question_2": "You want to download an older version of SPT but you have been informed old versions cannot be downloaded, what do you do?",
|
||||
"question_2_answer_1": "Get mad",
|
||||
@@ -45,10 +48,27 @@
|
||||
"question_2_answer_3": "Travel around various discords informing anyone who listens what bad people SPT are",
|
||||
"question_2_answer_4": "Create dozens of alt accounts to DM staff and inform them of your displeasure",
|
||||
"question_2_answer_5": "Ask when the old SPT version is coming back every day for weeks",
|
||||
|
||||
"question_3": "You are requesting help on the discord, what should you do?",
|
||||
"question_3_answer_1": "Ignore the FAQ and any channel vaguely support related",
|
||||
"question_3_answer_2": "Post in the pets channel, haha meow meow",
|
||||
"question_3_answer_3": "Pretend discord search does not exist and has never existed",
|
||||
"question_3_answer_4": "DM staff",
|
||||
"question_3_answer_5": "DM staff AND helpers, its their job to help",
|
||||
"question_3_answer_6": "DM staff, helpers AND anyone with a colored name, one of them will surely know the answer",
|
||||
"question_3_answer_7": "Post no logs, people should know what the problem is without me explaining",
|
||||
"question_3_answer_8": "Post logs in any channel EXCEPT support",
|
||||
"question_3_answer_9": "Ask for help in the support channel, but its for your friend who goes to a different hideout, they wouldn't know them",
|
||||
"question_3_answer_10": "Ask for help in the support channel then immediately disappear for 72 hours and ignore any responses",
|
||||
"question_3_answer_11": "Start insulting helpers when they give you the answer that resolves your problem",
|
||||
"question_3_answer_12": "Go insane and get permabanned when asked basic questions about your problem",
|
||||
"question_3_answer_13": "Ask for help with FIKA and then threaten to kill people when you're told to instead get help in FIKA discord",
|
||||
"question_3_answer_14": "Ask in the correct channel, post logs, get an answer and then when asked if the suggestion fixed the problem, DISAPPEAR AND NEVER BE SEEN FROM EVER AGAIN",
|
||||
|
||||
"title": "Feedback survey",
|
||||
"time": "About 1 minute",
|
||||
"description": "This was the second SPT survey, what a valuable use of 20 minutes that was.",
|
||||
"farewell": "You knew the first survey didn't do anything yet you still submitted the second one, you're quite an odd one."
|
||||
"description": "This was the third SPT survey, thanks for the data.",
|
||||
"farewell": "You keep filling these out even after we tell you they don't do anything, curious."
|
||||
}
|
||||
},
|
||||
"survey": {
|
||||
@@ -64,7 +84,8 @@
|
||||
"pages": [
|
||||
[
|
||||
0,
|
||||
1
|
||||
1,
|
||||
2
|
||||
]
|
||||
],
|
||||
"questions": [
|
||||
@@ -177,6 +198,100 @@
|
||||
"localeKey": "question_2_answer_5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"sortIndex": 1,
|
||||
"titleLocaleKey": "question_3",
|
||||
"hintLocaleKey": "",
|
||||
"answerLimit": 5,
|
||||
"answerType": "MultiOption",
|
||||
"answers": [
|
||||
{
|
||||
"id": 0,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_1"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_2"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_3"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_4"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_5"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_6"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_7"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_8"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_9"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_10"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_11"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_12"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_13"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"questionId": 2,
|
||||
"sortIndex": 1,
|
||||
"localeKey": "question_3_answer_14"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"isNew": false
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"healthMultipliers": {
|
||||
"blacked": 0.1
|
||||
"blacked": 0.1,
|
||||
"death": 0.3
|
||||
},
|
||||
"save": {
|
||||
"health": true
|
||||
|
||||
@@ -101,7 +101,9 @@
|
||||
"61b9e1aaef9a1b5d6a79899a",
|
||||
"5a43943586f77416ad2f06e2",
|
||||
"5a43957686f7742a2c2f11b0",
|
||||
"5c1a1e3f2e221602b66cc4c2"
|
||||
"5c1a1e3f2e221602b66cc4c2",
|
||||
"678f84bb9e85556ca60f0362",
|
||||
"679ba90d269ddfea47012159"
|
||||
],
|
||||
"customItemGlobalPresets": [
|
||||
{
|
||||
@@ -587,7 +589,10 @@
|
||||
"6866665cdf54e1190902df55",
|
||||
"6841b6b674a3c16f5e03d653",
|
||||
"679bab714e9ca6b3d80586b4",
|
||||
"64d4b23dc1b37504b41ac2b6"
|
||||
"64d4b23dc1b37504b41ac2b6",
|
||||
"678f84bb9e85556ca60f0362",
|
||||
"67ab3d4b83869afd170fdd3f",
|
||||
"678fa929819ddc4c350c0317"
|
||||
],
|
||||
"rewardItemTypeBlacklist": [
|
||||
"65649eb40bf0ed77b8044453"
|
||||
|
||||
@@ -1971,7 +1971,7 @@
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossName": "pmcUSEC",
|
||||
"BossPlayer": false,
|
||||
"BossZone": "ZoneCenter,ZoneCenterBot,ZoneOLI,ZoneIDEA,ZoneRoad,ZoneIDEAPark,ZoneGoshan,ZonePowerStation,ZoneTrucks,ZoneOLIPark",
|
||||
"BossZone": "ZoneCenter,ZoneCenterBot,ZoneOLI,ZoneIDEA,ZoneIDEAPark,ZoneGoshan,ZonePowerStation,ZoneTrucks,ZoneOLIPark",
|
||||
"Delay": 0,
|
||||
"IgnoreMaxBots": true,
|
||||
"RandomTimeSpawn": false,
|
||||
@@ -1991,7 +1991,7 @@
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossName": "pmcBEAR",
|
||||
"BossPlayer": false,
|
||||
"BossZone": "ZoneCenter,ZoneCenterBot,ZoneOLI,ZoneIDEA,ZoneRoad,ZoneIDEAPark,ZoneGoshan,ZonePowerStation,ZoneTrucks,ZoneOLIPark",
|
||||
"BossZone": "ZoneCenter,ZoneCenterBot,ZoneOLI,ZoneIDEA,ZoneIDEAPark,ZoneGoshan,ZonePowerStation,ZoneTrucks,ZoneOLIPark",
|
||||
"Delay": 0,
|
||||
"IgnoreMaxBots": true,
|
||||
"RandomTimeSpawn": false,
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
},
|
||||
"RAINY": {
|
||||
"clouds": {
|
||||
"0.7": 2,
|
||||
"0.7": 1,
|
||||
"0.9": 4,
|
||||
"1": 4,
|
||||
"1.2": 1
|
||||
"1.2": 2
|
||||
},
|
||||
"windSpeed": {
|
||||
"0": 3,
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
"619f94f5b90286142b59d45f": 10,
|
||||
"62a9e7d15ea3b87d6f642a28": 10,
|
||||
"6574aa9a1b144de18c0fba45": 10,
|
||||
"675ac3957908e416a20861e6": 10,
|
||||
"6644d2da35d958070c02642c": 0
|
||||
"6644d2da35d958070c02642c": 0,
|
||||
"675ac3957908e416a20861e6": 10
|
||||
},
|
||||
"voice": {
|
||||
"5fc1221a95572123ae7384a2": 1,
|
||||
@@ -697,7 +697,7 @@
|
||||
},
|
||||
"hard": {
|
||||
"Aiming": {
|
||||
"AIMING_TYPE": 2,
|
||||
"AIMING_TYPE": 4,
|
||||
"ANYTIME_LIGHT_WHEN_AIM_100": 70,
|
||||
"ANY_PART_SHOOT_TIME": 900,
|
||||
"BASE_HIT_AFFECTION_DELAY_SEC": 0.2,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3396
-3396
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
+2646
-2646
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3105
-3105
File diff suppressed because it is too large
Load Diff
+3245
-3245
File diff suppressed because it is too large
Load Diff
+3782
-3782
File diff suppressed because it is too large
Load Diff
+2887
-2887
File diff suppressed because it is too large
Load Diff
+2931
-2931
File diff suppressed because it is too large
Load Diff
+2631
-2631
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2808
-2808
File diff suppressed because it is too large
Load Diff
+3298
-3298
File diff suppressed because it is too large
Load Diff
+5575
-5575
File diff suppressed because it is too large
Load Diff
+4425
-4425
File diff suppressed because it is too large
Load Diff
+4419
-4419
File diff suppressed because it is too large
Load Diff
+2856
-2856
File diff suppressed because it is too large
Load Diff
+3541
-3541
File diff suppressed because it is too large
Load Diff
+3035
-3035
File diff suppressed because it is too large
Load Diff
+3436
-3436
File diff suppressed because it is too large
Load Diff
+3347
-3347
File diff suppressed because it is too large
Load Diff
+3644
-3644
File diff suppressed because it is too large
Load Diff
+3749
-3749
File diff suppressed because it is too large
Load Diff
+3026
-3026
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -688,7 +688,7 @@
|
||||
},
|
||||
"hard": {
|
||||
"Aiming": {
|
||||
"AIMING_TYPE": 2,
|
||||
"AIMING_TYPE": 4,
|
||||
"ANYTIME_LIGHT_WHEN_AIM_100": 70,
|
||||
"ANY_PART_SHOOT_TIME": 900,
|
||||
"BASE_HIT_AFFECTION_DELAY_SEC": 0.2,
|
||||
|
||||
@@ -12456,8 +12456,8 @@
|
||||
"61bcb5717113f767765c801f": "Eliminate celebrating enemies while wearing the specified equipment on Customs",
|
||||
"61bcb586ef0f505f0c6cd0fa": "Eliminate celebrating enemies while wearing the specified equipment on Factory",
|
||||
"61bcb59c54ba0f00d3551eb4": "Eliminate celebrating enemies while wearing the specified equipment in The Lab",
|
||||
"61bcc89aef0f505f0c6cd0fc Description": "Taking its name from an Old Norse Viking term for a lightning raid, Strandhögg was one of the first formalized battle tactics to use covert infiltrators in advance of an actual raiding operation. Strandhögg was a tactical game-changer in its time, just like the technological advances FirstSpear is bringing forward in this line of plate carriers.",
|
||||
"61bcc89aef0f505f0c6cd0fc Name": "FirstSpear Strandhogg plate carrier (Ranger Green)",
|
||||
"61bcc89aef0f505f0c6cd0fc Description": "Taking its name from an Old Norse Viking term for a lightning raid, Strandhögg was one of the first formalized battle tactics to use covert infiltrators in advance of an actual raiding operation. Strandhögg was a tactical game-changer in its time, just like the technological advances First Spear is bringing forward in this line of plate carriers.",
|
||||
"61bcc89aef0f505f0c6cd0fc Name": "First Spear Strandhogg plate carrier (Ranger Green)",
|
||||
"61bcc89aef0f505f0c6cd0fc ShortName": "Strandhogg",
|
||||
"61bf7b6302b3924be92fa8c3 Description": "A set of various metal technical parts, clamps and spacers. Extremely useful items for the repair of various mechanical devices and assemblies.",
|
||||
"61bf7b6302b3924be92fa8c3 Name": "Metal spare parts",
|
||||
@@ -23393,8 +23393,8 @@
|
||||
"67602a39c8e72a73250de739 Description": "A stargazer mask with a cap worn by astronomers from children's fairy tales.",
|
||||
"67602a39c8e72a73250de739 Name": "Astronomer mask",
|
||||
"67602a39c8e72a73250de739 ShortName": "Astronomer",
|
||||
"67603ea391ec8cab9406faa4 Description": "Festive T-shirt",
|
||||
"67603ea391ec8cab9406faa4 Name": "Snowman",
|
||||
"67603ea391ec8cab9406faa4 Description": "Festive t-shirt",
|
||||
"67603ea391ec8cab9406faa4 Name": "Snowman t-shirt",
|
||||
"67603ea391ec8cab9406faa4 ShortName": "Snowman",
|
||||
"67604476b1ae3717835ccaed": "Stash a pack of any 7.62x51 ammo at the specified spot on Customs",
|
||||
"676047054c9696a7d071bc08": "Install the second WI-FI Camera inside the industrial plant on Customs",
|
||||
@@ -24169,7 +24169,7 @@
|
||||
"67aaf808bf7609058606a926 Name": "Neoprene mask (M90 Desert)",
|
||||
"67aaf808bf7609058606a926 ShortName": "M90 D",
|
||||
"67aaf82d508ee9b6440e9c46 Description": "A lightweight neoprene mask with a luxury design.",
|
||||
"67aaf82d508ee9b6440e9c46 Name": "Neoprene mask (Louise Pitton)",
|
||||
"67aaf82d508ee9b6440e9c46 Name": "Neoprene mask (Loui Peeton)",
|
||||
"67aaf82d508ee9b6440e9c46 ShortName": "Pitton",
|
||||
"67aaf84104dca1c82c071cf6 Description": "A lightweight neoprene mask with a professional wrestling print.",
|
||||
"67aaf84104dca1c82c071cf6 Name": "Neoprene mask (Lucha Libre)",
|
||||
@@ -30874,7 +30874,7 @@
|
||||
"ragfair/Trader will be unlocked with the quest completion": "Trader will be unlocked with the task completion",
|
||||
"ragfair/Unable to sell the item that contains": "",
|
||||
"ragfair/Unable to sell the item that contains {0}": "Unable to sell the item that contains \"{0}\"",
|
||||
"ragfair/Unlocked at character LVL {0}": "The ability to create offers as well as to see and buy other players' goods is currently unavailable.",
|
||||
"ragfair/Unlocked at character LVL {0}": "The ability to create offers as well as to see and buy other players' goods will be unlocked on level {0}.",
|
||||
"ragfair/W-LIST": "W-LIST",
|
||||
"ragfair/You are temporarily banned from flea market": "You are temporarily banned from the Flea Market",
|
||||
"ragfair/You cannot place non-empty container at ragfair": "You can't sell non-empty container at the Flea Market",
|
||||
|
||||
@@ -12459,8 +12459,8 @@
|
||||
"61bcb5717113f767765c801f": "Одеться в условленную экипировку и ликвидировать празднующих на Таможне",
|
||||
"61bcb586ef0f505f0c6cd0fa": "Одеться в условленную экипировку и ликвидировать празднующих на Заводе",
|
||||
"61bcb59c54ba0f00d3551eb4": "Одеться в условленную экипировку и ликвидировать празднующих в Лаборатории",
|
||||
"61bcc89aef0f505f0c6cd0fc Description": "Получив свое название от древнескандинавского термина «молниеносный рейд» викингов, Strandhögg был одной из первых формализованных боевых тактик, в которых использовались тайные лазутчики перед фактической операцией рейда. Strandhögg изменил тактику в свое время, как и технологические достижения FirstSpear в этой линейке плейткерриеров.",
|
||||
"61bcc89aef0f505f0c6cd0fc Name": "Разгрузочный жилет с бронепластинами FirstSpear \"Strandhogg\" (Ranger Green)",
|
||||
"61bcc89aef0f505f0c6cd0fc Description": "Получив свое название от древнескандинавского термина «молниеносный рейд» викингов, Strandhögg был одной из первых формализованных боевых тактик, в которых использовались тайные лазутчики перед фактической операцией рейда. Strandhögg изменил тактику в свое время, как и технологические достижения First Spear в этой линейке плейткерриеров.",
|
||||
"61bcc89aef0f505f0c6cd0fc Name": "Разгрузочный жилет с бронепластинами First Spear \"Strandhogg\" (Ranger Green)",
|
||||
"61bcc89aef0f505f0c6cd0fc ShortName": "Strandhogg",
|
||||
"61bf7b6302b3924be92fa8c3 Description": "Набор различных металлических технических деталей, хомутов и прокладочных колец. Крайне полезные предметы для ремонта различных механических устройств и агрегатов.",
|
||||
"61bf7b6302b3924be92fa8c3 Name": "Набор металлических частей",
|
||||
@@ -24289,7 +24289,7 @@
|
||||
"67aaf808bf7609058606a926 Name": "Неопреновая маска (M90 Desert)",
|
||||
"67aaf808bf7609058606a926 ShortName": "M90 D",
|
||||
"67aaf82d508ee9b6440e9c46 Description": "Лёгкая неопреновая маска для лица с принтом. Такая маска — настоящий символ роскоши в Таркове.",
|
||||
"67aaf82d508ee9b6440e9c46 Name": "Неопреновая маска \"Louise Pitton\"",
|
||||
"67aaf82d508ee9b6440e9c46 Name": "Неопреновая маска \"Loui Peeton\"",
|
||||
"67aaf82d508ee9b6440e9c46 ShortName": "Pitton",
|
||||
"67aaf84104dca1c82c071cf6 Description": "Лёгкая неопреновая маска для лица. В такой и на ринг выйти не стыдно.",
|
||||
"67aaf84104dca1c82c071cf6 Name": "Неопреновая маска \"Lucha Libre\"",
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
"fence-unable_to_find_assort_by_id": "Kan ikke finde fence assort for id: %s",
|
||||
"fence-unable_to_find_offer_by_id": "Kan ikke finde tilbud med id: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Ingen penetrationsværdi fundet for Ammo: %s, Kan ikke kontrollere om den er over penetrationsgrænsen, antager falsk",
|
||||
"fixer-clothing_item_found": "Beklædning: %s fundet i profil, der ikke eksisterer i SPT. Du vil opleve fejl, dette kan skyldes at du bruger en tøj mod og har fjernet modden med din karakter stadig iført i den. BRUG IKKE DENNE PROFILE. Åbn SPT_Data\\Server\\configs\\core.json, rediger 'removeModItemsFromProfile' og sørg for at den er sat til true. Dette vil gøre det muligt for serveren at redigere din profil og forhåbentlig fjerne det manglende tøj",
|
||||
"fixer-mod_item_found": "Genstand: %s fundet i profil, som ikke findes i genstands db. Du vil opleve fejl, dette kan skyldes at du bruger en mod og har fjernet modden uden at slette de modded genstande fra dit stash. BRUG IKKE DENNE PROFILE. Åbn SPT_Data\\Server\\configs\\core.json, rediger 'removeModItemsFromProfile' og sørg for at den er sat til true. Dette vil give serveren mulighed for at redigere din profil og forhåbentlig fjerne de manglende genstande",
|
||||
"fixer-trader_found": "trader: %s fundet i profil, men findes ikke i SPT. Du vil opleve fejl, dette kan skyldes at du bruger en trader mod og har fjernet modden uden at slette beskederne fra nævnte trader. BRUG IKKE DENNE PROFIL . Åbn SPT_Data\\Server\\configs\\core.json, rediger 'removeModItemsFromProfile' og `removeInvalidTradersFromProfile` og sørg for at den er sat til true. Dette vil give serveren mulighed for at redigere din profil og forhåbentlig fjerne de gamle beskeder",
|
||||
"fixer-clothing_item_found": "Beklædning: %s fundet i profil, der ikke eksisterer i SPT. Du vil opleve fejl, dette kan skyldes at du bruger en tøj mod og har fjernet modden med din karakter stadig iført i den. BRUG IKKE DENNE PROFILE. Åbn SPT\\SPT_Data\\configs\\core.json, rediger 'removeModItemsFromProfile' og sørg for at den er sat til true. Dette vil gøre det muligt for serveren at redigere din profil og forhåbentlig fjerne det manglende tøj",
|
||||
"fixer-mod_item_found": "Genstand: %s fundet i profil, som ikke findes i genstands db. Du vil opleve fejl, dette kan skyldes at du bruger en mod og har fjernet modden uden at slette de modded genstande fra dit stash. BRUG IKKE DENNE PROFILE. Åbn SPT\\SPT_Data\\configs\\core.json, rediger 'removeModItemsFromProfile' og sørg for at den er sat til true. Dette vil give serveren mulighed for at redigere din profil og forhåbentlig fjerne de manglende genstande",
|
||||
"fixer-trader_found": "trader: %s fundet i profil, men findes ikke i SPT. Du vil opleve fejl, dette kan skyldes at du bruger en trader mod og har fjernet modden uden at slette beskederne fra nævnte trader. BRUG IKKE DENNE PROFIL . Åbn SPT\\SPT_Data\\configs\\core.json, rediger 'removeModItemsFromProfile' og `removeInvalidTradersFromProfile` og sørg for at den er sat til true. Dette vil give serveren mulighed for at redigere din profil og forhåbentlig fjerne de gamle beskeder",
|
||||
"fixer-updated_pockets": "Opdateret 'lomme' genstand til nye 18876 version med x3 specielle pladser",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - kan ikke finde bot af typen %s i databasen, spring over",
|
||||
"gameevent-no_gear_data": "Ingen geardata i sæsonhændelser.json config til begivenhed %s",
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"fence-unable_to_find_assort_by_id": "Im Sortiment von Fence konnte ID: %s nicht gefunden werden",
|
||||
"fence-unable_to_find_offer_by_id": "Angebot mit id: %s konnte nicht gefunden werden",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Kein Penetrationswert für Ammo; %s gefunden. Kann nicht überprüfen, ob die obere Penetrationsgrenze überschritten wird, gehe von falsch aus",
|
||||
"fixer-clothing_item_found": "Bekleidungsgegenstand: %s im Profil gefunden, das nicht in SPT existiert. Du WIRST Fehler in der Anwendung erhalten, dies kann durch die Verwendung eines Bekleidungsmods und/oder das Entfernen der Mod während der Charakter es noch trägt. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT_Data\\Server\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf `true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlende Kleidung zu entfernen",
|
||||
"fixer-mod_item_found": "Artikel: %s im Profil gefunden, das nicht in der Gegenstandsdatenbank ´items db´ db vorhanden ist. Du WIRST Fehler im Inventar erhalten. Dies kann durch die Verwendung eines Gegenstandsmods und/oder das Entfernen der Mod verursacht werden, ohne die modifizierten Gegenstände aus deinem Inventar zu löschen. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT_Data\\Server\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf ´true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlerhaften Gegestände zu entfernen",
|
||||
"fixer-trader_found": "Händler: %s im Profil gefunden, der nicht in SPT vorhanden ist. Du WIRST Fehler beim handeln erhalten. Dies kann durch die Verwendung eines Händlermods und/oder das Entfernen des Mods verursacht werden, ohne die Nachrichten von diesem Händler zu löschen. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT_Data\\Server\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf ´true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlerhaften Einträge zu entfernen",
|
||||
"fixer-clothing_item_found": "Bekleidungsgegenstand: %s im Profil gefunden, das nicht in SPT existiert. Du WIRST Fehler in der Anwendung erhalten, dies kann durch die Verwendung eines Bekleidungsmods und/oder das Entfernen der Mod während der Charakter es noch trägt. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT\\SPT_Data\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf `true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlende Kleidung zu entfernen",
|
||||
"fixer-mod_item_found": "Artikel: %s im Profil gefunden, das nicht in der Gegenstandsdatenbank ´items db´ db vorhanden ist. Du WIRST Fehler im Inventar erhalten. Dies kann durch die Verwendung eines Gegenstandsmods und/oder das Entfernen der Mod verursacht werden, ohne die modifizierten Gegenstände aus deinem Inventar zu löschen. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT\\SPT_Data\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf ´true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlerhaften Gegestände zu entfernen",
|
||||
"fixer-trader_found": "Händler: %s im Profil gefunden, der nicht in SPT vorhanden ist. Du WIRST Fehler beim handeln erhalten. Dies kann durch die Verwendung eines Händlermods und/oder das Entfernen des Mods verursacht werden, ohne die Nachrichten von diesem Händler zu löschen. DIESES PROFIL SOLLTE NICHT VERWENDET WERDEN. Öffnen Sie SPT\\SPT_Data\\configs\\core.json, bearbeiten Sie den Wert in 'removeModItemsFromProfile' auf ´true´. Dies wird dem Server erlauben, dein Profil zu bearbeiten und hoffentlich die fehlerhaften Einträge zu entfernen",
|
||||
"fixer-updated_pockets": "'Taschen' Gegenstand würde aktualisiert auf die neue Version 18876 mit x3 speziellen Steckplätzen",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() – Bot vom Typ %s konnte in der Datenbank nicht gefunden werden. Überspringen",
|
||||
"gameevent-no_gear_data": "Keine Ausrüstungsdaten in der seasonalevents.json Konfiguration für das Event %s",
|
||||
|
||||
@@ -92,9 +92,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "Unable to find offer with id: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "No penetration value found for Ammo: %s, Unable to check if its above penetration limit, assuming false",
|
||||
"fence-unable_to_find_root_item_to_add": "Unable to add items to Fence as no root items were found",
|
||||
"fixer-clothing_item_found": "Clothing item: %s found in profile that does not exist in SPT. You WILL experience errors, this can be due to using a clothing mod and removing the mod with your character still wearing it. DO NOT USE THIS PROFILE. Open SPT_Data\\Server\\configs\\core.json, edit 'removeModItemsFromProfile' to be true. This will allow the server to edit your profile and hopefully remove the missing clothing",
|
||||
"fixer-mod_item_found": "Item: %s found in profile that does not exist in items db. You WILL experience errors, this can be due to using an items mod and removing the mod without deleting the modded items from your inventory. DO NOT USE THIS PROFILE. Open SPT_Data\\Server\\configs\\core.json, edit 'removeModItemsFromProfile' to be true. This will allow the server to edit your profile and hopefully remove the bad items",
|
||||
"fixer-trader_found": "Trader: %s found in profile but does not exist in SPT. You WILL experience errors, this can be due to using a trader mod and removing the mod without deleting the messages from said trader. DO NOT USE THIS PROFILE. Open SPT_Data\\Server\\configs\\core.json, edit 'removeModItemsFromProfile' and `removeInvalidTradersFromProfile` to be true. This will allow the server to edit your profile and hopefully remove the bad messages",
|
||||
"fixer-clothing_item_found": "Clothing item: %s found in profile that does not exist in SPT. You WILL experience errors, this can be due to using a clothing mod and removing the mod with your character still wearing it. DO NOT USE THIS PROFILE. Open SPT\\SPT_Data\\configs\\core.json, edit 'removeModItemsFromProfile' to be true. This will allow the server to edit your profile and hopefully remove the missing clothing",
|
||||
"fixer-mod_item_found": "Item: %s found in profile that does not exist in items db. You WILL experience errors, this can be due to using an items mod and removing the mod without deleting the modded items from your inventory. DO NOT USE THIS PROFILE. Open SPT\\SPT_Data\\configs\\core.json, edit 'removeModItemsFromProfile' to be true. This will allow the server to edit your profile and hopefully remove the bad items",
|
||||
"fixer-trader_found": "Trader: %s found in profile but does not exist in SPT. You WILL experience errors, this can be due to using a trader mod and removing the mod without deleting the messages from said trader. DO NOT USE THIS PROFILE. Open SPT\\SPT_Data\\configs\\core.json, edit 'removeModItemsFromProfile' and `removeInvalidTradersFromProfile` to be true. This will allow the server to edit your profile and hopefully remove the bad messages",
|
||||
"fixer-updated_pockets": "Updated 'pocket' item to new 18876 version with x3 special slots",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - unable to find bot of type %s in database, skipping",
|
||||
"gameevent-no_gear_data": "No gear data in seasonalevents.json config for event %s",
|
||||
@@ -178,7 +178,7 @@
|
||||
"location-critical_error_see_log": "A critical error occurred when generating loot, see server log for details",
|
||||
"location-dynamic_items_spawned_success": "A total of: %s dynamic items spawned",
|
||||
"location-generated_success": "Generated location: %s",
|
||||
"location-loot_pool_is_empty_skipping": "The loot pool for postion id: %s had 0 items, skipping loot for this poistion",
|
||||
"location-loot_pool_is_empty_skipping": "The loot pool for position id: %s had 0 items, skipping loot for this position",
|
||||
"location-map_has_no_loose_loot_data": "Map: %s has no loose loot data, skipping",
|
||||
"location-missing_dynamic_template": "Chosen dynamic spawnpoint: %s has no template, skipping",
|
||||
"location-missing_item_distribution_data": "Container with id: %s is missing item distribution data",
|
||||
@@ -225,7 +225,7 @@
|
||||
"modloader-missing_dependency": "Mod: {{mod}} requires: {{modDependency}} to be installed.",
|
||||
"modloader-self_dependency": "Mod {{mod}} depends on itself, please remove it from your own dependency list",
|
||||
"modloader-self_incompatibility": "Mod {{mod}} is incompatible with itself, please remove it from your own incompatibilities list",
|
||||
"modloader-missing_package_json": "Mod: (%s) is missing a package.json. Make sure you have checked the mods hub page for install instructions",
|
||||
"modloader-missing_package_json": "Mod: (%s) is missing a package.json. Make sure you have checked the mods forge page for install instructions",
|
||||
"modloader-missing_package_json_property": "Mod: {{modName}} package.json requires {{prop}} property",
|
||||
"modloader-missing_sptversion_field": "Mod %s is missing the sptVersion field, most likely due to being out of date and incompatible with the current version of SPT",
|
||||
"modloader-mod_has_no_main_property": "ModLoader: Mod (%s) is incompatible. It lacks a 'main' property",
|
||||
@@ -235,7 +235,7 @@
|
||||
"modloader-mod_order_missing": "ModLoader: order.json is missing, creating...",
|
||||
"modloader-mod_order_missing_from_json": "ModLoader: Mod %s is missing from order.json, adding",
|
||||
"modloader-no_mods_loaded": "Errors were found with mods, NO MODS WILL BE LOADED",
|
||||
"modloader-not_correct_mod_folder": "A folder called: (%s) exists in your mods folder. You incorrectly installed a mod. You may have extracted the contents of a mod directly into the mod folder by mistake. Refer to the websites FAQ and the mods hub page on how to install mods correctly",
|
||||
"modloader-not_correct_mod_folder": "A folder called: (%s) exists in your mods folder. You incorrectly installed a mod. You may have extracted the contents of a mod directly into the mod folder by mistake. Refer to the websites FAQ and the mods forge page on how to install mods correctly",
|
||||
"modloader-outdated_dependency": "Mod: {{mod}} requires: {{modDependency}} version: {{requiredVersion}}. Current installed version is: {{currentVersion}}",
|
||||
"modloader-outdated_sptversion_field": "Mod: {{modName}} {{modVersion}} is not compatible with the current version of SPT. It was made for SPT: {{desiredSptVersion}} Please check for an updated version of this mod. You may encounter issues - no support will be provided!",
|
||||
"modloader-skipped_mod": "Skipping loading of Mod: {{mod}}",
|
||||
@@ -498,10 +498,10 @@
|
||||
"pmcresponse-victim_negative_92": "You did me a favour I was gonna fence that trash kit anyway",
|
||||
"pmcresponse-victim_negative_93": "You are such a rat i bet youre called Ratthew",
|
||||
"pmcresponse-victim_negative_94": "I bet you auditioned for Stuart Little you rat",
|
||||
"pmcresponse-victim_negative_95": "I bet youre using that radar mod i saw on the hub",
|
||||
"pmcresponse-victim_negative_95": "I bet youre using that radar mod i saw on the forge",
|
||||
"pmcresponse-victim_negative_96": "1v1 me in dorms, we will see whos the better player",
|
||||
"pmcresponse-victim_negative_97": "I bet youre one of those people who writes posts on the hub about bad streets fps",
|
||||
"pmcresponse-victim_negative_98": "I bet you installed out of date mods and got loads of errors and wrote a huge hub post about it",
|
||||
"pmcresponse-victim_negative_97": "I bet youre one of those people who writes posts on the forge about bad streets fps",
|
||||
"pmcresponse-victim_negative_98": "I bet you installed out of date mods and got loads of errors and wrote a huge forge post about it",
|
||||
"pmcresponse-victim_negative_99": "Your computer so bad you get 20fps on streets",
|
||||
"pmcresponse-victim_negative_103": "Yeah you only got that kill because i had busy hands",
|
||||
"pmcresponse-victim_negative_104": "I would have heard you but i forgot to turn off binaural audio",
|
||||
@@ -513,7 +513,7 @@
|
||||
"pmcresponse-victim_plead_11": "I just went to the toilet and you shot me",
|
||||
"pmcresponse-victim_plead_12": "I just went to the kitchen to pick up my dino nuggets and you killed me",
|
||||
"pmcresponse-victim_plead_13": "bro please",
|
||||
"pmcresponse-victim_plead_14": "Just you wait until I download some more mods off the hub, then I'll get you",
|
||||
"pmcresponse-victim_plead_14": "Just you wait until I download some more mods off the forge, then I'll get you",
|
||||
"pmcresponse-victim_plead_15": "Does the wiggle mean nothing smh smh fr",
|
||||
"pmcresponse-victim_plead_16": "I cant stand this game, I'm going back to roblox",
|
||||
"pmcresponse-victim_plead_17": "The wiggle is clearly a sign I'm friendly",
|
||||
|
||||
@@ -81,9 +81,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "No se puede encontrar la oferta con id: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "No se encontró valor de penetración para munición: %s, No se puede comprobar si está por encima de su límite de penetración, asumiendo falso",
|
||||
"fence-unable_to_find_root_item_to_add": "Incapaz de añadir objetos a Fence, ya que no se encontraron objetos raíz",
|
||||
"fixer-clothing_item_found": "Item de ropa: %s encontrado en perfil que no existe en SPT. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de ropa y remover el mod con tu personaje todavía usándolo. NO USES ESTE PERFIL. Abre SPT_Data\\Server\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al servidor modificar tu perfil y con suerte quitar la ropa faltante",
|
||||
"fixer-mod_item_found": "Item: %s encontrado en perfil que no existe en la base de datos de items. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de items y remover el mod sin eliminar estos items de tu inventario. NO USES ESTE PERFIL. Abre SPT_Data\\Server\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al servidor modificar tu perfil y con suerte remover los items malos",
|
||||
"fixer-trader_found": "Comerciante: %s encontrado en el perfil que no existe en SPT. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de comerciantes y removerlo sin eliminar los mensajes del mismo. NO USES ESTE PERFIL. Abre SPT_Data\\Server\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al sevidor modificar tu perfil y con suerte remover los mensajes malos",
|
||||
"fixer-clothing_item_found": "Item de ropa: %s encontrado en perfil que no existe en SPT. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de ropa y remover el mod con tu personaje todavía usándolo. NO USES ESTE PERFIL. Abre SPT\\SPT_Data\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al servidor modificar tu perfil y con suerte quitar la ropa faltante",
|
||||
"fixer-mod_item_found": "Item: %s encontrado en perfil que no existe en la base de datos de items. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de items y remover el mod sin eliminar estos items de tu inventario. NO USES ESTE PERFIL. Abre SPT\\SPT_Data\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al servidor modificar tu perfil y con suerte remover los items malos",
|
||||
"fixer-trader_found": "Comerciante: %s encontrado en el perfil que no existe en SPT. EXPERIMENTARÁS errores, esto puede deberse a usar un mod de comerciantes y removerlo sin eliminar los mensajes del mismo. NO USES ESTE PERFIL. Abre SPT\\SPT_Data\\configs\\core.json, edita 'removeModItemsFromProfile' con el valor true. Esto permitirá al sevidor modificar tu perfil y con suerte remover los mensajes malos",
|
||||
"fixer-updated_pockets": "Se ha actualizado 'pocket' item a la nueva version 18876 con x3 espacios especiales",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - no se puede encontrar el bot tipo %s en la base de datos, omitiendo",
|
||||
"gameevent-no_gear_data": "No se encontro datos de equipamento en la configuracion seasonalevents.json para el evento %s",
|
||||
|
||||
@@ -92,9 +92,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "Impossible de trouver l'offre avec l'id: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Aucune valeur de pénétration trouvée pour la munition: %s, impossible de vérifier si elle est supérieure à la limite de pénétration, faux par défaut",
|
||||
"fence-unable_to_find_root_item_to_add": "Impossible d'ajouter des objets dans l'inventaire de Fence car aucun objet source n'a été trouvé",
|
||||
"fixer-clothing_item_found": "Vêtement: %s a été trouvé dans le profil mais n'existe pas dans SPT. Vous rencontrerez des erreurs, qui peuvent être dues au fait d'utiliser un mod de vêtement que vous ayez supprimer alors que votre personnage le portait toujours. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT_Data\\Server\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer le vêtement manquant",
|
||||
"fixer-mod_item_found": "Objet: %s a été trouvé dans le profil mais n'existe pas dans la base de données. Cela VA produire des erreurs, ce type d'erreur peut survenir si vous utilisez un mod rajoutant des objets et que vous supprimez ce mod sans supprimer les items moddés de votre inventaire. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT_Data\\Server\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer ces objets",
|
||||
"fixer-trader_found": "Trader: %s a été trouvé dans le profil mais n'existe pas dans SPT. Cela VA produire des erreurs, ce type d'erreur peut survenir si vous utilisez un mod de trader et que vous supprimez ce mod sans supprimer les messages de ce trader. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT_Data\\Server\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer les messages",
|
||||
"fixer-clothing_item_found": "Vêtement: %s a été trouvé dans le profil mais n'existe pas dans SPT. Vous rencontrerez des erreurs, qui peuvent être dues au fait d'utiliser un mod de vêtement que vous ayez supprimer alors que votre personnage le portait toujours. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT\\SPT_Data\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer le vêtement manquant",
|
||||
"fixer-mod_item_found": "Objet: %s a été trouvé dans le profil mais n'existe pas dans la base de données. Cela VA produire des erreurs, ce type d'erreur peut survenir si vous utilisez un mod rajoutant des objets et que vous supprimez ce mod sans supprimer les items moddés de votre inventaire. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT\\SPT_Data\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer ces objets",
|
||||
"fixer-trader_found": "Trader: %s a été trouvé dans le profil mais n'existe pas dans SPT. Cela VA produire des erreurs, ce type d'erreur peut survenir si vous utilisez un mod de trader et que vous supprimez ce mod sans supprimer les messages de ce trader. N'UTILISEZ PAS CE PROFIL. Ouvrez SPT\\SPT_Data\\configs\\core.json, éditez 'removeModItemsFromProfile' à la valeur true. Cela permettra au serveur d'éditer votre profil, dans le but de supprimer les messages",
|
||||
"fixer-updated_pockets": "Objet 'poches' mis à jour vers la version 18876 avec x3 emplacements spéciaux",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - impossible de trouver le bot de type %s dans la base de données, passage à l'étape suivante",
|
||||
"gameevent-no_gear_data": "Aucune donnée d'équipement dans la configuration de seasonalevents.json pour l'événement %s",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"fence-unable_to_find_offer_by_id": "Nem sikerült megtalálni az eltávolítandó ajánlatot a következő azonosítóval: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Nem találhatóak penetrációs értékek a következő lőszerhez: %s, nem sikerült megnézni hogy a penetrációs limit felett van-e, hamis érték feltételezve",
|
||||
"fence-unable_to_find_root_item_to_add": "Nem lehetséges Fence-hez tárgyat adni, mivel a tárgy nem található",
|
||||
"fixer-mod_item_found": "Tétel: %s találtunk a profilban, viszont nem létezik az adatbázisban. HIBÁKAT fogsz tapasztalni, ami egy régi, használt mod eltávolítása miatt történhetett. A tételek törlése nélkül NE HASZNÁLD EZT A PROFILT. Nyisd meg a SPT_Data\\Server\\configs\\core.json fájlt, módosítsd a 'removeModItemsFromProfile' részt \"true\"-ra. Így használni tudod a profilod, és remélhetőleg eltávolít minden hibás tételt",
|
||||
"fixer-mod_item_found": "Tétel: %s találtunk a profilban, viszont nem létezik az adatbázisban. HIBÁKAT fogsz tapasztalni, ami egy régi, használt mod eltávolítása miatt történhetett. A tételek törlése nélkül NE HASZNÁLD EZT A PROFILT. Nyisd meg a SPT\\SPT_Data\\configs\\core.json fájlt, módosítsd a 'removeModItemsFromProfile' részt \"true\"-ra. Így használni tudod a profilod, és remélhetőleg eltávolít minden hibás tételt",
|
||||
"fixer-updated_pockets": "Frissített 'pocket' tárgy az új, 18876-os verzióra, amely x3 speciális slotot tartalmaz",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - nem találja a %s típusú botot az adatbázisban, kihagyás",
|
||||
"gameevent-no_gear_data": "A seasonalevents.json konfigban nincsenek felszerelés adatok a következő eseményhez %s",
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
"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-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\\SPT_Data\\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\\SPT_Data\\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\\SPT_Data\\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",
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "ID: %s のオファーが見つかりません",
|
||||
"fence-unable_to_get_ammo_penetration_value": "弾薬 %s から貫通力の値が見つかりません。貫通力の限界を計算できません。falseを仮定します",
|
||||
"fence-unable_to_find_root_item_to_add": "ルートアイテムが見つからないため、Fenceにアイテムを追加できませんでした",
|
||||
"fixer-clothing_item_found": "存在しない衣装アイテム %s がSPTから検出されました。エラーが発生する可能性があります。これは衣装アイテムを追加するModを導入したあと、該当するアイテムを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT_Data\\Server\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ存在しない衣装アイテムを削除してくれます",
|
||||
"fixer-mod_item_found": "アイテムDBに存在しないアイテム %s がSPTから見つかりました。エラーが発生する可能性があります。これはアイテムを追加するModを導入したあと、該当するアイテムを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT_Data\\Server\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ存在しないアイテムを削除してくれます",
|
||||
"fixer-trader_found": "存在しないトレーダー %s がSPTから検出されました。エラーが発生する可能性があります。これはトレーダーを追加するModを導入したあと、該当するトレーダーからのメッセージを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT_Data\\Server\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ壊れたメッセージを削除してくれます",
|
||||
"fixer-clothing_item_found": "存在しない衣装アイテム %s がSPTから検出されました。エラーが発生する可能性があります。これは衣装アイテムを追加するModを導入したあと、該当するアイテムを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT\\SPT_Data\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ存在しない衣装アイテムを削除してくれます",
|
||||
"fixer-mod_item_found": "アイテムDBに存在しないアイテム %s がSPTから見つかりました。エラーが発生する可能性があります。これはアイテムを追加するModを導入したあと、該当するアイテムを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT\\SPT_Data\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ存在しないアイテムを削除してくれます",
|
||||
"fixer-trader_found": "存在しないトレーダー %s がSPTから検出されました。エラーが発生する可能性があります。これはトレーダーを追加するModを導入したあと、該当するトレーダーからのメッセージを削除せずにModを削除したことが原因である可能性があります。このプロファイルは使用しないでください!SPT\\SPT_Data\\configs\\core.json を開き、 removeModItemsFromProfile をtrueにしてください。これにより、サーバーがあなたのプロファイルを自動的に編集し、運が良ければ壊れたメッセージを削除してくれます",
|
||||
"fixer-updated_pockets": "「ポケット」アイテムを新しい 18876 バージョンに更新し、x3 の特別なスロットを追加しました。",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - データベースからタイプ %s のbotが見つかりません。スキップします",
|
||||
"gameevent-no_gear_data": "イベント %s の seasonevents.json 構成に装備品データがありません",
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "해당 매물 id를 찾을 수 없음: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "%s 탄약에 관통력 수치값을 찾지 못하였습니다. 확인이 어려워 값이 리미트 이상이 아니라고 간주하였습니다",
|
||||
"fence-unable_to_find_root_item_to_add": "루트 아이템을 찾지 못해 펜스에게 아이템을 추가하지 못하였습니다.",
|
||||
"fixer-clothing_item_found": "SPT 에 존재하지 않는 의복 아이템 %s가 발견되었습니다. 의복 모드를 사용하여 커스텀 의복을 착용한 상태에서 해당 모드가 지워졌을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT_Data\\Server\\configs\\core.json 파일에서 필드값 removeModItemsFromProfile 을 true로 변경하십시오. 가능한 경우 서버가 자동으로 의복을 복구할 것입니다.",
|
||||
"fixer-mod_item_found": "데이터베이스에 없는 아이템 %s 가 발견되었습니다. 모드로 추가된 커스텀 아이템이 인벤토리에 있는 상태로 해당 모드를 삭제했을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT_Data\\Server\\configs\\core.json 파일을 열어 필드값 removeModItemsFromProfile 을 true로 수정해주십시오. 가능한 경우 서버가 자동으로 프로필을 복구할 것입니다.",
|
||||
"fixer-trader_found": "SPT에 존재하지않는 상인 %s가 프로필에서 발견되었습니다. 상인을 추가하는 모드를 사용한 뒤 해당 상인이 보낸 메시지를 삭제하지 않고 모드를 지웠을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT_Data\\Server\\configs\\core.json 을 열어 필드값removeModItemsFromProfile 을 true로 수정해주십시오. 가능한 경우 서버가 자동으로 프로필을 복구할 것입니다.",
|
||||
"fixer-clothing_item_found": "SPT 에 존재하지 않는 의복 아이템 %s가 발견되었습니다. 의복 모드를 사용하여 커스텀 의복을 착용한 상태에서 해당 모드가 지워졌을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT\\SPT_Data\\configs\\core.json 파일에서 필드값 removeModItemsFromProfile 을 true로 변경하십시오. 가능한 경우 서버가 자동으로 의복을 복구할 것입니다.",
|
||||
"fixer-mod_item_found": "데이터베이스에 없는 아이템 %s 가 발견되었습니다. 모드로 추가된 커스텀 아이템이 인벤토리에 있는 상태로 해당 모드를 삭제했을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT\\SPT_Data\\configs\\core.json 파일을 열어 필드값 removeModItemsFromProfile 을 true로 수정해주십시오. 가능한 경우 서버가 자동으로 프로필을 복구할 것입니다.",
|
||||
"fixer-trader_found": "SPT에 존재하지않는 상인 %s가 프로필에서 발견되었습니다. 상인을 추가하는 모드를 사용한 뒤 해당 상인이 보낸 메시지를 삭제하지 않고 모드를 지웠을 가능성이 높습니다. 해당 프로필로 접속하지 마시고 SPT\\SPT_Data\\configs\\core.json 을 열어 필드값removeModItemsFromProfile 을 true로 수정해주십시오. 가능한 경우 서버가 자동으로 프로필을 복구할 것입니다.",
|
||||
"fixer-updated_pockets": "'pocket' 아이템을 18876 게임 버전에 맞춰 스페셜 칸을 3개로 수정되었습니다",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - 봇 %s 타입을 데이터베이스에서 찾을 수 없습니다, 건너뜁니다",
|
||||
"gameevent-no_gear_data": "이벤트 %s 의 장비 정보가 seasonalevents.json 설정 파일에 없습니다",
|
||||
|
||||
@@ -92,9 +92,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "Kan aanbod met id: %s niet vinden",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Geen penetratie waarde gevonden voor munitie: %s, kan niet controleren of deze boven het penetratie limiet uitgaat, wordt aangenomen van niet",
|
||||
"fence-unable_to_find_root_item_to_add": "Niet in staat om items aan Fence toe te voegen omdat er geen fundamentele items gevonden zijn",
|
||||
"fixer-clothing_item_found": "Kleding voorwerp: %s gevonden in profiel dat niet bestaat in SPT. Je ZAL fouten tegenkomen, dit kan komen door het gebruik van een kleding mod en het verwijderen van de mod terwijl je karakter het nog steeds draagt. GEBRUIK DIT PROFIEL NIET. Open SPT_Data\\Server\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de missende kleding te verwijderen",
|
||||
"fixer-mod_item_found": "Voorwerp: %s gevonden in profiel dat niet bestaat in de 'items' database. Je ZAL fouten tegenkomen, dat kan komen door het gebruik van een voorwerpen mod en het verwijderen van de mod zonder de gemodificeerde items van de mod te verwijderen uit je inventaris. GEBRUIK DIT PROFIEL NIET. Open Open SPT_Data\\Server\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de foute voorwerpen te verwijderen",
|
||||
"fixer-trader_found": "Trader: %s gevonden in profiel maar bestaat niet in SPT. Je ZAL fouten tegenkomen, dat kan komen door het gebruik van een trader mod en het verwijderen van de mod zonder de berichten van de desbetreffende trader te verwijderen. GEBRUIK DIT PROFIEL NIET. Open Open SPT_Data\\Server\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de foute berichten te verwijderen",
|
||||
"fixer-clothing_item_found": "Kleding voorwerp: %s gevonden in profiel dat niet bestaat in SPT. Je ZAL fouten tegenkomen, dit kan komen door het gebruik van een kleding mod en het verwijderen van de mod terwijl je karakter het nog steeds draagt. GEBRUIK DIT PROFIEL NIET. Open SPT\\SPT_Data\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de missende kleding te verwijderen",
|
||||
"fixer-mod_item_found": "Voorwerp: %s gevonden in profiel dat niet bestaat in de 'items' database. Je ZAL fouten tegenkomen, dat kan komen door het gebruik van een voorwerpen mod en het verwijderen van de mod zonder de gemodificeerde items van de mod te verwijderen uit je inventaris. GEBRUIK DIT PROFIEL NIET. Open Open SPT\\SPT_Data\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de foute voorwerpen te verwijderen",
|
||||
"fixer-trader_found": "Trader: %s gevonden in profiel maar bestaat niet in SPT. Je ZAL fouten tegenkomen, dat kan komen door het gebruik van een trader mod en het verwijderen van de mod zonder de berichten van de desbetreffende trader te verwijderen. GEBRUIK DIT PROFIEL NIET. Open Open SPT\\SPT_Data\\configs\\core.json, wijzig 'removeModItemsFromProfile' van 'false' naar 'true'. Dit maakt het de server mogelijk om jouw profiel te wijzigen en hopelijk de foute berichten te verwijderen",
|
||||
"fixer-updated_pockets": "Het onderdeel 'pocket' geupdate naar nieuwe versie '18876' met 3x speciale plekken",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - kan geen bot van type %s vinden in database, wordt overgeslagen",
|
||||
"gameevent-no_gear_data": "Geen data voor uitrustingen in seasonalevents.json instellingen voor evenement %s",
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "Não foi possível encontrar a oferta com o ID: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Nenhum valor de penetração encontrado para a munição: %s. Incapaz de verificar se está acima do limite de penetração, assumindo falso",
|
||||
"fence-unable_to_find_root_item_to_add": "Não foi possível adicionar itens ao Fence, pois nenhum item raiz foi encontrado",
|
||||
"fixer-clothing_item_found": "Item de vestuário: %s encontrado no perfil que não existe no SPT. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de vestuário e remoção do mod enquanto seu personagem ainda estava usando. NÃO USE ESTE PERFIL. Abra SPT_Data\\Server\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova a vestimenta faltante",
|
||||
"fixer-mod_item_found": "Item: %s encontrado no perfil que não existe no banco de dados de itens. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de itens e remoção do mod sem excluir os itens modificados do seu inventário. NÃO USE ESTE PERFIL. Abra SPT_Data\\Server\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova os itens problemáticos",
|
||||
"fixer-trader_found": "Comerciante: %s encontrado no perfil que não existe no SPT. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de comerciante e remoção do mod sem excluir as mensagens desse comerciante. NÃO USE ESTE PERFIL. Abra SPT_Data\\Server\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova as mensagens problemáticas",
|
||||
"fixer-clothing_item_found": "Item de vestuário: %s encontrado no perfil que não existe no SPT. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de vestuário e remoção do mod enquanto seu personagem ainda estava usando. NÃO USE ESTE PERFIL. Abra SPT\\SPT_Data\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova a vestimenta faltante",
|
||||
"fixer-mod_item_found": "Item: %s encontrado no perfil que não existe no banco de dados de itens. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de itens e remoção do mod sem excluir os itens modificados do seu inventário. NÃO USE ESTE PERFIL. Abra SPT\\SPT_Data\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova os itens problemáticos",
|
||||
"fixer-trader_found": "Comerciante: %s encontrado no perfil que não existe no SPT. Você TERÁ problemas, isso pode ocorrer devido ao uso de um mod de comerciante e remoção do mod sem excluir as mensagens desse comerciante. NÃO USE ESTE PERFIL. Abra SPT\\SPT_Data\\configs\\core.json, edite 'removeModItemsFromProfile' para true. Isso permitirá que o servidor edite seu perfil e, com sorte, remova as mensagens problemáticas",
|
||||
"fixer-updated_pockets": "Atualizado o item 'bolso' para a nova versão 18876 com x3 slots especiais",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - não foi possível encontrar o bot do tipo %s no banco de dados, ignorando",
|
||||
"gameevent-no_gear_data": "Sem dados de equipamento no arquivo de configuração seasonalevents.json para o evento %s",
|
||||
|
||||
@@ -93,8 +93,8 @@
|
||||
"fence-unable_to_get_ammo_penetration_value": "Не найдено значение проникновения для Патронов: %s, Невозможно проверить превышение лимита проникновения, выставляем значение \"нет\"",
|
||||
"fence-unable_to_find_root_item_to_add": "Не удается добавить предметы Скупщику, так как корневые предметы не были найдены",
|
||||
"fixer-clothing_item_found": "В инвентаре найден предмет %s который не существует в базе данных, вы скорее всего столкнётесь с ошибками. Это могло произойти из-за использования мода, который добавляет новые предметы, и его последующего удаления, при этом не удаляя предметы из данного мода из своего инвентаря. Не используйте этот профиль",
|
||||
"fixer-mod_item_found": "Предмет: %s найден в профиле но отсутствует в базе данных. У вас будут ошибки, и это связано с использованием предмета из мода после удаления этого мода без удаления модовых предметов из вашего инвентаря. НЕ ПОЛЬЗУЙТЕСЬ ЭТИМ ПРОФИЛЕМ. Откройте SPT_Data\\Server\\configs\\core.json, измените значение 'removeModItemsFromProfile' на true. Это даст возможность серверу изменять ваш профиль и возможно он автоматически удалит этот предмет",
|
||||
"fixer-trader_found": "В профиле был обнаружен торговец: %s, которого не существует в SPT. Это может быть связано с использованием модификации, добавляющей нового торговца и которая была удалена без предварительного удаления сообщений от указанного торговца, и ОБЯЗАТЕЛЬНО приведет к ошибкам. НЕ ИСПОЛЬЗУЙТЕ ДАННЫЙ ПРОФИЛЬ. Откройте файл SPT_Data\\Server\\configs\\core.json и измените значение параметра 'removeModItemsFromProfile' на true. Это позволит серверу отредактировать ваш профиль и, по возможности, удалить проблемные сообщения",
|
||||
"fixer-mod_item_found": "Предмет: %s найден в профиле но отсутствует в базе данных. У вас будут ошибки, и это связано с использованием предмета из мода после удаления этого мода без удаления модовых предметов из вашего инвентаря. НЕ ПОЛЬЗУЙТЕСЬ ЭТИМ ПРОФИЛЕМ. Откройте SPT\\SPT_Data\\configs\\core.json, измените значение 'removeModItemsFromProfile' на true. Это даст возможность серверу изменять ваш профиль и возможно он автоматически удалит этот предмет",
|
||||
"fixer-trader_found": "В профиле был обнаружен торговец: %s, которого не существует в SPT. Это может быть связано с использованием модификации, добавляющей нового торговца и которая была удалена без предварительного удаления сообщений от указанного торговца, и ОБЯЗАТЕЛЬНО приведет к ошибкам. НЕ ИСПОЛЬЗУЙТЕ ДАННЫЙ ПРОФИЛЬ. Откройте файл SPT\\SPT_Data\\configs\\core.json и измените значение параметра 'removeModItemsFromProfile' на true. Это позволит серверу отредактировать ваш профиль и, по возможности, удалить проблемные сообщения",
|
||||
"fixer-updated_pockets": "Изменили предмет 'карман' на новую версию с тремя спец. слотами из обновления 18876",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - не смогли обнаружить бота типа %s в базе данных, пропускаем",
|
||||
"gameevent-no_gear_data": "Не смогли обнаружить информацию о снаряжении в конфиге seasonalevents.json для события %s",
|
||||
|
||||
@@ -92,9 +92,9 @@
|
||||
"fence-unable_to_find_offer_by_id": "id: %s ile teklif bulunamıyor",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Cephane: %s için penetrasyon değeri bulunamadı, penetrasyon sınırının üzerinde olup olmadığı kontrol edilemiyor, yanlış varsayılıyor",
|
||||
"fence-unable_to_find_root_item_to_add": "Kök öğe bulunamadığı için Fence'e öğe eklenemiyor",
|
||||
"fixer-clothing_item_found": "Giyim eşyası: SPT'de mevcut olmayan %s profilinde bulundu. Hatalar yaşayacaksınız, bunun nedeni bir kıyafet modu kullanmak ve karakteriniz hala giyerken modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT_Data\\Server\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım eksik kıyafetleri kaldırmasına izin verecektir",
|
||||
"fixer-mod_item_found": "Eşya: %s, eşya veritabanında bulunmayan profilde bulundu. Hatalarla karşılaşacaksınız, bunun nedeni bir eşya modu kullanmak ve modlanmış eşyaları envanterinizden silmeden modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT_Data\\Server\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım kötü öğeleri kaldırmasına izin verecektir",
|
||||
"fixer-trader_found": "Tüccar: SPT'de mevcut olmayan profilde %s bulundu. Hatalar yaşayacaksınız, bunun nedeni bir tüccar modu kullanmak ve söz konusu tüccardan gelen mesajları silmeden modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT_Data\\Server\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım kötü mesajları kaldırmasına izin verecektir",
|
||||
"fixer-clothing_item_found": "Giyim eşyası: SPT'de mevcut olmayan %s profilinde bulundu. Hatalar yaşayacaksınız, bunun nedeni bir kıyafet modu kullanmak ve karakteriniz hala giyerken modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT\\SPT_Data\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım eksik kıyafetleri kaldırmasına izin verecektir",
|
||||
"fixer-mod_item_found": "Eşya: %s, eşya veritabanında bulunmayan profilde bulundu. Hatalarla karşılaşacaksınız, bunun nedeni bir eşya modu kullanmak ve modlanmış eşyaları envanterinizden silmeden modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT\\SPT_Data\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım kötü öğeleri kaldırmasına izin verecektir",
|
||||
"fixer-trader_found": "Tüccar: SPT'de mevcut olmayan profilde %s bulundu. Hatalar yaşayacaksınız, bunun nedeni bir tüccar modu kullanmak ve söz konusu tüccardan gelen mesajları silmeden modu kaldırmak olabilir. BU PROFILI KULLANMAYIN. SPT\\SPT_Data\\configs\\core.json dosyasını açın, 'removeModItemsFromProfile' seçeneğini true olacak şekilde düzenleyin. Bu, sunucunun profilinizi düzenlemesine ve umarım kötü mesajları kaldırmasına izin verecektir",
|
||||
"fixer-updated_pockets": "'Cep' öğesi x3 özel yuvalı yeni 18876 sürümüne güncellendi",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - veritabanında %s türünde bot bulunamıyor, atlanıyor",
|
||||
"gameevent-no_gear_data": "Seasonalevents.json yapılandırmasında %s etkinliği için dişli verisi yok",
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
"fence-unable_to_find_assort_by_id": "Не вдалось знайти предмет в купця з ідентифікатором: %s",
|
||||
"fence-unable_to_find_offer_by_id": "Не вдалося пропозиції торговця з ідентифікатором: %s",
|
||||
"fence-unable_to_get_ammo_penetration_value": "Не вдалося знайти значення пробиття для набою %s, неможливо перевірити перевищення ліміту пробиття, використовую false",
|
||||
"fixer-clothing_item_found": "Предмет одягу: %s який не існує в SPT, знайдено в профілі. Це може бути пов'язано з використанням моду на одяг та видаленням моду, без зняття цього одягу з вашого персонажу. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT_Data\\Server\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить цей одяг автоматично",
|
||||
"fixer-mod_item_found": "Предмет: %s, якого не існує в базі даних, знайдемо в профілі. Це може бути пов'язано з використанням моду на предмети та видаленням цього моду без видалення предметів з інвентарю. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT_Data\\Server\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить ці предмети автоматично",
|
||||
"fixer-trader_found": "Торговець: %s який не існує в SPT, знайдено в профілі. Це може бути пов'язано з використанням моду на трейдера та видаленням моду без видалення повідомлень від цього трейдера. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT_Data\\Server\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить ці повідомлення автоматично",
|
||||
"fixer-clothing_item_found": "Предмет одягу: %s який не існує в SPT, знайдено в профілі. Це може бути пов'язано з використанням моду на одяг та видаленням моду, без зняття цього одягу з вашого персонажу. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT\\SPT_Data\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить цей одяг автоматично",
|
||||
"fixer-mod_item_found": "Предмет: %s, якого не існує в базі даних, знайдемо в профілі. Це може бути пов'язано з використанням моду на предмети та видаленням цього моду без видалення предметів з інвентарю. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT\\SPT_Data\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить ці предмети автоматично",
|
||||
"fixer-trader_found": "Торговець: %s який не існує в SPT, знайдено в профілі. Це може бути пов'язано з використанням моду на трейдера та видаленням моду без видалення повідомлень від цього трейдера. НЕ ВИКОРИСТОВУЙТЕ ЦЕЙ ПРОФІЛЬ. Відкрийте SPT\\SPT_Data\\configs\\core.json, відредагуйте значення 'removeModItemsFromProfile' на true. Це дозволить серверу редагувати ваш профіль і можливо, він видалить ці повідомлення автоматично",
|
||||
"fixer-updated_pockets": "Оновлено предмет \"кишені\" до нової версії 18876 з x3 слотами",
|
||||
"gameevent-bot_not_found": "addEventGearToScavs() - не вдалося знайти бота типу %s в базі даних, пропускаю",
|
||||
"gameevent-no_gear_data": "Немає даних спорядження у конфігурації seasonalevents.json для події %s",
|
||||
|
||||
@@ -62,6 +62,14 @@
|
||||
"chat-unable_to_register_command_already_registered": "无法注册已注册命令: %s",
|
||||
"client_request": "[客户端请求] %s",
|
||||
"client_request_ip": "[客户端请求] {{ip}}{{url}}",
|
||||
"websocket_request": "[WebSocket 请求] %s",
|
||||
"websocket_request_ip": "[WebSocket 请求] {{ip}} {{url}}",
|
||||
"custom-quest-service_quest_id_already_exists": "使用 id:{{questId}} 的任务已经存在。 ",
|
||||
"custom-quest-service_no_languages_for_quest": "Id 为 {{questId}} 的自定义任务没有加载任何语言",
|
||||
"custom-quest-service_no_entries_for_language": "语言键 {{languageKey}} 尚未添加任何本地化条目,这是有意为之吗?",
|
||||
"custom-quest-service_could_not_find_language_key": "在添加自定义任务时在全局本地化文件中无法找到对应的语言键:{{languageKey}}。可能是打错字了或者是游戏不支持该语言。",
|
||||
"custom-quest-service_locale_data_null": "语言 {{languageKey}} 本地化数据为空",
|
||||
"custom-quest-service_invalid_side": "任务 id:{{questId}} 中,由于阵营锁定,Savage 并非有效的阵营。",
|
||||
"customisation-item_already_purchased": "服装物品{{itemId}} {{itemName}}已购买",
|
||||
"customisation-suit_lacks_upd_or_stack_property": "服装 tpl: %s 缺少一个upd对象 或stackobjectcount属性",
|
||||
"customisation-unable_to_find_clothing_item_in_inventory": "物品栏中找不到id:%s的服装物品",
|
||||
@@ -215,6 +223,8 @@
|
||||
"modloader-main_property_not_js": "模组 %s package.json的main属性必须时一个.js文件",
|
||||
"modloader-main_property_points_to_nothing": "模组 %s package.json的main属性指向了一个不存在的文件",
|
||||
"modloader-missing_dependency": "模组 {{mod}} 需要安装 {{modDependency}}。",
|
||||
"modloader-self_dependency": "模组 {{mod}} 依赖项为自身,请将其从依赖项列表中移除",
|
||||
"modloader-self_incompatibility": "模组 {{mod}} 与自身冲突,请将其从冲突列表中移除",
|
||||
"modloader-missing_package_json": "模组(%s)缺少package.json",
|
||||
"modloader-missing_package_json_property": "模组{{modName}} package.json需要{{prop}}属性",
|
||||
"modloader-missing_sptversion_field": "模组 %s 缺少sptVersion字段,很可能由于版本过期且不兼容当前版本的SPT",
|
||||
@@ -652,8 +662,10 @@
|
||||
"repair-unable_to_find_item_repair_cost": "无法找到物品:%s的修理费用",
|
||||
"repair-unable_to_find_trader_details_by_id": "无法找到商人: %s 的修理数据",
|
||||
"repeatable-accepted_repeatable_quest_not_found_in_active_quests": "接受了activeQuests数组中找不到的重复任务:%s。请报告这个bug",
|
||||
"repeatable-completion_config_no_template": "无法找到 PMC 等级为 {{pmcLevel}} 级的完成配置",
|
||||
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive": "生成完成任务:没有剩下物品。白名单太小或黑名单限制太大",
|
||||
"repeatable-difficulty_was_nan": "重复奖励生成:难度是NaN。正在设置成1。",
|
||||
"repeatable-exploration_config_no_template": "无法找到 PMC 等级为 {{pmcLevel}} 级的探索配置",
|
||||
"repeatable-no_reward_item_found_in_price_range": "重复奖励生成:价格区间{{minPrice}}至{{roublesBudget}}中未找到物品",
|
||||
"repeatable-quest_handover_failed_condition_already_satisfied": "任务提交错误:条件已经满足?任务id:{{questId}},条件:{{conditionId}}, profileCounter:{{profileCounter}}, value:{{value}}",
|
||||
"repeatable-quest_handover_failed_condition_invalid": "任务提交错误:条件未找到或者值不正确。qid(任务):{{body.qid}},条件:{{body.conditionId}}",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
{
|
||||
"Chance": 50,
|
||||
"ChancePVE": 50,
|
||||
"Count": 40000,
|
||||
"Count": 5000,
|
||||
"CountPVE": 0,
|
||||
"EntryPoints": "Customs,Boiler Tanks",
|
||||
"EventAvailable": false,
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
],
|
||||
"BossLocationSpawn": [
|
||||
{
|
||||
"BossChance": 30,
|
||||
"BossChance": 0,
|
||||
"BossDifficult": "normal",
|
||||
"BossEscortAmount": "2",
|
||||
"BossEscortDifficult": "normal",
|
||||
|
||||
+6
-6
@@ -274,7 +274,7 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_Name": "Entrance to Catacombs"
|
||||
"SptName": "Entrance to Catacombs"
|
||||
},
|
||||
{
|
||||
"Chance": 100,
|
||||
@@ -298,7 +298,7 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_Name": "Ventilation Shaft"
|
||||
"SptName": "Ventilation Shaft"
|
||||
},
|
||||
{
|
||||
"Chance": 100,
|
||||
@@ -322,7 +322,7 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_Name": "Sewer Manhole"
|
||||
"SptName": "Sewer Manhole"
|
||||
},
|
||||
{
|
||||
"Chance": 100,
|
||||
@@ -346,7 +346,7 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_Name": "Near Kamchatskaya Arch"
|
||||
"SptName": "Near Kamchatskaya Arch"
|
||||
},
|
||||
{
|
||||
"Chance": 100,
|
||||
@@ -370,7 +370,7 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_name": "Cardinal Apartment Complex Parking"
|
||||
"SptName": "Cardinal Apartment Complex Parking"
|
||||
},
|
||||
{
|
||||
"Chance": 100,
|
||||
@@ -394,6 +394,6 @@
|
||||
"RequiredSlot": "FirstPrimaryWeapon",
|
||||
"RequirementTip": "",
|
||||
"Side": "Scav",
|
||||
"_name": "Klimov Shopping Mall Exfil"
|
||||
"SptName": "Klimov Shopping Mall Exfil"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c2d192b6d0abfaefe8dbc29a0fc948149de8f2b16ca611770c77fc1d817217db
|
||||
size 19064342
|
||||
oid sha256:5312d5454b1702f8ad1495d7635e07cec2b70941d46e819f236c5eb084bbe5aa
|
||||
size 19115860
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -16,6 +16,7 @@ public class GameCallbacks(
|
||||
HttpResponseUtil httpResponseUtil,
|
||||
Watermark watermark,
|
||||
SaveServer saveServer,
|
||||
BackupService backupService,
|
||||
GameController gameController,
|
||||
ProfileActivityService profileActivityService,
|
||||
TimeUtil timeUtil
|
||||
@@ -66,6 +67,10 @@ public class GameCallbacks(
|
||||
public async ValueTask<string> GameLogout(string url, EmptyRequestData _, MongoId sessionID)
|
||||
{
|
||||
await saveServer.SaveProfileAsync(sessionID);
|
||||
|
||||
// Backup profiles on exit
|
||||
await backupService.Init();
|
||||
|
||||
return httpResponseUtil.GetBody(new GameLogoutResponseData { Status = "ok" });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Controllers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
|
||||
namespace SPTarkov.Server.Core.Callbacks;
|
||||
|
||||
[Injectable]
|
||||
public class ModdedTraderCustomizationCallbacks(
|
||||
ModdedTraderCustomizationController moddedTraderCustomizationController,
|
||||
HttpResponseUtil httpResponseUtil
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle /singleplayer/moddedTraders
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ValueTask<string> GetCustomizationTraders(string url, EmptyRequestData _, MongoId sessionID)
|
||||
{
|
||||
return new ValueTask<string>(httpResponseUtil.NoBody(moddedTraderCustomizationController.GetCustomizationSellerIds()));
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ public class RagfairCallbacks(
|
||||
|
||||
public Task OnLoad()
|
||||
{
|
||||
ragfairServer.Load();
|
||||
ragfairPriceService.Load();
|
||||
ragfairServer.Load();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ public class SaveCallbacks(SaveServer saveServer, ConfigServer configServer, Bac
|
||||
|
||||
public async Task OnLoad()
|
||||
{
|
||||
await backupService.StartBackupSystem();
|
||||
await saveServer.LoadAsync();
|
||||
|
||||
// Note: This has to happen after loading the saveServer so we don't backup corrupted profiles
|
||||
await backupService.StartBackupSystem();
|
||||
}
|
||||
|
||||
public async Task<bool> OnUpdate(long secondsSinceLastRun)
|
||||
|
||||
@@ -469,7 +469,6 @@ public class DialogueController(
|
||||
foreach (var dialogId in dialogueIds)
|
||||
{
|
||||
dialogs[dialogId].New = 0;
|
||||
dialogs[dialogId].AttachmentsNew = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,9 +490,6 @@ public class DialogueController(
|
||||
return null;
|
||||
}
|
||||
|
||||
// Removes corner 'new messages' tag
|
||||
dialogInfo!.AttachmentsNew = 0;
|
||||
|
||||
var activeMessages = GetActiveMessagesFromDialog(sessionId, dialogueId);
|
||||
var messagesWithAttachments = GetMessageWithAttachments(activeMessages);
|
||||
|
||||
|
||||
@@ -1175,17 +1175,23 @@ public class HideoutController(
|
||||
/// <param name="finishEffect">Effect data to apply after completing QTE gym event</param>
|
||||
protected void HandleMusclePain(PmcData pmcData, QteResult finishEffect)
|
||||
{
|
||||
var hasMildPain = pmcData.Health.BodyParts["Chest"].Effects?.ContainsKey("MildMusclePain");
|
||||
var hasSeverePain = pmcData.Health.BodyParts["Chest"].Effects?.ContainsKey("SevereMusclePain");
|
||||
if (!pmcData.Health.BodyParts.TryGetValue("Chest", out var chest))
|
||||
{
|
||||
logger.Error($"Unable to apply muscle pain effect to player: {pmcData.Id.ToString}. They lack a chest");
|
||||
|
||||
return;
|
||||
}
|
||||
var hasMildPain = chest.Effects?.ContainsKey("MildMusclePain");
|
||||
var hasSeverePain = chest.Effects?.ContainsKey("SevereMusclePain");
|
||||
|
||||
// Has no muscle pain at all, add mild
|
||||
if (!hasMildPain.GetValueOrDefault(false) && !hasSeverePain.GetValueOrDefault(false))
|
||||
{
|
||||
// Nullguard
|
||||
pmcData.Health.BodyParts["Chest"].Effects ??= new();
|
||||
pmcData.Health.BodyParts["Chest"].Effects["MildMusclePain"] = new BodyPartEffectProperties
|
||||
// Create effects as it may not exist
|
||||
chest.Effects ??= [];
|
||||
chest.Effects["MildMusclePain"] = new BodyPartEffectProperties
|
||||
{
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time, // TODO - remove hard coded access, get value properly
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault()?.Time, // TODO - remove hard coded access, get value properly
|
||||
};
|
||||
|
||||
return;
|
||||
@@ -1194,12 +1200,9 @@ public class HideoutController(
|
||||
if (hasMildPain.GetValueOrDefault(false))
|
||||
{
|
||||
// Already has mild pain, remove mild and add severe
|
||||
pmcData.Health.BodyParts["Chest"].Effects.Remove("MildMusclePain");
|
||||
chest.Effects.Remove("MildMusclePain");
|
||||
|
||||
pmcData.Health.BodyParts["Chest"].Effects["SevereMusclePain"] = new BodyPartEffectProperties
|
||||
{
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time,
|
||||
};
|
||||
chest.Effects["SevereMusclePain"] = new BodyPartEffectProperties { Time = finishEffect.RewardEffects.FirstOrDefault()?.Time };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ public class InsuranceController(
|
||||
if (parentAttachmentsMap.ContainsKey(insuredItem.Id))
|
||||
{
|
||||
// This call will also return the parent item itself, queueing it for deletion as well.
|
||||
var itemAndChildren = insured.Items.GetItemWithChildren(insuredItem.Id);
|
||||
var itemAndChildren = insured.Items.GetItemWithChildren(insuredItem.Id, true);
|
||||
foreach (var item in itemAndChildren)
|
||||
{
|
||||
toDelete.Add(item.Id);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
using SPTarkov.Server.Core.Services;
|
||||
|
||||
namespace SPTarkov.Server.Core.Controllers;
|
||||
|
||||
[Injectable]
|
||||
public class ModdedTraderCustomizationController(DatabaseService databaseService)
|
||||
{
|
||||
public ModdedTraderListResponse GetCustomizationSellerIds()
|
||||
{
|
||||
var traders = databaseService.GetTraders();
|
||||
var customizationSellers = new ModdedTraderListResponse { ModdedTraders = [] };
|
||||
|
||||
foreach (var trader in traders)
|
||||
{
|
||||
if (trader.Value.Base.CustomizationSeller!.Value && trader.Key != Traders.RAGMAN)
|
||||
{
|
||||
customizationSellers.ModdedTraders.Add(trader.Key);
|
||||
}
|
||||
}
|
||||
return customizationSellers;
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class RagfairController(
|
||||
var traderAssorts = ragfairHelper.GetDisplayableAssorts(sessionID);
|
||||
var result = new GetOffersResult
|
||||
{
|
||||
Offers = [],
|
||||
Offers = null,
|
||||
OffersCount = searchRequest.Limit,
|
||||
SelectedCategory = searchRequest.HandbookId,
|
||||
};
|
||||
|
||||
@@ -137,7 +137,7 @@ public class RepeatableQuestController(
|
||||
var repeatableConfig = QuestConfig.RepeatableQuests.FirstOrDefault(config => config.Name == repeatablesOfTypeInProfile.Name);
|
||||
|
||||
// If the configuration dictates to replace with the same quest type, adjust the available quest types
|
||||
if (repeatableConfig?.KeepDailyQuestTypeOnReplacement is not null)
|
||||
if (repeatableConfig?.KeepDailyQuestTypeOnReplacement is not null && repeatableConfig.KeepDailyQuestTypeOnReplacement)
|
||||
{
|
||||
repeatableConfig.Types = [questToReplace.Type.ToString()];
|
||||
}
|
||||
|
||||
@@ -254,19 +254,6 @@ public static class ProfileExtensions
|
||||
return quest?.Status ?? QuestStatusEnum.Locked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use values from the profiles template to reset all body part max values
|
||||
/// </summary>
|
||||
/// <param name="profile">Profile to update</param>
|
||||
/// <param name="profileTemplate">Template used to create profile</param>
|
||||
public static void ResetMaxLimbHp(this PmcData profile, TemplateSide profileTemplate)
|
||||
{
|
||||
foreach (var (partKey, bodyPart) in profile.Health.BodyParts)
|
||||
{
|
||||
bodyPart.Health.Maximum = profileTemplate.Character.Health.BodyParts[partKey].Health.Maximum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle Remove event
|
||||
/// Remove item from player inventory + insured items array
|
||||
|
||||
@@ -171,7 +171,7 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomize the HpResource for bots e.g (245/400 resources)
|
||||
/// Choose a random value between a min and max for a resource to be
|
||||
/// </summary>
|
||||
/// <param name="maxResource">Max resource value of medical items</param>
|
||||
/// <param name="randomizationValues">Value provided from config</param>
|
||||
@@ -188,12 +188,11 @@ public class BotGeneratorHelper(
|
||||
return 1;
|
||||
}
|
||||
|
||||
var min = randomUtil.GetPercentOfValue(randomizationValues.ResourcePercent, maxResource, 0);
|
||||
// Generate a randomised min value the resource could have
|
||||
var min = Math.Max(1, randomUtil.GetPercentOfValue(randomizationValues.ResourcePercent, maxResource, 0));
|
||||
|
||||
// Using food at 0 causes client to error
|
||||
var clampedMin = Math.Clamp(min, 1, min);
|
||||
|
||||
return randomUtil.GetDouble(clampedMin, maxResource);
|
||||
// Choose value from randomised min and resource max possible
|
||||
return randomUtil.GetDouble(min, maxResource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,14 +50,13 @@ public class DialogueHelper(ISptLogger<DialogueHelper> logger, ProfileHelper pro
|
||||
public List<Item>? GetMessageItemContents(MongoId messageId, MongoId sessionId, MongoId itemId)
|
||||
{
|
||||
var fullProfile = profileHelper.GetFullProfile(sessionId);
|
||||
var dialogueData = fullProfile.DialogueRecords;
|
||||
if (dialogueData is null)
|
||||
if (fullProfile.DialogueRecords is null)
|
||||
{
|
||||
logger.Error("DialogueData is null when trying to get message item contents");
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach (var (dialogId, dialog) in dialogueData)
|
||||
foreach (var (dialogId, dialog) in fullProfile.DialogueRecords)
|
||||
{
|
||||
var message = dialog.Messages?.FirstOrDefault(x => x.Id == messageId);
|
||||
if (message is null)
|
||||
@@ -65,32 +64,21 @@ public class DialogueHelper(ISptLogger<DialogueHelper> logger, ProfileHelper pro
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message.Id != messageId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fullProfile.DialogueRecords is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var attachmentsNew = fullProfile.DialogueRecords[dialogId].AttachmentsNew;
|
||||
if (attachmentsNew > 0)
|
||||
{
|
||||
fullProfile.DialogueRecords[dialogId].AttachmentsNew = attachmentsNew - 1;
|
||||
}
|
||||
|
||||
// Check reward count when item being moved isn't in reward list
|
||||
// If count is 0, it means after this move occurs the reward array will be empty and all rewards collected
|
||||
message.Items ??= new MessageItems();
|
||||
message.Items.Data ??= [];
|
||||
|
||||
var messageItems = message.Items.Data?.Where(x => x.Id != itemId);
|
||||
if (messageItems is null || !messageItems.Any())
|
||||
// Check reward count when item being moved isn't in reward list
|
||||
// If count is 0, it means after this move occurs the reward array will be empty and all rewards collected
|
||||
var remainingItems = message.Items.Data.Where(x => x.Id != itemId);
|
||||
if (!remainingItems.Any())
|
||||
{
|
||||
message.RewardCollected = true;
|
||||
message.HasRewards = false;
|
||||
|
||||
if (dialog.AttachmentsNew > 0)
|
||||
{
|
||||
dialog.AttachmentsNew--;
|
||||
}
|
||||
}
|
||||
|
||||
return message.Items.Data;
|
||||
|
||||
@@ -3,6 +3,7 @@ using SPTarkov.Server.Core.Exceptions.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Eft.Health;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
using SPTarkov.Server.Core.Models.Utils;
|
||||
using SPTarkov.Server.Core.Servers;
|
||||
@@ -23,7 +24,7 @@ public class HealthHelper(ISptLogger<HealthHelper> logger, TimeUtil timeUtil, Co
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="pmcProfileToUpdate">Player profile to apply changes to</param>
|
||||
/// <param name="healthChanges">Changes to apply </param>
|
||||
public void ApplyHealthChangesToProfile(MongoId sessionId, PmcData pmcProfileToUpdate, BotBaseHealth healthChanges)
|
||||
public void ApplyHealthChangesToProfile(MongoId sessionId, PmcData pmcProfileToUpdate, BotBaseHealth healthChanges, bool isDead)
|
||||
{
|
||||
/* TODO: Not used here, need to check node or a live profile, commented out for now to avoid the potential alloc - Cj
|
||||
var fullProfile = saveServer.GetProfile(sessionId);
|
||||
@@ -41,8 +42,10 @@ public class HealthHelper(ISptLogger<HealthHelper> logger, TimeUtil timeUtil, Co
|
||||
throw new HealthHelperException(message);
|
||||
}
|
||||
|
||||
var playerWasCursed = !PlayerHadGearOnRaidStart(pmcProfileToUpdate.Inventory);
|
||||
|
||||
// Alter saved profiles Health with values from post-raid client data
|
||||
ModifyProfileHealthProperties(pmcProfileToUpdate, healthChanges.BodyParts, EffectsToSkip);
|
||||
ModifyProfileHealthProperties(pmcProfileToUpdate, healthChanges.BodyParts, EffectsToSkip, isDead, playerWasCursed);
|
||||
|
||||
// Adjust hydration/energy/temperature
|
||||
AdjustProfileHydrationEnergyTemperature(pmcProfileToUpdate, healthChanges);
|
||||
@@ -58,16 +61,57 @@ public class HealthHelper(ISptLogger<HealthHelper> logger, TimeUtil timeUtil, Co
|
||||
pmcProfileToUpdate.Health.UpdateTime = timeUtil.GetTimeStamp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Did the player start raid with gear, if false, they are 'cursed'
|
||||
/// </summary>
|
||||
/// <param name="inventory">Players inventory at start of raid</param>
|
||||
/// <returns>True = they had enough gear to not be classed as 'cursed'</returns>
|
||||
protected bool PlayerHadGearOnRaidStart(BotBaseInventory inventory)
|
||||
{
|
||||
if (inventory?.Items == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasWeapon = false;
|
||||
var hasVestRigOrBackpack = false;
|
||||
foreach (var item in inventory.Items)
|
||||
{
|
||||
// Possible early escape
|
||||
if (hasWeapon && hasVestRigOrBackpack)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.SlotId is "FirstPrimaryWeapon" or "SecondPrimaryWeapon" or "Holster")
|
||||
{
|
||||
hasWeapon = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.SlotId is "Backpack" or "ArmorVest" or "TacticalVest")
|
||||
{
|
||||
hasVestRigOrBackpack = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasWeapon && hasVestRigOrBackpack;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply Health values to profile
|
||||
/// </summary>
|
||||
/// <param name="profileToAdjust">Player profile on server</param>
|
||||
/// <param name="bodyPartChanges">Changes to apply</param>
|
||||
/// <param name="effectsToSkip"></param>
|
||||
/// <param name="isDead"></param>
|
||||
/// <param name="playerWasCursed">Did player enter raid with no equipment</param>
|
||||
protected void ModifyProfileHealthProperties(
|
||||
PmcData profileToAdjust,
|
||||
Dictionary<string, BodyPartHealth> bodyPartChanges,
|
||||
HashSet<string>? effectsToSkip = null
|
||||
HashSet<string>? effectsToSkip = null,
|
||||
bool isDead = false,
|
||||
bool playerWasCursed = false
|
||||
)
|
||||
{
|
||||
foreach (var (partName, partProperties) in bodyPartChanges)
|
||||
@@ -90,12 +134,25 @@ public class HealthHelper(ISptLogger<HealthHelper> logger, TimeUtil timeUtil, Co
|
||||
if (HealthConfig.Save.Health)
|
||||
{
|
||||
// Apply hp changes to profile
|
||||
matchingProfilePart.Health.Current =
|
||||
partProperties.Health.Current == 0
|
||||
? partProperties.Health.Maximum * HealthConfig.HealthMultipliers.Blacked
|
||||
: partProperties.Health.Current;
|
||||
if (!isDead)
|
||||
{
|
||||
// If the player isn't dead, restore blacked limbs with a penalty
|
||||
matchingProfilePart.Health.Current =
|
||||
partProperties.Health.Current == 0
|
||||
? matchingProfilePart.Health.Maximum * HealthConfig.HealthMultipliers.Blacked
|
||||
: partProperties.Health.Current;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the player died, set all limbs with a penalty
|
||||
matchingProfilePart.Health.Current = matchingProfilePart.Health.Maximum * HealthConfig.HealthMultipliers.Death;
|
||||
|
||||
matchingProfilePart.Health.Maximum = partProperties.Health.Maximum;
|
||||
// Cursed player, body part gets set to 1 on death
|
||||
if (playerWasCursed)
|
||||
{
|
||||
matchingProfilePart.Health.Current = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process each effect for each part
|
||||
|
||||
@@ -179,7 +179,7 @@ public class InRaidHelper(
|
||||
}
|
||||
|
||||
// Remove contents of fast panel
|
||||
pmcData.Inventory.FastPanel = new Dictionary<string, MongoId>();
|
||||
pmcData.Inventory.FastPanel = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -232,12 +232,13 @@ public class RagfairOfferHelper(
|
||||
var tieredFlea = RagfairConfig.TieredFlea;
|
||||
var tieredFleaLimitTypes = tieredFlea.UnlocksType;
|
||||
|
||||
foreach (var desiredItemTpl in searchRequest.BuildItems)
|
||||
// Clone offers when tiered flea enabled as we may modify the offer
|
||||
var buildItems = tieredFlea.Enabled
|
||||
? cloner.Clone(searchRequest.BuildItems.Keys.ToDictionary(key => key, ragfairOfferService.GetOffersOfType))
|
||||
: searchRequest.BuildItems.Keys.ToDictionary(key => key, ragfairOfferService.GetOffersOfType);
|
||||
|
||||
foreach (var (desiredItemTpl, matchingOffers) in buildItems)
|
||||
{
|
||||
// Clone offers when tiered flea enabled as we may modify the offer
|
||||
var matchingOffers = tieredFlea.Enabled
|
||||
? cloner.Clone(ragfairOfferService.GetOffersOfType(desiredItemTpl.Key))
|
||||
: ragfairOfferService.GetOffersOfType(desiredItemTpl.Key);
|
||||
if (matchingOffers is null)
|
||||
// No offers found for this item, skip
|
||||
{
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using SPTarkov.DI.Annotations;
|
||||
|
||||
namespace SPTarkov.Server.Core.Migration.Migrations;
|
||||
|
||||
[Injectable]
|
||||
public class BuyRestrictionMaxStringToInt : AbstractProfileMigration
|
||||
{
|
||||
public override string FromVersion
|
||||
{
|
||||
get { return "~4.0"; }
|
||||
}
|
||||
|
||||
public override string ToVersion
|
||||
{
|
||||
get { return "~4.0"; }
|
||||
}
|
||||
|
||||
public override string MigrationName
|
||||
{
|
||||
get { return "BuyRestrictionMaxStringToInt400"; }
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> PrerequisiteMigrations
|
||||
{
|
||||
get { return [typeof(ThreeElevenToFourZero)]; }
|
||||
}
|
||||
|
||||
public override bool CanMigrate(JsonObject profile, IEnumerable<IProfileMigration> previouslyRanMigrations)
|
||||
{
|
||||
if (profile?["characters"]?["pmc"]?["Inventory"]?["items"] is JsonArray items)
|
||||
{
|
||||
foreach (var itemNode in items)
|
||||
{
|
||||
if (itemNode is not JsonObject itemObj)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemObj["upd"] is JsonObject updObj)
|
||||
{
|
||||
if (updObj.TryGetPropertyValue("BuyRestrictionMax", out var buyRestrictionMaxNode))
|
||||
{
|
||||
if (buyRestrictionMaxNode is JsonValue value && value.TryGetValue(out string? _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override JsonObject? Migrate(JsonObject profile)
|
||||
{
|
||||
if (profile["characters"]?["pmc"]?["Inventory"]?["items"] is JsonArray items)
|
||||
{
|
||||
foreach (var itemNode in items)
|
||||
{
|
||||
if (itemNode is not JsonObject itemObj)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemObj["upd"] is JsonObject updObj && updObj.TryGetPropertyValue("BuyRestrictionMax", out var buyRestrictionMaxNode))
|
||||
{
|
||||
if (buyRestrictionMaxNode is JsonValue value && value.TryGetValue(out string? strValue))
|
||||
{
|
||||
if (int.TryParse(strValue, out var intValue))
|
||||
{
|
||||
updObj["BuyRestrictionMax"] = intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
updObj.Remove("BuyRestrictionMax");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.Migrate(profile);
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,10 @@ public record Location
|
||||
public StaticContainer? Statics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// All possible map extracts
|
||||
/// All possible map extracts extracted from client via modules patch
|
||||
/// </summary>
|
||||
[JsonPropertyName("allExtracts")]
|
||||
public IEnumerable<Exit> AllExtracts { get; set; }
|
||||
public IEnumerable<AllExtractsExit> AllExtracts { get; set; }
|
||||
}
|
||||
|
||||
public record StaticContainer
|
||||
|
||||
@@ -867,12 +867,6 @@ public record Exit
|
||||
[JsonPropertyName("Name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("_Name")]
|
||||
public string? _Name { get; set; }
|
||||
|
||||
[JsonPropertyName("_name")]
|
||||
public string? _NameLower { get; set; }
|
||||
|
||||
[JsonPropertyName("PassageRequirement")]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public RequirementState PassageRequirement { get; set; }
|
||||
@@ -890,6 +884,12 @@ public record Exit
|
||||
public string? Side { get; set; }
|
||||
}
|
||||
|
||||
public record AllExtractsExit : Exit
|
||||
{
|
||||
[JsonPropertyName("SptName")]
|
||||
public string? SptName { get; set; }
|
||||
}
|
||||
|
||||
public record MaxItemCountInLocation
|
||||
{
|
||||
[JsonPropertyName("TemplateId")]
|
||||
|
||||
@@ -13,6 +13,9 @@ public record BackupConfig : BaseConfig
|
||||
[JsonPropertyName("maxBackups")]
|
||||
public int MaxBackups { get; set; }
|
||||
|
||||
[JsonPropertyName("backupCooldown")]
|
||||
public int BackupCooldown { get; set; }
|
||||
|
||||
[JsonPropertyName("directory")]
|
||||
public string Directory { get; set; } = string.Empty;
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public record BotConfig : BaseConfig
|
||||
public required HashSet<string> BotRolesThatMustHaveUniqueName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bot roles that must have a unique name when generated vs other bots in raid
|
||||
/// Values relating to the weekly boss system - a boss is randomly picked each week to spawn at 100% of the time
|
||||
/// </summary>
|
||||
[JsonPropertyName("weeklyBoss")]
|
||||
public required WeeklyBossSettings WeeklyBoss { get; set; }
|
||||
|
||||
@@ -42,6 +42,50 @@ public record CoreConfig : BaseConfig
|
||||
[JsonPropertyName("features")]
|
||||
public required ServerFeatures Features { get; set; }
|
||||
|
||||
[JsonPropertyName("enableNoGCRegions")]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public required bool EnableNoGCRegions { get; set; }
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private int _noGCRegionMaxMemoryGB = 4;
|
||||
|
||||
[JsonPropertyName("noGCRegionMaxMemoryGB")]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public required int NoGCRegionMaxMemoryGB
|
||||
{
|
||||
get => _noGCRegionMaxMemoryGB;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Invalid value: {nameof(NoGCRegionMaxMemoryGB)}: {value}. Must be greater than zero."
|
||||
);
|
||||
}
|
||||
_noGCRegionMaxMemoryGB = value;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private int _noGCRegionMaxLOHMemoryGB = 3;
|
||||
|
||||
[JsonPropertyName("noGCRegionMaxLOHMemoryGB")]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public required int NoGCRegionMaxLOHMemoryGB
|
||||
{
|
||||
get => _noGCRegionMaxLOHMemoryGB;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Invalid value {nameof(NoGCRegionMaxLOHMemoryGB)}: {value}. Must be greater than zero."
|
||||
);
|
||||
}
|
||||
_noGCRegionMaxLOHMemoryGB = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commit hash build server was created from
|
||||
/// </summary>
|
||||
|
||||
@@ -18,6 +18,9 @@ public record HealthMultipliers
|
||||
{
|
||||
[JsonPropertyName("blacked")]
|
||||
public double Blacked { get; set; }
|
||||
|
||||
[JsonPropertyName("death")]
|
||||
public double Death { get; set; }
|
||||
}
|
||||
|
||||
public record HealthSave
|
||||
|
||||
@@ -189,15 +189,3 @@ public record DiscountOptions
|
||||
[JsonPropertyName("equipmentPresetMinMax")]
|
||||
public required MinMax<int> EquipmentPresetMinMax { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom trader data needed client side for things such as the clothing service
|
||||
/// </summary>
|
||||
public record ModdedTraders
|
||||
{
|
||||
/// <summary>
|
||||
/// Trader Ids to enable the clothing service for
|
||||
/// </summary>
|
||||
[JsonPropertyName("clothingService")]
|
||||
public List<string> ClothingService { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
|
||||
public record ModdedTraderListResponse
|
||||
{
|
||||
public List<string>? ModdedTraders { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Callbacks;
|
||||
using SPTarkov.Server.Core.DI;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
|
||||
namespace SPTarkov.Server.Core.Routers.Static;
|
||||
|
||||
[Injectable]
|
||||
public class ModdedTraderCustomizationRouter(JsonUtil jsonUtil, ModdedTraderCustomizationCallbacks moddedTraderCustomizationCallbacks)
|
||||
: StaticRouter(
|
||||
jsonUtil,
|
||||
[
|
||||
new RouteAction<EmptyRequestData>(
|
||||
"/singleplayer/moddedTraders",
|
||||
async (url, info, sessionID, output) =>
|
||||
await moddedTraderCustomizationCallbacks.GetCustomizationTraders(url, info, sessionID)
|
||||
),
|
||||
]
|
||||
) { }
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Frozen;
|
||||
using System.Text.Json;
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
@@ -75,12 +76,27 @@ public class ConfigServer
|
||||
if (acceptableFileExtensions.Contains(FileUtil.GetFileExtension(file)))
|
||||
{
|
||||
var type = GetConfigTypeByFilename(file);
|
||||
var deserializedContent = JsonUtil.DeserializeFromFile(file, type);
|
||||
if (type == null)
|
||||
{
|
||||
Logger.Error($"Config file: {file} has no associated ConfigTypes entry. Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
object? deserializedContent = null;
|
||||
try
|
||||
{
|
||||
deserializedContent = JsonUtil.DeserializeFromFile(file, type);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Logger.Error($"Config file: {file} failed to deserialize");
|
||||
throw new Exception($"Server will not run until the: {file} config error mentioned above is fixed", ex);
|
||||
}
|
||||
|
||||
if (deserializedContent == null)
|
||||
{
|
||||
Logger.Error($"Config file: {file} is corrupt. Use a site like: https://jsonlint.com to find the issue.");
|
||||
throw new Exception($"Server will not run until the: {file} config error mentioned above is fixed");
|
||||
throw new Exception($"Server will not run until the: {file} config error mentioned above is fixed");
|
||||
}
|
||||
|
||||
_configs[$"spt-{FileUtil.StripExtension(file)}"] = deserializedContent;
|
||||
@@ -88,9 +104,16 @@ public class ConfigServer
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetConfigTypeByFilename(string filename)
|
||||
private Type? GetConfigTypeByFilename(string filename)
|
||||
{
|
||||
var type = Enum.GetValues<ConfigTypes>().First(en => en.GetValue().Contains(FileUtil.StripExtension(filename)));
|
||||
Func<ConfigTypes, bool> filterMethod = (entry => entry.GetValue().Contains(FileUtil.StripExtension(filename)));
|
||||
|
||||
if (!Enum.GetValues<ConfigTypes>().Any(filterMethod))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = Enum.GetValues<ConfigTypes>().First(filterMethod);
|
||||
return type.GetConfigType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.DI;
|
||||
@@ -22,6 +23,7 @@ public class SaveServer(
|
||||
HashUtil hashUtil,
|
||||
ServerLocalisationService serverLocalisationService,
|
||||
ProfileValidatorService profileValidatorService,
|
||||
BackupService backupService,
|
||||
ISptLogger<SaveServer> logger,
|
||||
ConfigServer configServer
|
||||
)
|
||||
@@ -33,6 +35,7 @@ public class SaveServer(
|
||||
|
||||
protected readonly ConcurrentDictionary<MongoId, SptProfile> profiles = new();
|
||||
protected readonly ConcurrentDictionary<MongoId, string> saveMd5 = new();
|
||||
protected readonly ConcurrentDictionary<MongoId, SemaphoreSlim> saveLocks = new();
|
||||
|
||||
/// <summary>
|
||||
/// Add callback to occur prior to saving profile changes
|
||||
@@ -70,7 +73,12 @@ public class SaveServer(
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
foreach (var file in files)
|
||||
{
|
||||
await LoadProfileAsync(fileUtil.StripExtension(file));
|
||||
// Only allow files that fit the criteria of being a mongo id be parsed
|
||||
var filename = Path.GetFileNameWithoutExtension(file);
|
||||
if (MongoId.IsValidMongoId(filename))
|
||||
{
|
||||
await LoadProfileAsync(fileUtil.StripExtension(file));
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
@@ -201,12 +209,35 @@ public class SaveServer(
|
||||
/// <param name="sessionID"> ID of profile to store in memory </param>
|
||||
public async Task LoadProfileAsync(MongoId sessionID)
|
||||
{
|
||||
var filename = $"{sessionID.ToString()}.json";
|
||||
var filePath = $"{profileFilepath}{filename}";
|
||||
var filePath = Path.Combine(profileFilepath, $"{sessionID}.json");
|
||||
if (fileUtil.FileExists(filePath))
|
||||
// File found, store in profiles[]
|
||||
{
|
||||
var profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
|
||||
JsonObject? profile = null;
|
||||
|
||||
try
|
||||
{
|
||||
profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
// If the profile fails to deserialize, it may have corrupted, try to restore from a backup
|
||||
logger.Warning($"Failed loading profile for {sessionID.ToString()}. Attempting to load backup");
|
||||
|
||||
// We make a copy of the profile before overwriting it, just incase
|
||||
var corruptBackupPath = Path.Combine(profileFilepath, $"{sessionID}-corrupt.json");
|
||||
File.Copy(filePath, corruptBackupPath, true);
|
||||
|
||||
if (backupService.RestoreProfile(sessionID))
|
||||
{
|
||||
profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
|
||||
logger.Success("Profile restored from backup!");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Failed to restore profile backup", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (profile is not null)
|
||||
{
|
||||
@@ -249,34 +280,48 @@ public class SaveServer(
|
||||
return 0;
|
||||
}
|
||||
|
||||
var filePath = $"{profileFilepath}{sessionID.ToString()}.json";
|
||||
// Lock based on sessionID so we don't attempt to write to the same save file
|
||||
// multiple times at the same time, leading to file access contention
|
||||
SemaphoreSlim saveLock = saveLocks.GetOrAdd(sessionID, _ => new SemaphoreSlim(1, 1));
|
||||
await saveLock.WaitAsync();
|
||||
|
||||
// Run pre-save callbacks before we save into json
|
||||
foreach (var callback in onBeforeSaveCallbacks)
|
||||
Stopwatch start;
|
||||
try
|
||||
{
|
||||
var previous = profiles[sessionID];
|
||||
try
|
||||
var filePath = Path.Combine(profileFilepath, $"{sessionID}.json");
|
||||
|
||||
// Run pre-save callbacks before we save into json
|
||||
foreach (var callback in onBeforeSaveCallbacks)
|
||||
{
|
||||
profiles[sessionID] = onBeforeSaveCallbacks[callback.Key](profiles[sessionID]);
|
||||
var previous = profiles[sessionID];
|
||||
try
|
||||
{
|
||||
profiles[sessionID] = onBeforeSaveCallbacks[callback.Key](profiles[sessionID]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error(serverLocalisationService.GetText("profile_save_callback_error", new { callback, error = e }));
|
||||
profiles[sessionID] = previous;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
start = Stopwatch.StartNew();
|
||||
var jsonProfile = jsonUtil.Serialize(profiles[sessionID], !configServer.GetConfig<CoreConfig>().Features.CompressProfile);
|
||||
var fmd5 = await hashUtil.GenerateHashForDataAsync(HashingAlgorithm.MD5, jsonProfile);
|
||||
if (!saveMd5.TryGetValue(sessionID, out var currentMd5) || currentMd5 != fmd5)
|
||||
{
|
||||
logger.Error(serverLocalisationService.GetText("profile_save_callback_error", new { callback, error = e }));
|
||||
profiles[sessionID] = previous;
|
||||
saveMd5[sessionID] = fmd5;
|
||||
// save profile to disk
|
||||
await fileUtil.WriteFileAsync(filePath, jsonProfile);
|
||||
}
|
||||
|
||||
start.Stop();
|
||||
}
|
||||
finally
|
||||
{
|
||||
saveLock.Release();
|
||||
}
|
||||
|
||||
var start = Stopwatch.StartNew();
|
||||
var jsonProfile = jsonUtil.Serialize(profiles[sessionID], !configServer.GetConfig<CoreConfig>().Features.CompressProfile);
|
||||
var fmd5 = await hashUtil.GenerateHashForDataAsync(HashingAlgorithm.MD5, jsonProfile);
|
||||
if (!saveMd5.TryGetValue(sessionID, out var currentMd5) || currentMd5 != fmd5)
|
||||
{
|
||||
saveMd5[sessionID] = fmd5;
|
||||
// save profile to disk
|
||||
await fileUtil.WriteFileAsync(filePath, jsonProfile);
|
||||
}
|
||||
|
||||
start.Stop();
|
||||
return start.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
@@ -287,7 +332,7 @@ public class SaveServer(
|
||||
/// <returns> True if successful </returns>
|
||||
public bool RemoveProfile(MongoId sessionID)
|
||||
{
|
||||
var file = $"{profileFilepath}{sessionID}.json";
|
||||
var file = Path.Combine(profileFilepath, $"{sessionID}.json");
|
||||
if (profiles.ContainsKey(sessionID))
|
||||
{
|
||||
profiles.TryRemove(sessionID, out _);
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace SPTarkov.Server.Core.Services;
|
||||
public class BackupService
|
||||
{
|
||||
protected const string ProfileDir = "./user/profiles";
|
||||
protected const string activeModsFilename = "activeMods.json";
|
||||
|
||||
protected readonly List<string> ActiveServerMods;
|
||||
protected readonly BackupConfig BackupConfig;
|
||||
@@ -20,6 +21,9 @@ public class BackupService
|
||||
// Runs Init() every x minutes
|
||||
protected Timer _backupIntervalTimer;
|
||||
|
||||
protected SemaphoreSlim BackupLock = new SemaphoreSlim(1, 1);
|
||||
protected long LastBackupTimestamp;
|
||||
|
||||
protected readonly FileUtil FileUtil;
|
||||
protected readonly JsonUtil JsonUtil;
|
||||
protected readonly ISptLogger<BackupService> Logger;
|
||||
@@ -77,7 +81,7 @@ public class BackupService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the backup process. <br />
|
||||
/// Run the backup process. <br />
|
||||
/// This method orchestrates the profile backup service. Handles copying profiles to a backup directory and cleaning
|
||||
/// up old backups if the number exceeds the configured maximum.
|
||||
/// </summary>
|
||||
@@ -88,63 +92,86 @@ public class BackupService
|
||||
return;
|
||||
}
|
||||
|
||||
var targetDir = GenerateBackupTargetDir();
|
||||
|
||||
// Fetch all profiles in the profile directory.
|
||||
List<string> currentProfilePaths;
|
||||
try
|
||||
// If the backup lock is already locked, skip backup. This stops multiple backups running at once
|
||||
// Passing 0 is a non-blocking Wait, will return false if the lock can't be acquired
|
||||
bool lockAcquired = await BackupLock.WaitAsync(0);
|
||||
if (!lockAcquired)
|
||||
{
|
||||
currentProfilePaths = FileUtil.GetFiles(ProfileDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug($"Skipping profile backup: Unable to read profiles directory, {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentProfilePaths.Count == 0)
|
||||
try
|
||||
{
|
||||
if (Logger.IsLogEnabled(LogLevel.Debug))
|
||||
// Make sure we don't back up too often by using a configurable Cooldown
|
||||
var currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
if (currentTimestamp < LastBackupTimestamp + BackupConfig.BackupCooldown)
|
||||
{
|
||||
Logger.Debug("No profiles to backup");
|
||||
return;
|
||||
}
|
||||
LastBackupTimestamp = currentTimestamp;
|
||||
|
||||
var targetDir = GenerateBackupTargetDir();
|
||||
|
||||
// Fetch all profiles in the profile directory.
|
||||
List<string> currentProfilePaths;
|
||||
try
|
||||
{
|
||||
currentProfilePaths = FileUtil.GetFiles(ProfileDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug($"Skipping profile backup: Unable to read profiles directory, {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileUtil.CreateDirectory(targetDir);
|
||||
|
||||
foreach (var profilePath in currentProfilePaths)
|
||||
if (currentProfilePaths.Count == 0)
|
||||
{
|
||||
// Get filename + extension, removing the path
|
||||
var profileFileName = FileUtil.GetFileNameAndExtension(profilePath);
|
||||
|
||||
// Create absolute path to file
|
||||
var relativeSourceFilePath = Path.Combine(ProfileDir, profileFileName);
|
||||
var absoluteDestinationFilePath = Path.Combine(targetDir, profileFileName);
|
||||
if (!FileUtil.CopyFile(relativeSourceFilePath, absoluteDestinationFilePath))
|
||||
if (Logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
Logger.Error($"Source file not found: {relativeSourceFilePath}. Cannot copy to: {absoluteDestinationFilePath}");
|
||||
Logger.Debug("No profiles to backup");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileUtil.CreateDirectory(targetDir);
|
||||
|
||||
foreach (var profilePath in currentProfilePaths)
|
||||
{
|
||||
// Get filename + extension, removing the path
|
||||
var profileFileName = FileUtil.GetFileNameAndExtension(profilePath);
|
||||
|
||||
// Create absolute path to file
|
||||
var relativeSourceFilePath = Path.Combine(ProfileDir, profileFileName);
|
||||
var absoluteDestinationFilePath = Path.Combine(targetDir, profileFileName);
|
||||
if (!FileUtil.CopyFile(relativeSourceFilePath, absoluteDestinationFilePath))
|
||||
{
|
||||
Logger.Error($"Source file not found: {relativeSourceFilePath}. Cannot copy to: {absoluteDestinationFilePath}");
|
||||
}
|
||||
}
|
||||
|
||||
// Write a copy of active mods.
|
||||
await FileUtil.WriteFileAsync(Path.Combine(targetDir, activeModsFilename), JsonUtil.Serialize(ActiveServerMods));
|
||||
|
||||
if (Logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
Logger.Debug($"Profile backup created in: {targetDir}");
|
||||
}
|
||||
}
|
||||
|
||||
// Write a copy of active mods.
|
||||
await FileUtil.WriteFileAsync(Path.Combine(targetDir, "activeMods.json"), JsonUtil.Serialize(ActiveServerMods));
|
||||
|
||||
if (Logger.IsLogEnabled(LogLevel.Debug))
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug($"Profile backup created in: {targetDir}");
|
||||
Logger.Error($"Unable to write to backup profile directory: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Unable to write to backup profile directory: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
CleanBackups();
|
||||
CleanBackups();
|
||||
}
|
||||
finally
|
||||
{
|
||||
BackupLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -223,6 +250,24 @@ public class BackupService
|
||||
return result;
|
||||
}
|
||||
|
||||
protected string? GetMostRecentProfileBackup(IEnumerable<string> backupPaths, string profileId)
|
||||
{
|
||||
var profileFilename = $"{profileId}.json";
|
||||
var backupPathsWithCreationDateTime = GetBackupPathsWithCreationTimestamp(backupPaths);
|
||||
|
||||
foreach (var (backupTimestamp, backupPath) in backupPathsWithCreationDateTime.Reverse())
|
||||
{
|
||||
var profileBackups = FileUtil.GetFiles(backupPath);
|
||||
var profileBackup = profileBackups.FirstOrDefault(path => path.EndsWith(profileFilename));
|
||||
if (profileBackup != null)
|
||||
{
|
||||
return profileBackup;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves and sorts the backup file paths from the specified directory.
|
||||
/// </summary>
|
||||
@@ -308,4 +353,29 @@ public class BackupService
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the most recent profile backup for the given profile Id
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile ID of the backup to restore</param>
|
||||
/// <returns>True on success. False on failure</returns>
|
||||
public bool RestoreProfile(string profileId)
|
||||
{
|
||||
var backupDir = BackupConfig.Directory;
|
||||
var backupPaths = GetBackupPaths(backupDir);
|
||||
var mostRecentBackupForProfile = GetMostRecentProfileBackup(backupPaths, profileId);
|
||||
|
||||
// Verify we have a backup for this profile
|
||||
if (mostRecentBackupForProfile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore the most recent profile backup
|
||||
var profileFileName = FileUtil.GetFileNameAndExtension(mostRecentBackupForProfile);
|
||||
var targetProfilePath = Path.Combine(ProfileDir, profileFileName);
|
||||
|
||||
File.Copy(mostRecentBackupForProfile, targetProfilePath, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public class BotLootCacheService(
|
||||
private readonly Lock _grenadeLock = new();
|
||||
private readonly Lock _specialLock = new();
|
||||
private readonly Lock _healingLock = new();
|
||||
private readonly Lock _lootCacheGeneratedLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Remove cached bot loot data
|
||||
@@ -47,7 +48,7 @@ public class BotLootCacheService(
|
||||
/// <param name="botJsonTemplate">Base json db file for the bot having its loot generated</param>
|
||||
/// <param name="itemPriceMinMax">OPTIONAL - item price min and max value filter</param>
|
||||
/// <remarks>THIS IS NOT A THREAD SAFE METHOD</remarks>
|
||||
/// <returns>dictionary</returns>
|
||||
/// <returns>dictionary, key = item tpl, value = weight</returns>
|
||||
public Dictionary<MongoId, double> GetLootFromCache(
|
||||
string botRole,
|
||||
bool isPmc,
|
||||
@@ -56,10 +57,14 @@ public class BotLootCacheService(
|
||||
MinMax<double>? itemPriceMinMax = null
|
||||
)
|
||||
{
|
||||
if (!BotRoleExistsInCache(botRole))
|
||||
// Ensure cache is hydrated before use
|
||||
lock (_lootCacheGeneratedLock)
|
||||
{
|
||||
InitCacheForBotRole(botRole);
|
||||
AddLootToCache(botRole, isPmc, botJsonTemplate);
|
||||
if (!BotRoleExistsInCache(botRole))
|
||||
{
|
||||
InitCacheForBotRole(botRole);
|
||||
AddLootToCache(botRole, isPmc, botJsonTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_lootCache.TryGetValue(botRole, out var botRoleCache))
|
||||
|
||||
@@ -26,6 +26,7 @@ public class LocationLifecycleService(
|
||||
TimeUtil timeUtil,
|
||||
DatabaseService databaseService,
|
||||
ProfileHelper profileHelper,
|
||||
BackupService backupService,
|
||||
ProfileActivityService profileActivityService,
|
||||
BotNameService botNameService,
|
||||
ICloner cloner,
|
||||
@@ -77,6 +78,9 @@ public class LocationLifecycleService(
|
||||
/// </summary>
|
||||
public virtual StartLocalRaidResponseData StartLocalRaid(MongoId sessionId, StartLocalRaidRequestData request)
|
||||
{
|
||||
// Backup the profile on raid start
|
||||
backupService.Init().GetAwaiter().GetResult();
|
||||
|
||||
logger.Debug($"Starting: {request.Location}");
|
||||
|
||||
var playerProfile = profileHelper.GetFullProfile(sessionId);
|
||||
@@ -409,6 +413,10 @@ public class LocationLifecycleService(
|
||||
HandleCoopExtract(sessionId, pmcProfile, request.Results.ExitName);
|
||||
SendCoopTakenFenceMessage(sessionId);
|
||||
}
|
||||
|
||||
// Save and backup the profile on raid end
|
||||
saveServer.SaveProfileAsync(sessionId).GetAwaiter().GetResult();
|
||||
backupService.Init().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -622,7 +630,7 @@ public class LocationLifecycleService(
|
||||
pmcProfile.Info.LastTimePlayedAsSavage = timeUtil.GetTimeStamp();
|
||||
|
||||
// Force a profile save
|
||||
saveServer.SaveProfileAsync(sessionId);
|
||||
saveServer.SaveProfileAsync(sessionId).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -784,11 +792,7 @@ public class LocationLifecycleService(
|
||||
MergePmcAndScavEncyclopedias(serverPmcProfile, scavProfile);
|
||||
|
||||
// Handle temp, hydration, limb hp/effects
|
||||
healthHelper.ApplyHealthChangesToProfile(sessionId, serverPmcProfile, postRaidProfile.Health);
|
||||
|
||||
// Required when player loses limb in-raid and fixes it, max now stuck at 50% or less if lost multiple times
|
||||
var profileTemplate = profileHelper.GetProfileTemplateForSide(fullServerProfile.ProfileInfo.Edition, serverPmcProfile.Info.Side);
|
||||
serverPmcProfile.ResetMaxLimbHp(profileTemplate);
|
||||
healthHelper.ApplyHealthChangesToProfile(sessionId, serverPmcProfile, postRaidProfile.Health, isDead);
|
||||
|
||||
if (isTransfer)
|
||||
{
|
||||
|
||||
@@ -240,27 +240,34 @@ public class CustomItemService(
|
||||
/// <param name="newItemId"> ID of the item being created </param>
|
||||
protected void AddToLocaleDbs(Dictionary<string, LocaleDetails> localeDetails, string newItemId)
|
||||
{
|
||||
// Validate that there's atleast one locale to use as a default
|
||||
var defaultLocale = localeDetails.Keys.FirstOrDefault();
|
||||
if (defaultLocale == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var languages = databaseService.GetLocales().Languages;
|
||||
foreach (var shortNameKey in languages)
|
||||
{
|
||||
// Get locale details passed in, if not provided by caller use first record in newItemDetails.locales
|
||||
localeDetails.TryGetValue(shortNameKey.Key, out var newLocaleDetails);
|
||||
|
||||
newLocaleDetails ??= localeDetails[localeDetails.Keys.FirstOrDefault()];
|
||||
newLocaleDetails ??= localeDetails[defaultLocale];
|
||||
|
||||
// If there's no name defined, don't add a transformer, the mod is probably handling locales elsewhere
|
||||
if (newLocaleDetails.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (databaseService.GetLocales().Global.TryGetValue(shortNameKey.Key, out var lazyLoad))
|
||||
{
|
||||
lazyLoad.AddTransformer(localeData =>
|
||||
{
|
||||
if (!localeData.TryAdd($"{newItemId} Name", newLocaleDetails.Name))
|
||||
{
|
||||
logger.Error($"Error adding locale `{newItemId} Name` to {shortNameKey.Key}, duplicate key");
|
||||
}
|
||||
else
|
||||
{
|
||||
localeData.TryAdd($"{newItemId} ShortName", newLocaleDetails.ShortName);
|
||||
localeData.TryAdd($"{newItemId} Description", newLocaleDetails.Description);
|
||||
}
|
||||
localeData![$"{newItemId} Name"] = newLocaleDetails.Name;
|
||||
localeData[$"{newItemId} ShortName"] = newLocaleDetails.ShortName ?? "";
|
||||
localeData[$"{newItemId} Description"] = newLocaleDetails.Description ?? "";
|
||||
|
||||
return localeData;
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ public class ProfileDataService(ISptLogger<ProfileDataService> logger, FileUtil
|
||||
value = jsonUtil.Deserialize<T>(fileUtil.ReadFile($"{ProfileDataFilepath}{profileId}/{modKey}.json"));
|
||||
if (value != null)
|
||||
{
|
||||
while (!_profileDataCache.TryAdd(profileDataKey, value)) { }
|
||||
_profileDataCache[$"{profileId}:{modKey}"] = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -42,14 +42,13 @@ public class ProfileDataService(ISptLogger<ProfileDataService> logger, FileUtil
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(profileData);
|
||||
|
||||
var data = jsonUtil.Serialize(profileData, profileData.GetType());
|
||||
var data = jsonUtil.Serialize(profileData, profileData.GetType(), true);
|
||||
if (data == null)
|
||||
{
|
||||
throw new Exception("The profile data when serialized resulted in a null value");
|
||||
}
|
||||
|
||||
while (!_profileDataCache.TryAdd($"{profileId}:{modKey}", data)) { }
|
||||
|
||||
_profileDataCache[$"{profileId}:{modKey}"] = profileData;
|
||||
fileUtil.WriteFile($"{ProfileDataFilepath}{profileId}/{modKey}.json", data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
drawnKeys.Add(chosenItem.Key);
|
||||
|
||||
// Only remove item if it's not in whitelist
|
||||
if (neverRemoveWhitelist is not null && !neverRemoveWhitelist.Contains(chosenItem.Key))
|
||||
if (neverRemoveWhitelist is null || !neverRemoveWhitelist.Contains(chosenItem.Key))
|
||||
{
|
||||
// Reduce total weight value by items weight + Remove item from pool
|
||||
totalWeight -= chosenItem.Weight;
|
||||
|
||||
@@ -131,10 +131,18 @@ public class FileUtil
|
||||
|
||||
// We flush here so we can be sure it's immediately committed to disk
|
||||
await fs.FlushAsync();
|
||||
fs.Flush(true);
|
||||
}
|
||||
|
||||
// Overwrite over the old file
|
||||
File.Move(tempFilePath, filePath, overwrite: true);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Replace(tempFilePath, filePath, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Move(tempFilePath, filePath);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -12,5 +12,6 @@ public class StaticReferences
|
||||
public Dictionary<string, object> Reference
|
||||
{
|
||||
get { return _reference; }
|
||||
set { _reference = value; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Watermark(
|
||||
{
|
||||
var versionTag = GetVersionTag();
|
||||
|
||||
versionLabel = $"{sptConfig.ProjectName} {versionTag} | EFT {sptConfig.CompatibleTarkovVersion}";
|
||||
versionLabel = $"{sptConfig.ProjectName} {versionTag}";
|
||||
|
||||
text.Add(versionLabel);
|
||||
text.AddRange(watermarkLocale.Description);
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
_sptVersion = ProgramStatics.SPT_VERSION().ToString();
|
||||
_debugEnabled = ProgramStatics.DEBUG();
|
||||
_modsEnabled = ProgramStatics.MODS();
|
||||
_startTime = TimeUtil.GetDateTimeFromTimeStamp(coreConfig.ServerStartTime.Value);
|
||||
_startTime = TimeUtil.GetDateTimeFromTimeStamp(coreConfig.ServerStartTime.Value).ToLocalTime();
|
||||
_uptimeSeconds = DateTimeOffset.Now.ToUnixTimeSeconds() - coreConfig.ServerStartTime.Value;
|
||||
|
||||
var activeProfileIds = ProfileActivityService.GetActiveProfileIdsWithinMinutes(30);
|
||||
|
||||
@@ -58,7 +58,22 @@ public class JsonExtensionDataPatch : IPatcher
|
||||
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, field));
|
||||
get.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
// Add setter
|
||||
var set = new MethodDefinition(
|
||||
"set_ExtensionData",
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
assembly.MainModule.TypeSystem.Void
|
||||
);
|
||||
|
||||
set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, _dictionaryStringObjectReference));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
|
||||
set.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
propertyDefinition.SetMethod = set;
|
||||
propertyDefinition.GetMethod = get;
|
||||
typeDefinition.Methods.Add(set);
|
||||
typeDefinition.Methods.Add(get);
|
||||
|
||||
typeDefinition.Properties.Add(propertyDefinition);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user