Merge pull request #44 from CWXDEV/main

more bits
This commit is contained in:
clodanSPT
2025-01-12 19:27:50 +00:00
committed by GitHub
10 changed files with 474 additions and 51 deletions
+285 -30
View File
@@ -1,25 +1,71 @@
using Core.Annotations;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Customization;
using Core.Models.Eft.Hideout;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
using Core.Routers;
using Core.Servers;
using Core.Services;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
using Product = Core.Models.Eft.ItemEvent.Product;
namespace Core.Controllers;
[Injectable]
public class CustomizationController
{
protected ILogger _logger;
protected EventOutputHolder _eventOutputHolder;
protected DatabaseService _databaseService;
protected SaveServer _saveServer;
protected LocalisationService _localisationService;
protected ProfileHelper _profileHelper;
protected ICloner _cloner;
public CustomizationController
(
ILogger logger,
EventOutputHolder eventOutputHolder,
DatabaseService databaseService,
SaveServer saveServer,
LocalisationService localisationService,
ProfileHelper profileHelper,
ICloner cloner
)
{
_logger = logger;
_eventOutputHolder = eventOutputHolder;
_databaseService = databaseService;
_saveServer = saveServer;
_localisationService = localisationService;
_profileHelper = profileHelper;
_cloner = cloner;
}
/// <summary>
/// Get purchasable clothing items from trader that match players side (usec/bear)
/// </summary>
/// <param name="traderId">trader to look up clothing for</param>
/// <param name="sessionId">Session id</param>
/// <returns>Suit array</returns>
public Suit GetTraderSuits(string traderId, string sessionId)
public List<Suit> GetTraderSuits(string traderId, string sessionId)
{
throw new NotImplementedException();
var pmcData = _profileHelper.GetPmcProfile(sessionId);
var clothing = _databaseService.GetCustomization();
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();
if (matchingSuits == null)
throw new Exception(_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
return matchingSuits;
}
/// <summary>
@@ -35,7 +81,41 @@ public class CustomizationController
BuyClothingRequestData buyClothingRequest,
string sessionId)
{
throw new NotImplementedException();
var output = _eventOutputHolder.GetOutput(sessionId);
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;
if (OutfitAlreadyPurchased(suitId, sessionId))
{
var suitDetails = _databaseService.GetCustomization()[suitId];
_logger.Error(_localisationService.GetText("customisation-item_already_purchased", new
{
ItemId = suitDetails.Id,
ItemName = suitDetails.Name,
}));
}
return output;
}
private bool OutfitAlreadyPurchased(object suitId, string sessionId)
{
return _saveServer.GetProfile(sessionId).Suits.Contains(suitId);
}
private Suit GetTraderClothingOffer(string sessionId, string? 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));
return foundSuit;
}
/// <summary>
@@ -45,13 +125,13 @@ public class CustomizationController
/// <param name="pmcData">Player profile</param>
/// <param name="itemsToPayForClothingWith">Clothing purchased</param>
/// <param name="output">Client response</param>
private void PayForClothingItems(
string sessionId,
PmcData pmcData,
List<PaymentItemForClothing> itemsToPayForClothingWith,
private void PayForClothingItems(string sessionId, PmcData pmcData, List<PaymentItemForClothing> itemsToPayForClothingWith,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
foreach (var inventoryItemToProcess in itemsToPayForClothingWith)
{
PayForClothingItem(sessionId, pmcData, inventoryItemToProcess, output);
}
}
/// <summary>
@@ -61,13 +141,65 @@ public class CustomizationController
/// <param name="pmcData">Player profile</param>
/// <param name="paymentItemDetails">Payment details</param>
/// <param name="output">Client response</param>
private void PayForClothingItem(
string sessionId,
PmcData pmcData,
PaymentItemForClothing paymentItemDetails,
ItemEventRouterResponse output)
private void PayForClothingItem(string sessionId, PmcData pmcData, PaymentItemForClothing paymentItemDetails, ItemEventRouterResponse output)
{
throw new NotImplementedException();
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)
{
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
});
pmcData.Inventory.Items.Remove(inventoryItem);
}
if (inventoryItem.Upd == null)
inventoryItem.Upd = new() { StackObjectsCount = 1 };
if (inventoryItem.Upd.StackObjectsCount == null)
inventoryItem.Upd.StackObjectsCount = 1;
if (inventoryItem.Upd.StackObjectsCount == paymentItemDetails.Count)
{
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
});
pmcData.Inventory.Items.Remove(inventoryItem);
return;
}
if (inventoryItem.Upd.StackObjectsCount > paymentItemDetails.Count)
{
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
});
}
}
/// <summary>
@@ -77,7 +209,16 @@ public class CustomizationController
/// <returns></returns>
private List<Suit> GetAllTraderSuits(string sessionId)
{
throw new NotImplementedException();
var traders = _databaseService.GetTraders();
var result = new List<Suit>();
foreach (var trader in traders)
{
if (trader.Value.Base.CustomizationSeller.Value)
result.AddRange(GetTraderSuits(trader.Key, sessionId));
}
return result;
}
/// <summary>
@@ -86,11 +227,9 @@ public class CustomizationController
/// <param name="sessionId"></param>
/// <param name="info"></param>
/// <returns></returns>
public HideoutCustomisation GetHideoutCustomisation(
string sessionId,
EmptyRequestData info)
public HideoutCustomisation GetHideoutCustomisation(string sessionId, EmptyRequestData info)
{
throw new NotImplementedException();
return _databaseService.GetHideout().Customisation;
}
/// <summary>
@@ -103,7 +242,79 @@ public class CustomizationController
string sessionId,
EmptyRequestData info)
{
throw new NotImplementedException();
var customisationResultsClone = _cloner.Clone(_databaseService.GetTemplates().CustomisationStorage);
var profile = _profileHelper.GetFullProfile(sessionId);
switch (GetGameEdition(profile))
{
case GameEditions.EDGE_OF_DARKNESS:
customisationResultsClone.Add(new ()
{
Id = "6746fd09bafff85008048838",
Source = "default",
Type = "dogTag"
});
customisationResultsClone.Add(new ()
{
Id = "67471938bafff850080488b7",
Source = "default",
Type = "dogTag"
});
break;
case GameEditions.UNHEARD:
customisationResultsClone.Add(new ()
{
Id = "6746fd09bafff85008048838",
Source = "default",
Type = "dogTag"
});
customisationResultsClone.Add(new ()
{
Id = "67471938bafff850080488b7",
Source = "default",
Type = "dogTag"
});
customisationResultsClone.Add(new ()
{
Id = "67471928d17d6431550563b5",
Source = "default",
Type = "dogTag"
});
customisationResultsClone.Add(new ()
{
Id = "6747193f170146228c0d2226",
Source = "default",
Type = "dogTag"
});
break;
default:
throw new Exception($"Unknown game edition given from profile {profile}");
}
var prestigeLevel = profile.CharacterData.PmcData.Info.PrestigeLevel;
if (prestigeLevel != null)
{
if (prestigeLevel >= 1)
{
customisationResultsClone.Add(new ()
{
Id = "674dbf593bee1152d407f005",
Source = "default",
Type = "dogTag"
});
}
if (prestigeLevel >= 2)
{
customisationResultsClone.Add(new ()
{
Id = "675dcfea7ae1a8792107ca99",
Source = "default",
Type = "dogTag"
});
}
}
return customisationResultsClone;
}
/// <summary>
@@ -113,7 +324,22 @@ public class CustomizationController
/// <returns></returns>
private string GetGameEdition(SptProfile profile)
{
throw new NotImplementedException();
var edition = profile.CharacterData.PmcData.Info.GameVersion;
if (edition == null)
{
var launcherEdition = profile.ProfileInfo.Edition;
switch (launcherEdition.ToLower())
{
case "edge of darkness":
return GameEditions.EDGE_OF_DARKNESS;
case "unheard":
return GameEditions.UNHEARD;
default:
return GameEditions.STANDARD;
}
}
return edition;
}
/// <summary>
@@ -123,12 +349,25 @@ public class CustomizationController
/// <param name="request"></param>
/// <param name="pmcData"></param>
/// <returns></returns>
public ItemEventRouterResponse SetClothing(
string sessionId,
CustomizationSetRequest request,
PmcData pmcData)
public ItemEventRouterResponse SetClothing(string sessionId, CustomizationSetRequest request, PmcData pmcData)
{
throw new NotImplementedException();
foreach (var customisation in request.Customizations)
{
switch (customisation.Id)
{
case "dogTag":
pmcData.Customization.DogTag = customisation.Id;
break;
case "suite":
ApplyClothingItemToProfile(customisation, pmcData);
break;
default:
_logger.Error($"Unhandled customisation type: {customisation.Type}");
break;
}
}
return _eventOutputHolder.GetOutput(sessionId);
}
/// <summary>
@@ -136,10 +375,26 @@ public class CustomizationController
/// </summary>
/// <param name="customization">Suit to apply to profile</param>
/// <param name="pmcData">Profile to update</param>
private void ApplyClothingItemToProfile(
CustomizationSetOption customization,
PmcData pmcData)
private void ApplyClothingItemToProfile(CustomizationSetOption customisation, PmcData pmcData)
{
throw new NotImplementedException();
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}");
return;
}
if (dbSuit.Parent == "5cd944d01388ce000a659df9")
{
pmcData.Customization.Body = dbSuit.Properties.Body;
pmcData.Customization.Hands = dbSuit.Properties.Hands;
return;
}
if (dbSuit.Parent == "5cd944ca1388ce03a44dc2a4")
{
pmcData.Customization.Feet = dbSuit.Properties.Feet;
}
}
}
+163 -9
View File
@@ -1,10 +1,15 @@
using Core.Annotations;
using Core.Context;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Game;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Controllers;
@@ -12,19 +17,86 @@ namespace Core.Controllers;
[Injectable]
public class GameController
{
private readonly ConfigServer _configServer;
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;
private readonly RandomUtil _randomUtil;
private readonly HideoutHelper _hideoutHelper;
private readonly ProfileHelper _profileHelper;
private readonly ProfileFixerService _profileFixerService;
private readonly LocalisationService _localisationService;
private readonly PostDbLoadService _postDbLoadService;
private readonly CustomLocationWaveService _customLocationWaveService;
private readonly OpenZoneService _openZoneService;
private readonly SeasonalEventService _seasonalEventService;
private readonly ItemBaseClassService _itemBaseClassService;
private readonly GiftService _giftService;
private readonly RaidTimeAdjustmentService _raidTimeAdjustmentService;
private readonly ProfileActivityService _profileActivityService;
private readonly ApplicationContext _applicationContext;
private readonly ICloner _cloner;
private readonly CoreConfig _coreConfig;
private readonly HttpConfig _httpConfig;
private readonly RagfairConfig _ragfairConfig;
private readonly HideoutConfig _hideoutConfig;
private readonly BotConfig _botConfig;
public GameController(
ILogger logger,
ConfigServer configServer)
ConfigServer configServer,
DatabaseService databaseService,
TimeUtil timeUtil,
HttpServerHelper httpServerHelper,
InventoryHelper inventoryHelper,
RandomUtil randomUtil,
HideoutHelper hideoutHelper,
ProfileHelper profileHelper,
ProfileFixerService profileFixerService,
LocalisationService localisationService,
PostDbLoadService postDbLoadService,
CustomLocationWaveService customLocationWaveService,
OpenZoneService openZoneService,
SeasonalEventService seasonalEventService,
ItemBaseClassService itemBaseClassService,
GiftService giftService,
RaidTimeAdjustmentService raidTimeAdjustmentService,
ProfileActivityService profileActivityService,
ApplicationContext applicationContext,
ICloner cloner
)
{
_logger = logger;
_configServer = configServer;
_databaseService = databaseService;
_timeUtil = timeUtil;
_httpServerHelper = httpServerHelper;
_inventoryHelper = inventoryHelper;
_randomUtil = randomUtil;
_hideoutHelper = hideoutHelper;
_profileHelper = profileHelper;
_profileFixerService = profileFixerService;
_localisationService = localisationService;
_postDbLoadService = postDbLoadService;
_customLocationWaveService = customLocationWaveService;
_openZoneService = openZoneService;
_seasonalEventService = seasonalEventService;
_itemBaseClassService = itemBaseClassService;
_giftService = giftService;
_raidTimeAdjustmentService = raidTimeAdjustmentService;
_profileActivityService = profileActivityService;
_applicationContext = applicationContext;
_cloner = cloner;
_coreConfig = configServer.GetConfig<CoreConfig>(ConfigTypes.CORE);
_httpConfig = configServer.GetConfig<HttpConfig>(ConfigTypes.HTTP);
_ragfairConfig = configServer.GetConfig<RagfairConfig>(ConfigTypes.RAGFAIR);
_hideoutConfig = configServer.GetConfig<HideoutConfig>(ConfigTypes.HIDEOUT);
_botConfig = configServer.GetConfig<BotConfig>(ConfigTypes.BOT);
}
/// <summary>
@@ -34,11 +106,93 @@ public class GameController
/// <param name="info"></param>
/// <param name="sessionId"></param>
/// <param name="startTimeStampMs"></param>
public void GameStart(
string url,
EmptyRequestData info,
string sessionId,
long startTimeStampMs)
public void GameStart(string url, EmptyRequestData info, string sessionId, long startTimeStampMs)
{
// 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
if (sessionId != null)
{
var fullProfile = _profileHelper.GetFullProfile(sessionId);
if (fullProfile.ProfileInfo.IsWiped.Value)
return;
if (fullProfile.SptData.Migrations == null)
fullProfile.SptData.Migrations = new();
if (fullProfile.FriendProfileIds == null)
fullProfile.FriendProfileIds = new();
if (fullProfile.SptData.Version.Contains("3.9.") && !fullProfile.SptData.Migrations.Any(m => m.Key == "39x"))
{
_inventoryHelper.ValidateInventoryUsesMongoIds(fullProfile.CharacterData.PmcData.Inventory.Items);
Migrate39xProfile(fullProfile);
// flag as migrated
fullProfile.SptData.Migrations.Add("39x", _timeUtil.GetTimeStamp());
_logger.Success($"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 = {};
// }
//
// 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);
if (pmcProfile.Inventory != null)
{
SendPraporGiftsToNewProfiles(pmcProfile);
_profileFixerService.CheckForOrphanedModdedItems(sessionId, fullProfile);
}
_profileFixerService.CheckForAndRemoveInvalidTraders(fullProfile);
_profileFixerService.CheckForAndFixPmcProfileIssues(pmcProfile);
if (pmcProfile.Hideout != null)
{
_profileFixerService.AddMissingHideoutBonusesToProfile(pmcProfile);
_hideoutHelper.SetHideoutImprovementsToCompleted(pmcProfile);
_hideoutHelper.UnlockHideoutWallInProfile(pmcProfile);
}
LogProfileDetails(fullProfile);
SaveActiveModsToProfile(fullProfile);
if (pmcProfile.Info != null)
{
AddPlayerToPmcNames(pmcProfile);
CheckForAndRemoveUndefinedDialogues(fullProfile);
}
if (pmcProfile.Skills.Common != null)
WarnOnActiveBotReloadSkill(pmcProfile);
_seasonalEventService.GivePlayerSeasonalGifts(sessionId);
}
}
private void Migrate39xProfile(SptProfile fullProfile)
{
throw new NotImplementedException();
}
@@ -210,6 +364,6 @@ public class GameController
public void Load()
{
return; // TODO: actually implement
_postDbLoadService.PerformPostDbLoadActions();
}
}
+4 -4
View File
@@ -106,7 +106,7 @@ public class LauncherController
{
foreach (var sessionID in _saveServer.GetProfiles()) {
var account = _saveServer.GetProfile(sessionID.Key).ProfileInfo;
if (info.Username == account.UserName) {
if (info.Username == account.Username) {
return sessionID.Key;
}
}
@@ -117,7 +117,7 @@ public class LauncherController
public string Register(RegisterData info)
{
foreach (var sessionID in _saveServer.GetProfiles()) {
if (info.Username == _saveServer.GetProfile(sessionID.Key).ProfileInfo.UserName) {
if (info.Username == _saveServer.GetProfile(sessionID.Key).ProfileInfo.Username) {
return "";
}
}
@@ -133,7 +133,7 @@ public class LauncherController
ProfileId = profileId,
ScavengerId = scavId,
Aid = _hashUtil.GenerateAccountId(),
UserName = info.Username,
Username = info.Username,
Password = info.Password,
IsWiped = true,
Edition = info.Edition,
@@ -167,7 +167,7 @@ public class LauncherController
var sessionID = Login(info);
if (!string.IsNullOrEmpty(sessionID)) {
_saveServer.GetProfile(sessionID).ProfileInfo.UserName = info.Change;
_saveServer.GetProfile(sessionID).ProfileInfo.Username = info.Change;
}
return sessionID;
+3 -3
View File
@@ -107,7 +107,7 @@ public class ProfileController
{
return new MiniProfile()
{
Username = profile.ProfileInfo?.UserName ?? "",
Username = profile.ProfileInfo?.Username ?? "",
Nickname = "unknown",
Side = "unknown",
CurrentLevel = 0,
@@ -125,7 +125,7 @@ public class ProfileController
var nextlvl = _profileHelper.GetExperience((int)(currlvl + 1));
return new MiniProfile()
{
Username = profile.ProfileInfo.UserName,
Username = profile.ProfileInfo.Username,
Nickname = pmc.Info.Nickname,
Side = pmc.Info.Side,
CurrentLevel = (int)(pmc.Info.Level),
@@ -167,7 +167,7 @@ public class ProfileController
pmcData.Savage = account.ScavengerId;
pmcData.SessionId = sessionID;
pmcData.Info.Nickname = info.Nickname;
pmcData.Info.LowerNickname = account.UserName.ToLower();
pmcData.Info.LowerNickname = account.Username.ToLower();
pmcData.Info.RegistrationDate = _timeUtil.GetTimeStamp();
pmcData.Info.Voice = _databaseService.GetCustomization()[info.VoiceId].Name;
pmcData.Stats = _profileHelper.GetDefaultCounters();
+2 -2
View File
@@ -77,12 +77,12 @@ public class PresetHelper
_itemHelper.ArmorItemCanHoldMods(p.Value.Encyclopedia)).ToDictionary();
}
return _defaultWeaponPresets;
return _defaultEquipmentPresets;
}
public bool IsPreset(string id)
{
throw new NotImplementedException();
return _databaseService.GetGlobals().ItemPresets.ContainsKey(id);
}
/**
+1 -1
View File
@@ -23,7 +23,7 @@ public class Item
public string? Desc { get; set; }
[JsonPropertyName("upd")]
public Upd? Update { get; set; }
public Upd? Upd { get; set; }
}
public class ItemLocation
@@ -1350,6 +1350,9 @@ public class Props
[JsonPropertyName("Hands")]
public string? Hands { get; set; }
[JsonPropertyName("Feet")]
public string? Feet { get; set; }
[JsonExtensionData]
public Dictionary<string, object> OtherProperties { get; set; }
}
+1 -1
View File
@@ -71,7 +71,7 @@ public class Info
public int? Aid { get; set; }
[JsonPropertyName("username")]
public string? UserName { get; set; }
public string? Username { get; set; }
[JsonPropertyName("password")]
public string? Password { get; set; }
+11
View File
@@ -0,0 +1,11 @@
namespace Core.Models.Enums;
public class GameEditions
{
public const string STANDARD = "standard";
public const string LEFT_BEHIND = "left_behind";
public const string PREPARE_FOR_ESCAPE = "prepare_for_escape";
public const string EDGE_OF_DARKNESS = "edge_of_darkness";
public const string UNHEARD = "unheard_edition";
public const string TOURNAMENT = "tournament_live";
}
@@ -18,7 +18,7 @@ public class CustomizationDynamicRouter : DynamicRouter
jsonUtil,
[
new RouteAction(
"",
"/client/trading/customization/",
(
url,
info,