Merge branch 'main' of https://github.com/sp-tarkov/server-csharp
This commit is contained in:
@@ -55,6 +55,18 @@ public class ClientLogCallbacks
|
||||
public string ReleaseNotes()
|
||||
{
|
||||
var data = _configServer.GetConfig<CoreConfig>(ConfigTypes.CORE).Release;
|
||||
data.BetaDisclaimerText = "BetaDisclaimerText";
|
||||
data.BetaDisclaimerAcceptText = "BetaDisclaimerAcceptText";
|
||||
data.ServerModsLoadedText = "ServerModsLoadedText";
|
||||
data.ClientModsLoadedText = "clientModsLoadedText";
|
||||
data.ClientModsLoadedDebugText = "clientModsLoadedDebugText";
|
||||
data.ClientModsLoadedDebugText = "clientModsLoadedDebugText";
|
||||
data.IllegalPluginsExceptionText = "IllegalPluginsExceptionText";
|
||||
data.ReleaseSummaryText = "ReleaseSummaryText";
|
||||
data.IsBeta = false;
|
||||
data.IsModdable = true;
|
||||
data.IsModded = false;
|
||||
|
||||
|
||||
// data.betaDisclaimerText = ProgramStatics.MODS
|
||||
// ? this.localisationService.getText("release-beta-disclaimer-mods-enabled")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Core.Annotations;
|
||||
using Core.Annotations;
|
||||
using Core.Controllers;
|
||||
using Core.DI;
|
||||
using Core.Models.Eft.Common;
|
||||
|
||||
@@ -57,10 +57,10 @@ public class CustomizationController
|
||||
{
|
||||
var pmcData = _profileHelper.GetPmcProfile(sessionId);
|
||||
var clothing = _databaseService.GetCustomization();
|
||||
var suits = _databaseService.GetTrader(traderId).Suits;
|
||||
var suits = _databaseService.GetTrader(traderId)?.Suits;
|
||||
|
||||
var matchingSuits = suits.Where(s => clothing.ContainsKey(s.SuiteId)).ToList();
|
||||
matchingSuits = matchingSuits?.Where(s => clothing[s.SuiteId].Properties.Side.Contains(pmcData.Info.Side)).ToList();
|
||||
var matchingSuits = suits.Where(s => clothing.ContainsKey(s?.SuiteId)).ToList();
|
||||
matchingSuits = matchingSuits?.Where(s => clothing[s?.SuiteId].Properties.Side.Contains(pmcData?.Info?.Side)).ToList();
|
||||
|
||||
if (matchingSuits == null)
|
||||
throw new Exception(_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
|
||||
@@ -83,21 +83,21 @@ public class CustomizationController
|
||||
{
|
||||
var output = _eventOutputHolder.GetOutput(sessionId);
|
||||
|
||||
var traderOffer = GetTraderClothingOffer(sessionId, buyClothingRequest.Offer);
|
||||
var traderOffer = GetTraderClothingOffer(sessionId, buyClothingRequest?.Offer);
|
||||
if (traderOffer == null)
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("customisation-unable_to_find_suit_by_id", buyClothingRequest.Offer));
|
||||
return output;
|
||||
}
|
||||
|
||||
var suitId = traderOffer.SuiteId;
|
||||
var suitId = traderOffer?.SuiteId;
|
||||
if (OutfitAlreadyPurchased(suitId, sessionId))
|
||||
{
|
||||
var suitDetails = _databaseService.GetCustomization()[suitId];
|
||||
_logger.Error(_localisationService.GetText("customisation-item_already_purchased", new
|
||||
{
|
||||
ItemId = suitDetails.Id,
|
||||
ItemName = suitDetails.Name,
|
||||
ItemId = suitDetails?.Id,
|
||||
ItemName = suitDetails?.Name,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class CustomizationController
|
||||
|
||||
private Suit GetTraderClothingOffer(string sessionId, string? offerId)
|
||||
{
|
||||
var foundSuit = GetAllTraderSuits(sessionId).FirstOrDefault(s => s.Id == offerId);
|
||||
var foundSuit = GetAllTraderSuits(sessionId).FirstOrDefault(s => s?.Id == offerId);
|
||||
if (foundSuit == null)
|
||||
throw new Exception(_localisationService.GetText("customisation-unable_to_find_suit_with_id", offerId));
|
||||
|
||||
@@ -143,61 +143,61 @@ public class CustomizationController
|
||||
/// <param name="output">Client response</param>
|
||||
private void PayForClothingItem(string sessionId, PmcData pmcData, PaymentItemForClothing paymentItemDetails, ItemEventRouterResponse output)
|
||||
{
|
||||
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(x => x.Id == paymentItemDetails.Id);
|
||||
var inventoryItem = pmcData?.Inventory?.Items?.FirstOrDefault(x => x?.Id == paymentItemDetails?.Id);
|
||||
if (inventoryItem == null)
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("customisation-unable_to_find_clothing_item_in_inventory", paymentItemDetails.Id));
|
||||
return;
|
||||
}
|
||||
|
||||
if (paymentItemDetails.Del != null)
|
||||
if (paymentItemDetails?.Del != null)
|
||||
{
|
||||
output.ProfileChanges[sessionId].Items.DeletedItems.Add(new Product
|
||||
output?.ProfileChanges[sessionId]?.Items?.DeletedItems?.Add(new Product
|
||||
{
|
||||
Id = inventoryItem.Id,
|
||||
Template = inventoryItem.Template,
|
||||
ParentId = inventoryItem.ParentId,
|
||||
SlotId = inventoryItem.SlotId,
|
||||
Location = (ItemLocation)inventoryItem.Location,
|
||||
Upd = inventoryItem.Upd
|
||||
Id = inventoryItem?.Id,
|
||||
Template = inventoryItem?.Template,
|
||||
ParentId = inventoryItem?.ParentId,
|
||||
SlotId = inventoryItem?.SlotId,
|
||||
Location = (ItemLocation)inventoryItem?.Location,
|
||||
Upd = inventoryItem?.Upd
|
||||
});
|
||||
|
||||
pmcData.Inventory.Items.Remove(inventoryItem);
|
||||
pmcData?.Inventory?.Items?.Remove(inventoryItem);
|
||||
}
|
||||
|
||||
if (inventoryItem.Upd == null)
|
||||
if (inventoryItem?.Upd == null)
|
||||
inventoryItem.Upd = new() { StackObjectsCount = 1 };
|
||||
|
||||
if (inventoryItem.Upd.StackObjectsCount == null)
|
||||
if (inventoryItem?.Upd?.StackObjectsCount == null)
|
||||
inventoryItem.Upd.StackObjectsCount = 1;
|
||||
|
||||
if (inventoryItem.Upd.StackObjectsCount == paymentItemDetails.Count)
|
||||
if (inventoryItem?.Upd?.StackObjectsCount == paymentItemDetails?.Count)
|
||||
{
|
||||
output.ProfileChanges[sessionId].Items.DeletedItems.Add(new Product
|
||||
output?.ProfileChanges[sessionId]?.Items?.DeletedItems?.Add(new Product
|
||||
{
|
||||
Id = inventoryItem.Id,
|
||||
Template = inventoryItem.Template,
|
||||
ParentId = inventoryItem.ParentId,
|
||||
SlotId = inventoryItem.SlotId,
|
||||
Location = (ItemLocation)inventoryItem.Location,
|
||||
Upd = inventoryItem.Upd
|
||||
Id = inventoryItem?.Id,
|
||||
Template = inventoryItem?.Template,
|
||||
ParentId = inventoryItem?.ParentId,
|
||||
SlotId = inventoryItem?.SlotId,
|
||||
Location = (ItemLocation)inventoryItem?.Location,
|
||||
Upd = inventoryItem?.Upd
|
||||
});
|
||||
|
||||
pmcData.Inventory.Items.Remove(inventoryItem);
|
||||
pmcData?.Inventory?.Items?.Remove(inventoryItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inventoryItem.Upd.StackObjectsCount > paymentItemDetails.Count)
|
||||
if (inventoryItem.Upd.StackObjectsCount > paymentItemDetails?.Count)
|
||||
{
|
||||
inventoryItem.Upd.StackObjectsCount -= paymentItemDetails.Count;
|
||||
output.ProfileChanges[sessionId].Items.ChangedItems.Add(new Product
|
||||
inventoryItem.Upd.StackObjectsCount -= paymentItemDetails?.Count;
|
||||
output?.ProfileChanges[sessionId]?.Items?.ChangedItems.Add(new Product
|
||||
{
|
||||
Id = inventoryItem.Id,
|
||||
Template = inventoryItem.Template,
|
||||
ParentId = inventoryItem.ParentId,
|
||||
SlotId = inventoryItem.SlotId,
|
||||
Location = (ItemLocation)inventoryItem.Location,
|
||||
Upd = inventoryItem.Upd
|
||||
Id = inventoryItem?.Id,
|
||||
Template = inventoryItem?.Template,
|
||||
ParentId = inventoryItem?.ParentId,
|
||||
SlotId = inventoryItem?.SlotId,
|
||||
Location = (ItemLocation)inventoryItem?.Location,
|
||||
Upd = inventoryItem?.Upd
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ public class CustomizationController
|
||||
throw new Exception($"Unknown game edition given from profile {profile}");
|
||||
}
|
||||
|
||||
var prestigeLevel = profile.CharacterData.PmcData.Info.PrestigeLevel;
|
||||
var prestigeLevel = profile?.CharacterData?.PmcData?.Info?.PrestigeLevel;
|
||||
if (prestigeLevel != null)
|
||||
{
|
||||
if (prestigeLevel >= 1)
|
||||
@@ -324,10 +324,10 @@ public class CustomizationController
|
||||
/// <returns></returns>
|
||||
private string GetGameEdition(SptProfile profile)
|
||||
{
|
||||
var edition = profile.CharacterData.PmcData.Info.GameVersion;
|
||||
var edition = profile?.CharacterData?.PmcData?.Info?.GameVersion;
|
||||
if (edition == null)
|
||||
{
|
||||
var launcherEdition = profile.ProfileInfo.Edition;
|
||||
var launcherEdition = profile?.ProfileInfo?.Edition;
|
||||
switch (launcherEdition.ToLower())
|
||||
{
|
||||
case "edge of darkness":
|
||||
@@ -351,18 +351,18 @@ public class CustomizationController
|
||||
/// <returns></returns>
|
||||
public ItemEventRouterResponse SetClothing(string sessionId, CustomizationSetRequest request, PmcData pmcData)
|
||||
{
|
||||
foreach (var customisation in request.Customizations)
|
||||
foreach (var customisation in request?.Customizations)
|
||||
{
|
||||
switch (customisation.Id)
|
||||
{
|
||||
case "dogTag":
|
||||
pmcData.Customization.DogTag = customisation.Id;
|
||||
pmcData.Customization.DogTag = customisation?.Id;
|
||||
break;
|
||||
case "suite":
|
||||
ApplyClothingItemToProfile(customisation, pmcData);
|
||||
break;
|
||||
default:
|
||||
_logger.Error($"Unhandled customisation type: {customisation.Type}");
|
||||
_logger.Error($"Unhandled customisation type: {customisation?.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -377,24 +377,24 @@ public class CustomizationController
|
||||
/// <param name="pmcData">Profile to update</param>
|
||||
private void ApplyClothingItemToProfile(CustomizationSetOption customisation, PmcData pmcData)
|
||||
{
|
||||
var dbSuit = _databaseService.GetCustomization()[customisation.Id];
|
||||
var dbSuit = _databaseService.GetCustomization()[customisation?.Id];
|
||||
if (dbSuit == null)
|
||||
{
|
||||
_logger.Error($"Unable to find suit customisation id: {customisation.Id}, cannot apply clothing to player profile: {pmcData.Id}");
|
||||
_logger.Error($"Unable to find suit customisation id: {customisation?.Id}, cannot apply clothing to player profile: {pmcData.Id}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbSuit.Parent == "5cd944d01388ce000a659df9")
|
||||
{
|
||||
pmcData.Customization.Body = dbSuit.Properties.Body;
|
||||
pmcData.Customization.Hands = dbSuit.Properties.Hands;
|
||||
pmcData.Customization.Body = dbSuit?.Properties?.Body;
|
||||
pmcData.Customization.Hands = dbSuit?.Properties?.Hands;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbSuit.Parent == "5cd944ca1388ce03a44dc2a4")
|
||||
{
|
||||
pmcData.Customization.Feet = dbSuit.Properties.Feet;
|
||||
pmcData.Customization.Feet = dbSuit?.Properties?.Feet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
using Core.Annotations;
|
||||
using Core.Helpers;
|
||||
using Core.Helpers.Dialogue;
|
||||
using Core.Models.Eft.Dialog;
|
||||
using Core.Models.Eft.HttpResponse;
|
||||
using Core.Models.Eft.Profile;
|
||||
using Core.Models.Enums;
|
||||
using Core.Servers;
|
||||
|
||||
namespace Core.Controllers;
|
||||
|
||||
[Injectable]
|
||||
public class DialogueController
|
||||
{
|
||||
private readonly DialogueHelper _dialogueHelper;
|
||||
private readonly ProfileHelper _profileHelper;
|
||||
private readonly SaveServer _saveServer;
|
||||
private readonly List<IDialogueChatBot> _dialogueChatBots;
|
||||
|
||||
public DialogueController(
|
||||
DialogueHelper dialogueHelper,
|
||||
ProfileHelper profileHelper,
|
||||
SaveServer saveServer)
|
||||
{
|
||||
_dialogueHelper = dialogueHelper;
|
||||
_profileHelper = profileHelper;
|
||||
_saveServer = saveServer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -35,7 +53,33 @@ public class DialogueController
|
||||
/// <returns>GetFriendListDataResponse</returns>
|
||||
public GetFriendListDataResponse GetFriendList(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Add all chatbots to the friends list
|
||||
var friends = _dialogueChatBots.Select((bot) => bot.GetChatBot()).ToList();
|
||||
|
||||
// Add any friends the user has after the chatbots
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
if (profile?.FriendProfileIds is not null)
|
||||
{
|
||||
foreach (var friendId in profile.FriendProfileIds) {
|
||||
var friendProfile = _profileHelper.GetChatRoomMemberFromSessionId(friendId);
|
||||
if (friendProfile is not null)
|
||||
{
|
||||
friends.Add(new UserDialogInfo
|
||||
{
|
||||
Id = friendProfile.Id,
|
||||
Aid = friendProfile.Aid,
|
||||
Info = friendProfile.Info,
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new GetFriendListDataResponse
|
||||
{
|
||||
Friends = friends,
|
||||
Ignore = [],
|
||||
InIgnoreList = []
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,7 +91,13 @@ public class DialogueController
|
||||
/// <returns>list of dialogs</returns>
|
||||
public List<DialogueInfo> GenerateDialogueList(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var data = new List<DialogueInfo>();
|
||||
foreach (var dialogueId in _dialogueHelper.GetDialogsForProfile(sessionId))
|
||||
{
|
||||
data.Add(GetDialogueInfo(dialogueId.Key, sessionId));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -60,7 +110,20 @@ public class DialogueController
|
||||
string dialogueId,
|
||||
string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var dialogs = _dialogueHelper.GetDialogsForProfile(sessionId);
|
||||
var dialogue = dialogs.GetValueOrDefault(dialogueId);
|
||||
|
||||
var result = new DialogueInfo {
|
||||
Id = dialogueId,
|
||||
Type = dialogue.Type ?? MessageType.NPC_TRADER,
|
||||
Message = _dialogueHelper.GetMessagePreview(dialogue),
|
||||
New = dialogue.New,
|
||||
AttachmentsNew = dialogue.AttachmentsNew,
|
||||
Pinned = dialogue.Pinned,
|
||||
Users = GetDialogueUsers(dialogue, dialogue.Type.Value, sessionId),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,11 +134,36 @@ public class DialogueController
|
||||
/// <param name="sessionId">Player id</param>
|
||||
/// <returns>UserDialogInfo list</returns>
|
||||
public List<UserDialogInfo> GetDialogueUsers(
|
||||
Dialogue dialogue,
|
||||
Dialogue dialog,
|
||||
MessageType messageType,
|
||||
string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var profile = _saveServer.GetProfile(sessionId);
|
||||
|
||||
// User to user messages are special in that they need the player to exist in them, add if they don't
|
||||
if (
|
||||
messageType == MessageType.USER_MESSAGE &&
|
||||
!dialog.Users.Any((userDialog) => userDialog.Id == profile.CharacterData.PmcData.SessionId))
|
||||
{
|
||||
// nullguard
|
||||
dialog.Users ??= [];
|
||||
|
||||
dialog.Users.Add( new UserDialogInfo
|
||||
{
|
||||
Id = profile.CharacterData.PmcData.SessionId,
|
||||
Aid = profile.CharacterData.PmcData.Aid,
|
||||
Info = new UserDialogDetails
|
||||
{
|
||||
Level = profile.CharacterData.PmcData.Info.Level,
|
||||
Nickname = profile.CharacterData.PmcData.Info.Nickname,
|
||||
Side = profile.CharacterData.PmcData.Info.Side,
|
||||
MemberCategory = profile.CharacterData.PmcData.Info.MemberCategory,
|
||||
SelectedMemberCategory = profile.CharacterData.PmcData.Info.SelectedMemberCategory,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return dialog.Users;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,7 +20,9 @@ public class GameController
|
||||
private readonly ILogger _logger;
|
||||
private readonly ConfigServer _configServer;
|
||||
private readonly DatabaseService _databaseService;
|
||||
|
||||
private readonly TimeUtil _timeUtil;
|
||||
|
||||
// private readonly PreSptModLoader _preSptModLoader;
|
||||
private readonly HttpServerHelper _httpServerHelper;
|
||||
private readonly InventoryHelper _inventoryHelper;
|
||||
@@ -39,7 +41,7 @@ public class GameController
|
||||
private readonly ProfileActivityService _profileActivityService;
|
||||
private readonly ApplicationContext _applicationContext;
|
||||
private readonly ICloner _cloner;
|
||||
|
||||
|
||||
private readonly CoreConfig _coreConfig;
|
||||
private readonly HttpConfig _httpConfig;
|
||||
private readonly RagfairConfig _ragfairConfig;
|
||||
@@ -68,7 +70,7 @@ public class GameController
|
||||
ProfileActivityService profileActivityService,
|
||||
ApplicationContext applicationContext,
|
||||
ICloner cloner
|
||||
)
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_configServer = configServer;
|
||||
@@ -98,7 +100,7 @@ public class GameController
|
||||
_hideoutConfig = configServer.GetConfig<HideoutConfig>(ConfigTypes.HIDEOUT);
|
||||
_botConfig = configServer.GetConfig<BotConfig>(ConfigTypes.BOT);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/start
|
||||
/// </summary>
|
||||
@@ -110,9 +112,9 @@ public class GameController
|
||||
{
|
||||
// Store client start time in app context
|
||||
_applicationContext.AddValue(ContextVariableType.CLIENT_START_TIMESTAMP, $"{sessionId}_{startTimeStampMs}");
|
||||
|
||||
|
||||
_profileActivityService.SetActivityTimestamp(sessionId);
|
||||
|
||||
|
||||
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
|
||||
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
|
||||
// successful) repeatable quests. We also have to remove the Counters from the repeatableQuests
|
||||
@@ -124,7 +126,7 @@ public class GameController
|
||||
|
||||
if (fullProfile.SptData.Migrations == null)
|
||||
fullProfile.SptData.Migrations = new();
|
||||
|
||||
|
||||
if (fullProfile.FriendProfileIds == null)
|
||||
fullProfile.FriendProfileIds = new();
|
||||
|
||||
@@ -132,12 +134,12 @@ public class GameController
|
||||
{
|
||||
_inventoryHelper.ValidateInventoryUsesMongoIds(fullProfile.CharacterData.PmcData.Inventory.Items);
|
||||
Migrate39xProfile(fullProfile);
|
||||
|
||||
|
||||
// flag as migrated
|
||||
fullProfile.SptData.Migrations.Add("39x", _timeUtil.GetTimeStamp());
|
||||
_logger.Info($"Migration of 3.9.x profile: {fullProfile.ProfileInfo.Username} completed successfully");
|
||||
}
|
||||
|
||||
|
||||
// with our method of converting type from array for this prop, we *might* not need this?
|
||||
// if (Array.isArray(fullProfile.characters.pmc.WishList)) {
|
||||
// fullProfile.characters.pmc.WishList = {};
|
||||
@@ -146,17 +148,17 @@ public class GameController
|
||||
// if (Array.isArray(fullProfile.characters.scav.WishList)) {
|
||||
// fullProfile.characters.scav.WishList = {};
|
||||
// }
|
||||
|
||||
|
||||
if (fullProfile.DialogueRecords != null)
|
||||
_profileFixerService.CheckForAndFixDialogueAttachments(fullProfile);
|
||||
|
||||
|
||||
_logger.Debug($"Started game with session {sessionId} {fullProfile.ProfileInfo.Username}");
|
||||
|
||||
var pmcProfile = fullProfile.CharacterData.PmcData;
|
||||
|
||||
if (_coreConfig.Fixes.FixProfileBreakingInventoryItemIssues)
|
||||
_profileFixerService.FixProfileBreakingInventoryItemIssues(pmcProfile);
|
||||
|
||||
|
||||
if (pmcProfile.Health != null)
|
||||
UpdateProfileHealthValues(pmcProfile);
|
||||
|
||||
@@ -165,7 +167,7 @@ public class GameController
|
||||
SendPraporGiftsToNewProfiles(pmcProfile);
|
||||
_profileFixerService.CheckForOrphanedModdedItems(sessionId, fullProfile);
|
||||
}
|
||||
|
||||
|
||||
_profileFixerService.CheckForAndRemoveInvalidTraders(fullProfile);
|
||||
_profileFixerService.CheckForAndFixPmcProfileIssues(pmcProfile);
|
||||
|
||||
@@ -175,7 +177,7 @@ public class GameController
|
||||
_hideoutHelper.SetHideoutImprovementsToCompleted(pmcProfile);
|
||||
_hideoutHelper.UnlockHideoutWallInProfile(pmcProfile);
|
||||
}
|
||||
|
||||
|
||||
LogProfileDetails(fullProfile);
|
||||
SaveActiveModsToProfile(fullProfile);
|
||||
|
||||
@@ -184,10 +186,10 @@ public class GameController
|
||||
AddPlayerToPmcNames(pmcProfile);
|
||||
CheckForAndRemoveUndefinedDialogues(fullProfile);
|
||||
}
|
||||
|
||||
|
||||
if (pmcProfile.Skills.Common != null)
|
||||
WarnOnActiveBotReloadSkill(pmcProfile);
|
||||
|
||||
|
||||
_seasonalEventService.GivePlayerSeasonalGifts(sessionId);
|
||||
}
|
||||
}
|
||||
@@ -214,7 +216,41 @@ public class GameController
|
||||
/// <returns></returns>
|
||||
public GameConfigResponse GetGameConfig(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var profile = _profileHelper.GetPmcProfile(sessionId);
|
||||
var gameTime = profile?.Stats?.Eft?.OverallCounters?.Items.FirstOrDefault(c =>
|
||||
c.Key.Contains("LifeTime") &&
|
||||
c.Key.Contains("Pmc")).Value ?? 0D;
|
||||
|
||||
var config = new GameConfigResponse
|
||||
{
|
||||
Languages = _databaseService.GetLocales().Languages,
|
||||
IsNdaFree = false,
|
||||
IsReportAvailable = false,
|
||||
IsTwitchEventMember = false,
|
||||
Language = "en",
|
||||
Aid = profile.Aid,
|
||||
Taxonomy = 6,
|
||||
ActiveProfileId = sessionId,
|
||||
Backend = new()
|
||||
{
|
||||
Lobby = _httpServerHelper.GetBackendUrl(),
|
||||
Trading = _httpServerHelper.GetBackendUrl(),
|
||||
Messaging = _httpServerHelper.GetBackendUrl(),
|
||||
Main = _httpServerHelper.GetBackendUrl(),
|
||||
RagFair = _httpServerHelper.GetBackendUrl()
|
||||
},
|
||||
UseProtobuf = false,
|
||||
UtcTime = _timeUtil.GetTimeStamp(),
|
||||
TotalInGame = gameTime,
|
||||
SessionMode = "pve",
|
||||
PurchasedGames = new()
|
||||
{
|
||||
IsEftPurchased = true,
|
||||
IsArenaPurchased = false
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -241,7 +277,15 @@ public class GameController
|
||||
/// <returns></returns>
|
||||
public List<ServerDetails> GetServer(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return
|
||||
[
|
||||
new ServerDetails
|
||||
{
|
||||
Ip = _httpConfig.BackendIp,
|
||||
Port = _httpConfig.BackendPort
|
||||
}
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -249,12 +293,14 @@ public class GameController
|
||||
/// </summary>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public CurrentGroupResponse GetCurrentGroup(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return new CurrentGroupResponse
|
||||
{
|
||||
Squad = []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/checkVersion
|
||||
@@ -263,7 +309,11 @@ public class GameController
|
||||
/// <returns></returns>
|
||||
public CheckVersionResponse GetValidGameVersion(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return new CheckVersionResponse
|
||||
{
|
||||
IsValid = true,
|
||||
LatestVersion = _coreConfig.CompatibleTarkovVersion
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
using Core.Annotations;
|
||||
using Core.Models.Eft.Common;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Services;
|
||||
|
||||
namespace Core.Controllers;
|
||||
|
||||
[Injectable]
|
||||
public class PrestigeController
|
||||
{
|
||||
private DatabaseService _databaseService;
|
||||
|
||||
public PrestigeController
|
||||
(
|
||||
DatabaseService databaseService
|
||||
)
|
||||
{
|
||||
_databaseService = databaseService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle /client/prestige/list
|
||||
/// </summary>
|
||||
@@ -17,7 +28,7 @@ public class PrestigeController
|
||||
string sessionId,
|
||||
EmptyRequestData info)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _databaseService.GetTemplates().Prestige;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -35,7 +35,8 @@ public class TraderController
|
||||
/// <returns>Return a list of all traders</returns>
|
||||
public List<TraderBase> GetAllTraders(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var traders = new List<TraderBase>();
|
||||
return traders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Core.Annotations;
|
||||
using Core.Annotations;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
|
||||
namespace Core.Helpers;
|
||||
@@ -10,28 +10,28 @@ public class QuestConditionHelper
|
||||
List<QuestCondition> questConditions,
|
||||
Func<QuestCondition, List<QuestCondition>> furtherFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return FilterConditions(questConditions, "Quest", furtherFilter);
|
||||
}
|
||||
|
||||
public List<QuestCondition> GetLevelConditions(
|
||||
List<QuestCondition> questConditions,
|
||||
Func<QuestCondition, List<QuestCondition>> furtherFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return FilterConditions(questConditions, "Level", furtherFilter);
|
||||
}
|
||||
|
||||
public List<QuestCondition> GetLoyaltyConditions(
|
||||
List<QuestCondition> questConditions,
|
||||
Func<QuestCondition, List<QuestCondition>> furtherFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return FilterConditions(questConditions, "TraderLoyalty", furtherFilter);
|
||||
}
|
||||
|
||||
public List<QuestCondition> GetStandingConditions(
|
||||
List<QuestCondition> questConditions,
|
||||
Func<QuestCondition, List<QuestCondition>> furtherFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return FilterConditions(questConditions, "TraderStanding", furtherFilter);
|
||||
}
|
||||
|
||||
protected List<QuestCondition> FilterConditions(
|
||||
@@ -39,6 +39,15 @@ public class QuestConditionHelper
|
||||
string questType,
|
||||
Func<QuestCondition, List<QuestCondition>> furtherFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var filteredQuests = questConditions.Where((c) => {
|
||||
if (c.ConditionType == questType)
|
||||
{
|
||||
// return true or run the passed in function
|
||||
return furtherFilter is null || furtherFilter(c).Any();
|
||||
}
|
||||
return false;
|
||||
}).ToList();
|
||||
|
||||
return filteredQuests;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ using Core.Models.Eft.Hideout;
|
||||
using Core.Models.Eft.ItemEvent;
|
||||
using Core.Models.Eft.Quests;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Servers;
|
||||
using Core.Services;
|
||||
using Core.Utils;
|
||||
using ILogger = Core.Models.Utils.ILogger;
|
||||
@@ -19,19 +21,26 @@ public class QuestHelper
|
||||
private readonly DatabaseService _databaseService;
|
||||
private readonly QuestConditionHelper _questConditionHelper;
|
||||
private readonly ProfileHelper _profileHelper;
|
||||
private readonly LocalisationService _localisationService;
|
||||
private readonly QuestConfig _questConfig;
|
||||
|
||||
public QuestHelper(
|
||||
ILogger logger,
|
||||
TimeUtil timeUtil,
|
||||
DatabaseService databaseService,
|
||||
QuestConditionHelper questConditionHelper,
|
||||
ProfileHelper profileHelper)
|
||||
ProfileHelper profileHelper,
|
||||
LocalisationService localisationService,
|
||||
ConfigServer configServer)
|
||||
{
|
||||
_logger = logger;
|
||||
_timeUtil = timeUtil;
|
||||
_databaseService = databaseService;
|
||||
_questConditionHelper = questConditionHelper;
|
||||
_profileHelper = profileHelper;
|
||||
_localisationService = localisationService;
|
||||
|
||||
_questConfig = configServer.GetConfig<QuestConfig>(ConfigTypes.QUEST);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,7 +62,30 @@ public class QuestHelper
|
||||
/// <returns>true if player level is greater than or equal to quest</returns>
|
||||
public bool DoesPlayerLevelFulfilCondition(double playerLevel, QuestCondition condition)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
if (condition.ConditionType == "Level")
|
||||
{
|
||||
var conditionValue = double.Parse(condition.Value.ToString());
|
||||
switch (condition.CompareMethod)
|
||||
{
|
||||
case ">=":
|
||||
return playerLevel >= conditionValue;
|
||||
case ">":
|
||||
return playerLevel > conditionValue;
|
||||
case "<":
|
||||
return playerLevel < conditionValue;
|
||||
case "<=":
|
||||
return playerLevel <= conditionValue;
|
||||
case "=":
|
||||
return playerLevel == conditionValue;
|
||||
default:
|
||||
_logger.Error(
|
||||
_localisationService.GetText("quest-unable_to_find_compare_condition", condition.CompareMethod));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -189,7 +221,20 @@ public class QuestHelper
|
||||
*/
|
||||
public bool QuestIsForOtherSide(string playerSide, string questId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var isUsec = playerSide.ToLower() == "usec";
|
||||
if (isUsec && _questConfig.BearOnlyQuests.Contains(questId))
|
||||
{
|
||||
// Player is usec and quest is bear only, skip
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isUsec && _questConfig.UsecOnlyQuests.Contains(questId))
|
||||
{
|
||||
// Player is bear and quest is usec only, skip
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,11 +496,49 @@ public class QuestHelper
|
||||
/**
|
||||
* Add all quests to a profile with the provided statuses
|
||||
* @param pmcProfile profile to update
|
||||
* @param statuses statuses quests should have
|
||||
* @param statuses statuses quests should have added to profile
|
||||
*/
|
||||
public void AddAllQuestsToProfile(PmcData pmcProfile, List<QuestStatusEnum> statuses)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Iterate over all quests in db
|
||||
var quests = _databaseService.GetQuests();
|
||||
foreach (var (key, questData) in quests) {
|
||||
// Quest from db matches quests in profile, skip
|
||||
if (pmcProfile.Quests.Any((x) => x.QId == questData.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create dict of status to add to quest in profile
|
||||
var statusesDict = new Dictionary<QuestStatusEnum, long>();
|
||||
foreach (var status in statuses)
|
||||
{
|
||||
statusesDict.Add(status, _timeUtil.GetTimeStamp());
|
||||
}
|
||||
|
||||
var questRecordToAdd = new QuestStatus {
|
||||
QId = key,
|
||||
StartTime = _timeUtil.GetTimeStamp(),
|
||||
Status = statuses[^1], // Get last status in list as currently active status
|
||||
StatusTimers = statusesDict,
|
||||
CompletedConditions = [],
|
||||
AvailableAfter = 0,
|
||||
};
|
||||
|
||||
// Check if the quest already exists in the profile
|
||||
var existingQuest = pmcProfile.Quests.FirstOrDefault(x => x.QId == key);
|
||||
if (existingQuest != null)
|
||||
{
|
||||
// Update existing quest
|
||||
existingQuest.Status = questRecordToAdd.Status;
|
||||
existingQuest.StatusTimers = questRecordToAdd.StatusTimers;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new quest to the profile
|
||||
pmcProfile.Quests.Add(questRecordToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FindAndRemoveQuestFromArrayIfExists(string questId, List<QuestStatus> quests)
|
||||
@@ -578,7 +661,7 @@ public class QuestHelper
|
||||
if (conditionToFulfil.AvailableAfter > 0)
|
||||
{
|
||||
// Compare current time to unlock time for previous quest
|
||||
prerequisiteQuest.StatusTimers.TryGetValue(prerequisiteQuest.Status.ToString(), out var previousQuestCompleteTime);
|
||||
prerequisiteQuest.StatusTimers.TryGetValue(prerequisiteQuest.Status.Value, out var previousQuestCompleteTime);
|
||||
var unlockTime = previousQuestCompleteTime + conditionToFulfil.AvailableAfter;
|
||||
if (unlockTime > _timeUtil.GetTimeStamp())
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ public class BotBase
|
||||
|
||||
[JsonPropertyName("aid")]
|
||||
[JsonConverter(typeof(StringToNumberFactoryConverter))]
|
||||
public double? Aid { get; set; }
|
||||
public int? Aid { get; set; }
|
||||
|
||||
/** SPT property - use to store player id - TODO - move to AID ( account id as guid of choice) */
|
||||
[JsonPropertyName("sessionId")]
|
||||
@@ -57,7 +57,7 @@ public class BotBase
|
||||
public Hideout? Hideout { get; set; }
|
||||
|
||||
[JsonPropertyName("Quests")]
|
||||
public List<Quests>? Quests { get; set; }
|
||||
public List<QuestStatus>? Quests { get; set; }
|
||||
|
||||
[JsonPropertyName("TradersInfo")]
|
||||
public Dictionary<string, TraderInfo>? TradersInfo { get; set; }
|
||||
@@ -153,7 +153,7 @@ public class Info
|
||||
public bool? HasCoopExtension { get; set; }
|
||||
public bool? HasPveGame { get; set; }
|
||||
public string? Voice { get; set; }
|
||||
public double? Level { get; set; }
|
||||
public int? Level { get; set; }
|
||||
public double? Experience { get; set; }
|
||||
[JsonConverter(typeof(StringToNumberFactoryConverter))]
|
||||
public long? RegistrationDate { get; set; }
|
||||
@@ -710,7 +710,7 @@ public class Quests
|
||||
public QuestStatusEnum? Status { get; set; }
|
||||
|
||||
[JsonPropertyName("statusTimers")]
|
||||
public Dictionary<string, long>? StatusTimers { get; set; }
|
||||
public Dictionary<QuestStatusEnum, long>? StatusTimers { get; set; }
|
||||
|
||||
/** Property does not exist in live profile data, but is used by ProfileChanges.questsStatus when sent to client */
|
||||
[JsonPropertyName("completedConditions")]
|
||||
|
||||
@@ -130,6 +130,9 @@ public class Quest
|
||||
public double ChangeStandingCost { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as BotBase.Quests
|
||||
/// </summary>
|
||||
public class QuestStatus
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
@@ -139,16 +142,16 @@ public class QuestStatus
|
||||
public string? Uid { get; set; }
|
||||
|
||||
[JsonPropertyName("qid")]
|
||||
public string? Qid { get; set; }
|
||||
public string? QId { get; set; }
|
||||
|
||||
[JsonPropertyName("startTime")]
|
||||
public double? StartTime { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public double? Status { get; set; }
|
||||
public QuestStatusEnum? Status { get; set; }
|
||||
|
||||
[JsonPropertyName("statusTimers")]
|
||||
public Dictionary<string, double>? StatusTimers { get; set; }
|
||||
public Dictionary<QuestStatusEnum, long>? StatusTimers { get; set; }
|
||||
|
||||
[JsonPropertyName("completedConditions")]
|
||||
public List<string>? CompletedConditions { get; set; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Core.Models.Eft.Common.Tables;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class RepeatableQuestStatus
|
||||
public string? Uid { get; set; }
|
||||
|
||||
[JsonPropertyName("qid")]
|
||||
public string? Qid { get; set; }
|
||||
public string? QId { get; set; }
|
||||
|
||||
[JsonPropertyName("startTime")]
|
||||
public long? StartTime { get; set; }
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Core.Models.Eft.Game;
|
||||
public class GameConfigResponse
|
||||
{
|
||||
[JsonPropertyName("aid")]
|
||||
public int? Aid { get; set; }
|
||||
public double? Aid { get; set; }
|
||||
|
||||
[JsonPropertyName("lang")]
|
||||
public string? Language { get; set; }
|
||||
@@ -33,7 +33,7 @@ public class GameConfigResponse
|
||||
|
||||
/** Total in game time */
|
||||
[JsonPropertyName("totalInGame")]
|
||||
public int? TotalInGame { get; set; }
|
||||
public double? TotalInGame { get; set; }
|
||||
|
||||
[JsonPropertyName("reportAvailable")]
|
||||
public bool? IsReportAvailable { get; set; }
|
||||
@@ -73,4 +73,4 @@ public class Backend
|
||||
|
||||
[JsonPropertyName("RagFair")]
|
||||
public string? RagFair { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Enums;
|
||||
|
||||
namespace Core.Models.Eft.Profile;
|
||||
|
||||
/// <summary>
|
||||
/// Identical to `UserDialogInfo`
|
||||
/// </summary>
|
||||
public class SearchFriendResponse
|
||||
{
|
||||
[JsonPropertyName("_id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("aid")]
|
||||
public double? Aid { get; set; }
|
||||
public int? Aid { get; set; }
|
||||
|
||||
[JsonPropertyName("Info")]
|
||||
public FriendInfo? Info { get; set; }
|
||||
}
|
||||
|
||||
// NOTE: Renamed from `Info` because of a name collision.
|
||||
public class FriendInfo
|
||||
{
|
||||
[JsonPropertyName("Nickname")]
|
||||
public string? Nickname { get; set; }
|
||||
|
||||
[JsonPropertyName("Side")]
|
||||
public string? Side { get; set; }
|
||||
|
||||
[JsonPropertyName("Level")]
|
||||
public double? Level { get; set; }
|
||||
|
||||
[JsonPropertyName("MemberCategory")]
|
||||
public MemberCategory? MemberCategory { get; set; }
|
||||
|
||||
[JsonPropertyName("SelectedMemberCategory")]
|
||||
public MemberCategory? SelectedMemberCategory { get; set; }
|
||||
public UserDialogDetails? Info { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Enums;
|
||||
|
||||
namespace Core.Models.Eft.Profile;
|
||||
|
||||
public class UserDialogInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// _id
|
||||
/// </summary>
|
||||
[JsonPropertyName("_id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
@@ -31,4 +34,4 @@ public class UserDialogDetails
|
||||
|
||||
[JsonPropertyName("SelectedMemberCategory")]
|
||||
public MemberCategory? SelectedMemberCategory { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public class SptHttpListener : IHttpListener
|
||||
if (IsDebugRequest(req)) {
|
||||
// Send only raw response without transformation
|
||||
SendJson(resp, output, sessionID);
|
||||
Console.WriteLine($"Response: {output}");
|
||||
// Console.WriteLine($"Response: {output}");
|
||||
// TODO: this.logRequest(req, output);
|
||||
return;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class SptHttpListener : IHttpListener
|
||||
// No serializer can handle the request (majority of requests dont), zlib the output and send response back
|
||||
SendZlibJson(resp, output, sessionID);
|
||||
}
|
||||
Console.WriteLine($"Response: {output}");
|
||||
// Console.WriteLine($"Response: {output}");
|
||||
// TODO: this.LogRequest(req, output);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
using Core.Annotations;
|
||||
using Core.Utils;
|
||||
|
||||
namespace Core.Services;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class ProfileActivityService
|
||||
{
|
||||
private TimeUtil _timeUtil;
|
||||
private Dictionary<string, long> profileActivityTimestamps = new();
|
||||
|
||||
public ProfileActivityService(TimeUtil timeUtil)
|
||||
{
|
||||
_timeUtil = timeUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was the requested profile active in the last requested minutes
|
||||
* @param sessionId Profile to check
|
||||
@@ -13,7 +22,13 @@ public class ProfileActivityService
|
||||
*/
|
||||
public bool ActiveWithinLastMinutes(string sessionId, int minutes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var currentTimestamp = _timeUtil.GetTimeStamp();
|
||||
var storedActivityTimestamp = profileActivityTimestamps[sessionId];
|
||||
|
||||
if (storedActivityTimestamp != null)
|
||||
return false;
|
||||
|
||||
return currentTimestamp - storedActivityTimestamp < minutes * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +38,21 @@ public class ProfileActivityService
|
||||
*/
|
||||
public List<string> GetActiveProfileIdsWithinMinutes(int minutes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var currentTimestamp = _timeUtil.GetTimeStamp();
|
||||
var result = new List<string>();
|
||||
|
||||
foreach (var activity in profileActivityTimestamps ?? new())
|
||||
{
|
||||
var lastActivityTimestamp = activity.Value;
|
||||
if (lastActivityTimestamp == null)
|
||||
continue;
|
||||
|
||||
// Profile was active in last x minutes, add to return list
|
||||
if (currentTimestamp - lastActivityTimestamp < minutes * 60)
|
||||
result.Add(activity.Key);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,6 +61,6 @@ public class ProfileActivityService
|
||||
*/
|
||||
public void SetActivityTimestamp(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
profileActivityTimestamps[sessionId] = _timeUtil.GetTimeStamp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ public class JsonUtil
|
||||
private static readonly JsonSerializerOptions jsonSerializerOptionsNoIndent = new()
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
|
||||
Converters =
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user