From 6e345193bc49808cae5f6e65f5f1a768443d0f31 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sun, 25 Aug 2024 09:34:31 -0400 Subject: [PATCH 1/8] BankSystem: cache bank accounts --- Content.Server/_NF/Bank/BankSystem.cs | 60 +++++++++++++++++---------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index ebef827959d..d6c2ff79351 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -21,6 +21,9 @@ public sealed partial class BankSystem : EntitySystem private ISawmill _log = default!; + // Cached bank accounts + private Dictionary _cachedBankAccounts = new(); + public override void Initialize() { base.Initialize(); @@ -31,18 +34,47 @@ public override void Initialize() InitializeStationATM(); } - // To ensure that bank account data gets saved, we are going to update the db every time the component changes - // I at first wanted to try to reduce database calls, however notafet suggested I just do it every time the account changes - // TODO: stop it from running 5 times every time + // This could use a refactor into a BankAccountManager that handles your caching. private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, ref ComponentGetState args) { var user = args.Player?.UserId; if (user == null || args.Player?.AttachedEntity != mobUid) { + // The person reading this isn't the controller of the character. + // Never update - return cached value if it exists, otherwise trust the data we receive. + int balance = bank.Balance; + var earlyUserId = args.Player?.UserId; + if (earlyUserId is not null && _cachedBankAccounts.ContainsKey(earlyUserId.Value)) + { + balance = _cachedBankAccounts[earlyUserId.Value]; + } + args.State = new BankAccountComponentState + { + Balance = balance + }; return; } + var userId = user = user.Value; + + // Regardless of what happens, the given balance will be the returned state. + // Possible desync with database if character is the wrong type. + args.State = new BankAccountComponentState + { + Balance = bank.Balance + }; + + // Check if value is in cache. + if (_cachedBankAccounts.ContainsKey(userId.Value)) + { + // Our cached value matches the request, nothing to do. + if (_cachedBankAccounts[userId.Value] == bank.Balance) + { + return; + } + } + // Mismatched or missing value in cache. Update DB & cache new value. var prefs = _prefsManager.GetPreferences((NetUserId) user); var character = prefs.SelectedCharacter; var index = prefs.IndexOfCharacter(character); @@ -52,26 +84,8 @@ private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, r return; } - var newProfile = new HumanoidCharacterProfile( - profile.Name, - profile.FlavorText, - profile.Species, - profile.Age, - profile.Sex, - profile.Gender, - bank.Balance, - profile.Appearance, - profile.SpawnPriority, - new Dictionary, JobPriority>(profile.JobPriorities), // Frontier Merge - profile.PreferenceUnavailable, - new HashSet>(profile.AntagPreferences), // Frontier Merge - new HashSet>(profile.TraitPreferences), // Frontier Merge - new Dictionary(profile.Loadouts)); - - args.State = new BankAccountComponentState - { - Balance = bank.Balance, - }; + var newProfile = profile.WithBankBalance(bank.Balance); + _cachedBankAccounts[userId.Value] = bank.Balance; _dbManager.SaveCharacterSlotAsync((NetUserId) user, newProfile, index); _log.Info($"Character {profile.Name} saved"); From 2cb04afd651a3ce17c2e1e2c72ac98b352439c80 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Tue, 27 Aug 2024 10:50:08 -0400 Subject: [PATCH 2/8] BankSystem: fix cache lookup player --- Content.Server/_NF/Bank/BankSystem.cs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index d6c2ff79351..85e4f9b943e 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -11,6 +11,7 @@ using Robust.Shared.Prototypes; using Content.Shared.Roles; using Content.Shared.Traits; +using Robust.Shared.Player; namespace Content.Server.Bank; @@ -18,10 +19,10 @@ public sealed partial class BankSystem : EntitySystem { [Dependency] private readonly IServerPreferencesManager _prefsManager = default!; [Dependency] private readonly IServerDbManager _dbManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; private ISawmill _log = default!; - // Cached bank accounts private Dictionary _cachedBankAccounts = new(); public override void Initialize() @@ -44,10 +45,10 @@ private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, r // The person reading this isn't the controller of the character. // Never update - return cached value if it exists, otherwise trust the data we receive. int balance = bank.Balance; - var earlyUserId = args.Player?.UserId; - if (earlyUserId is not null && _cachedBankAccounts.ContainsKey(earlyUserId.Value)) + if (_playerManager.TryGetSessionByEntity(mobUid, out var session) && + _cachedBankAccounts.ContainsKey(session.UserId)) { - balance = _cachedBankAccounts[earlyUserId.Value]; + balance = _cachedBankAccounts[session.UserId]; } args.State = new BankAccountComponentState { @@ -55,7 +56,7 @@ private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, r }; return; } - var userId = user = user.Value; + var userId = user.Value; // Regardless of what happens, the given balance will be the returned state. // Possible desync with database if character is the wrong type. @@ -65,17 +66,17 @@ private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, r }; // Check if value is in cache. - if (_cachedBankAccounts.ContainsKey(userId.Value)) + if (_cachedBankAccounts.ContainsKey(userId)) { // Our cached value matches the request, nothing to do. - if (_cachedBankAccounts[userId.Value] == bank.Balance) + if (_cachedBankAccounts[userId] == bank.Balance) { return; } } // Mismatched or missing value in cache. Update DB & cache new value. - var prefs = _prefsManager.GetPreferences((NetUserId) user); + var prefs = _prefsManager.GetPreferences(userId); var character = prefs.SelectedCharacter; var index = prefs.IndexOfCharacter(character); @@ -85,9 +86,9 @@ private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, r } var newProfile = profile.WithBankBalance(bank.Balance); - _cachedBankAccounts[userId.Value] = bank.Balance; + _cachedBankAccounts[userId] = bank.Balance; - _dbManager.SaveCharacterSlotAsync((NetUserId) user, newProfile, index); + _dbManager.SaveCharacterSlotAsync((NetUserId) userId, newProfile, index); _log.Info($"Character {profile.Name} saved"); } @@ -119,7 +120,7 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) bank.Balance -= amount; _log.Info($"{mobUid} withdrew {amount}"); - Dirty(bank); + Dirty(mobUid, bank); return true; } @@ -145,7 +146,7 @@ public bool TryBankDeposit(EntityUid mobUid, int amount) bank.Balance += amount; _log.Info($"{mobUid} deposited {amount}"); - Dirty(bank); + Dirty(mobUid, bank); return true; } From 880ec2ce4c2811ffce81f013c62da52b631cc39a Mon Sep 17 00:00:00 2001 From: Whatstone Date: Wed, 28 Aug 2024 10:43:34 -0400 Subject: [PATCH 3/8] BankSystem rewrite --- Content.Client/_NF/Bank/BankSystem.cs | 6 + Content.Server/Cloning/CloningSystem.cs | 7 +- Content.Server/Species/Systems/NymphSystem.cs | 10 +- .../Station/Systems/StationSpawningSystem.cs | 3 +- Content.Server/_NF/Bank/BankSystem.cs | 190 ++++++++++-------- .../Species/Systems/ReformSystem.cs | 10 +- .../Bank/Components/BankAccountComponent.cs | 9 +- Content.Shared/_NF/Bank/SharedBankSystem.cs | 13 +- 8 files changed, 125 insertions(+), 123 deletions(-) create mode 100644 Content.Client/_NF/Bank/BankSystem.cs diff --git a/Content.Client/_NF/Bank/BankSystem.cs b/Content.Client/_NF/Bank/BankSystem.cs new file mode 100644 index 00000000000..699d3b258a6 --- /dev/null +++ b/Content.Client/_NF/Bank/BankSystem.cs @@ -0,0 +1,6 @@ +using Content.Shared.Bank; + +namespace Content.Client.Bank; + +// Shared is abstract. +public sealed partial class BankSystem : SharedBankSystem; diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index a3f7e685db5..8114604ee3c 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -253,11 +253,10 @@ public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Entity(bodyToClone, out var bank)) + // Frontier: bank account transfer + if (HasComp(bodyToClone)) { - var bankComp = EnsureComp(mob); - bankComp.Balance = bank.Balance; + EnsureComp(mob); } // Frontier diff --git a/Content.Server/Species/Systems/NymphSystem.cs b/Content.Server/Species/Systems/NymphSystem.cs index 302854686ef..259aa42f9a3 100644 --- a/Content.Server/Species/Systems/NymphSystem.cs +++ b/Content.Server/Species/Systems/NymphSystem.cs @@ -51,14 +51,10 @@ private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, ref OrganRemo // Frontier EnsureComp(nymph); - // Frontier - // bank account transfer - if (TryComp(args.OldBody, out var bank)) + // Frontier: bank account transfer + if (HasComp(args.OldBody)) { - // Do this carefully since changing the value of a bank account component on a entity will save the balance immediately through subscribers. - var oldBankBalance = bank.Balance; - var newBank = EnsureComp(nymph); - newBank.Balance = oldBankBalance; + EnsureComp(nymph); } } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 7dfda170c3c..830f48b936a 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -284,8 +284,7 @@ public EntityUid SpawnPlayerMob( if (prototype?.StartingGear is not null) EquipStartingGear(entity.Value, prototype.StartingGear, raiseEvent: false); - var bank = EnsureComp(entity.Value); - bank.Balance = bankBalance; + EnsureComp(entity.Value); } var gearEquippedEv = new StartingGearEquippedEvent(entity.Value); diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index 85e4f9b943e..b61dd6d7a7d 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -1,104 +1,92 @@ using System.Threading; -using Content.Server.Database; using Content.Server.Preferences.Managers; using Content.Server.GameTicking; +using Content.Shared.Bank; using Content.Shared.Bank.Components; using Content.Shared.Preferences; -using Robust.Shared.GameStates; -using Robust.Shared.Network; -using Content.Server.Cargo.Components; -using Content.Shared.Preferences.Loadouts; -using Robust.Shared.Prototypes; -using Content.Shared.Roles; -using Content.Shared.Traits; using Robust.Shared.Player; namespace Content.Server.Bank; -public sealed partial class BankSystem : EntitySystem +public sealed partial class BankSystem : SharedBankSystem { [Dependency] private readonly IServerPreferencesManager _prefsManager = default!; - [Dependency] private readonly IServerDbManager _dbManager = default!; [Dependency] private readonly ISharedPlayerManager _playerManager = default!; private ISawmill _log = default!; - private Dictionary _cachedBankAccounts = new(); - public override void Initialize() { base.Initialize(); _log = Logger.GetSawmill("bank"); - SubscribeLocalEvent(OnBankAccountChanged); - SubscribeLocalEvent(OnPlayerLobbyJoin); InitializeATM(); InitializeStationATM(); + + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnPlayerLobbyJoin); } - // This could use a refactor into a BankAccountManager that handles your caching. - private void OnBankAccountChanged(EntityUid mobUid, BankAccountComponent bank, ref ComponentGetState args) + /// + /// Attempts to remove money from a character's bank account. This should always be used instead of attempting to modify the bankaccountcomponent directly + /// + /// The UID that the bank account is attached to, typically the player controlled mob + /// The integer amount of which to decrease the bank account + /// true if the transaction was successful, false if it was not + public bool TryBankWithdraw(EntityUid mobUid, int amount) { - var user = args.Player?.UserId; + if (amount <= 0) + { + _log.Info($"{amount} is invalid"); + return false; + } - if (user == null || args.Player?.AttachedEntity != mobUid) + if (!TryComp(mobUid, out var bank)) { - // The person reading this isn't the controller of the character. - // Never update - return cached value if it exists, otherwise trust the data we receive. - int balance = bank.Balance; - if (_playerManager.TryGetSessionByEntity(mobUid, out var session) && - _cachedBankAccounts.ContainsKey(session.UserId)) - { - balance = _cachedBankAccounts[session.UserId]; - } - args.State = new BankAccountComponentState - { - Balance = balance - }; - return; + _log.Info($"{mobUid} has no bank account"); + return false; } - var userId = user.Value; - // Regardless of what happens, the given balance will be the returned state. - // Possible desync with database if character is the wrong type. - args.State = new BankAccountComponentState + if (!_playerManager.TryGetSessionByEntity(mobUid, out var session) || + !_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { - Balance = bank.Balance - }; + _log.Info($"{mobUid} has no cached prefs"); + return false; + } - // Check if value is in cache. - if (_cachedBankAccounts.ContainsKey(userId)) + if (prefs.SelectedCharacter is not HumanoidCharacterProfile profile) { - // Our cached value matches the request, nothing to do. - if (_cachedBankAccounts[userId] == bank.Balance) - { - return; - } + _log.Info($"{mobUid} has the wrong prefs type"); + return false; } - // Mismatched or missing value in cache. Update DB & cache new value. - var prefs = _prefsManager.GetPreferences(userId); - var character = prefs.SelectedCharacter; - var index = prefs.IndexOfCharacter(character); + int balance = profile.BankBalance; - if (character is not HumanoidCharacterProfile profile) + if (balance < amount) { - return; + _log.Info($"{mobUid} has insufficient funds"); + return false; } - var newProfile = profile.WithBankBalance(bank.Balance); - _cachedBankAccounts[userId] = bank.Balance; + balance -= amount; - _dbManager.SaveCharacterSlotAsync((NetUserId) userId, newProfile, index); - _log.Info($"Character {profile.Name} saved"); + var newProfile = profile.WithBankBalance(balance); + var index = prefs.IndexOfCharacter(prefs.SelectedCharacter); + _prefsManager.SetProfile(session.UserId, index, newProfile); + + bank.Balance = balance; + Dirty(mobUid, bank); + _log.Info($"{mobUid} withdrew {amount}"); + return true; } /// - /// Attempts to remove money from a character's bank account. This should always be used instead of attempting to modify the bankaccountcomponent directly + /// Attempts to add money to a character's bank account. This should always be used instead of attempting to modify the bankaccountcomponent directly /// - /// The UID that the bank account is attached to, typically the player controlled mob - /// The integer amount of which to decrease the bank account + /// The UID that the bank account is connected to, typically the player controlled mob + /// The amount of spesos to remove from the bank account /// true if the transaction was successful, false if it was not - public bool TryBankWithdraw(EntityUid mobUid, int amount) + public bool TryBankDeposit(EntityUid mobUid, int amount) { if (amount <= 0) { @@ -112,59 +100,91 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) return false; } - if (bank.Balance < amount) + if (!_playerManager.TryGetSessionByEntity(mobUid, out var session) || + !_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { - _log.Info($"{mobUid} has insufficient funds"); + _log.Info($"{mobUid} has no cached prefs"); return false; } - bank.Balance -= amount; - _log.Info($"{mobUid} withdrew {amount}"); + if (prefs.SelectedCharacter is not HumanoidCharacterProfile profile) + { + _log.Info($"{mobUid} has the wrong prefs type"); + return false; + } + + int balance = profile.BankBalance + amount; + + var newProfile = profile.WithBankBalance(balance); + var index = prefs.IndexOfCharacter(prefs.SelectedCharacter); + _prefsManager.SetProfile(session.UserId, index, newProfile); + + bank.Balance = balance; Dirty(mobUid, bank); + _log.Info($"{mobUid} deposited {amount}"); return true; } /// /// Attempts to add money to a character's bank account. This should always be used instead of attempting to modify the bankaccountcomponent directly /// - /// The UID that the bank account is connected to, typically the player controlled mob - /// The integer amount of which to increase the bank account + /// The UID that the bank account is connected to, typically the player controlled mob + /// The amount of spesos to add into the bank account /// true if the transaction was successful, false if it was not - public bool TryBankDeposit(EntityUid mobUid, int amount) + public bool TryGetBalance(EntityUid ent, out int balance) { - if (amount <= 0) + if (!_playerManager.TryGetSessionByEntity(ent, out var session) || + !_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { - _log.Info($"{amount} is invalid"); + _log.Info($"{ent} has no cached prefs"); + balance = 0; return false; } - if (!TryComp(mobUid, out var bank)) + if (prefs.SelectedCharacter is not HumanoidCharacterProfile profile) { - _log.Info($"{mobUid} has no bank account"); + _log.Info($"{ent} has the wrong prefs type"); + balance = 0; return false; } - bank.Balance += amount; - _log.Info($"{mobUid} deposited {amount}"); - Dirty(mobUid, bank); + balance = profile.BankBalance; return true; } /// - /// ok so this is incredibly fucking cursed, and really shouldnt be calling LoadData - /// However - /// as of writing, the preferences system caches all player character data at the time of client connection. - /// This is causing some bad bahavior where the cache is becoming outdated after character data is getting saved to the db - /// and there is no method right now to invalidate and refresh this cache to ensure we get accurate bank data from the database, - /// resulting in respawns/round restarts populating the bank account component with an outdated cache and then re-saving that - /// bad cached data into the db. - /// effectively a gigantic money exploit. - /// So, this will have to stay cursed until I can find another way to refresh the character cache - /// or the db gods themselves come up to smite me from below, whichever comes first - /// - /// EDIT 5/13/2024 THE DB GODS THEY CAME. THEY SMOTE. SAVE ME + /// Update the bank balance to the current + /// + private void UpdateBankBalance(EntityUid mobUid, BankAccountComponent comp) + { + if (TryGetBalance(mobUid, out var balance)) + comp.Balance = balance; + else + comp.Balance = 0; + + Dirty(mobUid, comp); + } + + /// + /// Player attached, make sure the bank account is up-to-date. + /// + public void OnPlayerAttached(EntityUid mobUid, BankAccountComponent comp, PlayerAttachedEvent _) + { + UpdateBankBalance(mobUid, comp); + } + + /// + /// Player detached, make sure the bank account is up-to-date. + /// + public void OnPlayerDetached(EntityUid mobUid, BankAccountComponent comp, PlayerDetachedEvent _) + { + UpdateBankBalance(mobUid, comp); + } + + /// + /// Ensures the bank account listed in the lobby is accurate by ensuring the preferences cache is up-to-date. /// - private void OnPlayerLobbyJoin (PlayerJoinedLobbyEvent args) + private void OnPlayerLobbyJoin(PlayerJoinedLobbyEvent args) { var cts = new CancellationToken(); _prefsManager.RefreshPreferencesAsync(args.PlayerSession, cts); diff --git a/Content.Shared/Species/Systems/ReformSystem.cs b/Content.Shared/Species/Systems/ReformSystem.cs index b7f32b41bd1..d18a5abb5e3 100644 --- a/Content.Shared/Species/Systems/ReformSystem.cs +++ b/Content.Shared/Species/Systems/ReformSystem.cs @@ -97,14 +97,10 @@ private void OnDoAfter(EntityUid uid, ReformComponent comp, ReformDoAfterEvent a if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) _mindSystem.TransferTo(mindId, child, mind: mind); - // Frontier - // bank account transfer - if (TryComp(uid, out var bank)) + // Frontier: bank account transfer + if (HasComp(uid)) { - // Do this carefully since changing the value of a bank account component on a entity will save the balance immediately through subscribers. - var oldBankBalance = bank.Balance; - var newBank = EnsureComp(child); - newBank.Balance = oldBankBalance; + EnsureComp(child); } // Frontier diff --git a/Content.Shared/_NF/Bank/Components/BankAccountComponent.cs b/Content.Shared/_NF/Bank/Components/BankAccountComponent.cs index d029ab277c4..8a1267574f8 100644 --- a/Content.Shared/_NF/Bank/Components/BankAccountComponent.cs +++ b/Content.Shared/_NF/Bank/Components/BankAccountComponent.cs @@ -6,11 +6,8 @@ namespace Content.Shared.Bank.Components; [RegisterComponent, NetworkedComponent] public sealed partial class BankAccountComponent : Component { - [DataField("balance")] - public int Balance; -} -[Serializable, NetSerializable] -public sealed partial class BankAccountComponentState : ComponentState -{ + // The amount of money this entity has in their bank account. + // Should not be modified directly, may be out-of-date. + [DataField("balance", serverOnly: true), Access(typeof(SharedBankSystem))] public int Balance; } diff --git a/Content.Shared/_NF/Bank/SharedBankSystem.cs b/Content.Shared/_NF/Bank/SharedBankSystem.cs index 051fe8dd3d5..a9d511d4b14 100644 --- a/Content.Shared/_NF/Bank/SharedBankSystem.cs +++ b/Content.Shared/_NF/Bank/SharedBankSystem.cs @@ -12,14 +12,13 @@ public enum BankATMMenuUiKey : byte BlackMarket } -public sealed partial class SharedBankSystem : EntitySystem +public abstract partial class SharedBankSystem : EntitySystem { [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnComponentInit); @@ -45,15 +44,5 @@ private void OnComponentRemove(EntityUid uid, StationBankATMComponent component, { _itemSlotsSystem.RemoveItemSlot(uid, component.CashSlot); } - - private void OnHandleState(EntityUid playerUid, BankAccountComponent component, ref ComponentHandleState args) - { - if (args.Current is not BankAccountComponentState state) - { - return; - } - - component.Balance = state.Balance; - } } From 54e023b78a530d6885bd8328e0e7484d752f89f7 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sat, 7 Sep 2024 11:42:49 -0400 Subject: [PATCH 4/8] BankSystem: no-ent functions, spawn event sessions --- .../Administration/Systems/AdminVerbSystem.cs | 4 +- .../GameTicking/GameTicker.Spawning.cs | 2 +- .../Shuttles/Systems/ArrivalsSystem.cs | 3 +- .../ContainerSpawnPointSystem.cs | 3 +- .../EntitySystems/SpawnPointSystem.cs | 3 +- .../Station/Systems/StationSpawningSystem.cs | 53 +++++-- Content.Server/_NF/Bank/BankSystem.cs | 131 ++++++++++++++---- 7 files changed, 155 insertions(+), 44 deletions(-) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index d7888b491c3..fb6aa4d944a 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -175,7 +175,7 @@ private void AddAdminVerbs(GetVerbsEvent args) var stationUid = _stations.GetOwningStation(args.Target); var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession); - var mobUid = _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid); + var mobUid = _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid, session: targetActor.PlayerSession); // Frontier: added session var targetMind = _mindSystem.GetMind(args.Target); if (targetMind != null) @@ -203,7 +203,7 @@ private void AddAdminVerbs(GetVerbsEvent args) var stationUid = _stations.GetOwningStation(args.Target); var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession); - _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid); + _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid, session: targetActor.PlayerSession); // Frontier: added session }, ConfirmationPopup = true, Impact = LogImpact.High, diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 5742d697db3..e9e7a4fddf4 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -237,7 +237,7 @@ private void SpawnPlayer(ICommonSession player, spawnPointType = SpawnPointType.Job; } - var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character, spawnPointType: spawnPointType); + var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character, spawnPointType: spawnPointType, session: player); // Frontier: add session DebugTools.AssertNotNull(mobMaybe); var mob = mobMaybe!.Value; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 79c564611f5..3f383671c99 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -359,7 +359,8 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) spawnLoc, ev.Job, ev.HumanoidCharacterProfile, - ev.Station); + ev.Station, + session: ev.Session); // Frontier EnsureComp(ev.SpawnResult.Value); EnsureComp(ev.SpawnResult.Value); diff --git a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs index 463e9dad125..39032bcfcf0 100644 --- a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs @@ -63,7 +63,8 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) baseCoords, args.Job, args.HumanoidCharacterProfile, - args.Station); + args.Station, + session: args.Session); // Frontier _random.Shuffle(possibleContainers); foreach (var (uid, spawnPoint, manager, xform) in possibleContainers) diff --git a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs index 92170be13de..08b3ffbd072 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs @@ -86,6 +86,7 @@ private void OnPlayerSpawning(PlayerSpawningEvent args) spawnLoc, args.Job, args.HumanoidCharacterProfile, - args.Station); + args.Station, + session: args.Session); // Frontier } } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 830f48b936a..8b98ddaef69 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -31,7 +31,9 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using Content.Server.Spawners.Components; -using Content.Shared.Bank.Components; // DeltaV +using Content.Shared.Bank.Components; +using Content.Server.Bank; // DeltaV +using Content.Server.Preferences.Managers; // Frontier namespace Content.Server.Station.Systems; @@ -55,7 +57,9 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem [Dependency] private readonly PdaSystem _pdaSystem = default!; [Dependency] private readonly SharedAccessSystem _accessSystem = default!; [Dependency] private readonly IDependencyCollection _dependencyCollection = default!; // Frontier + [Dependency] private readonly IServerPreferencesManager _preferences = default!; // Frontier + [Dependency] private readonly BankSystem _bank = default!; // Frontier private bool _randomizeCharacters; private Dictionary> _spawnerCallbacks = new(); @@ -89,18 +93,19 @@ public override void Initialize() /// The character profile to use, if any. /// Resolve pattern, the station spawning component for the station. /// Delta-V: Set desired spawn point type. + /// Frontier: The session associated with the character, if any. /// The resulting player character, if any. /// Thrown when the given station is not a station. /// /// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable. /// - public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null, SpawnPointType spawnPointType = SpawnPointType.Unset) + public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null, SpawnPointType spawnPointType = SpawnPointType.Unset, ICommonSession? session = null) // Frontier: add session { if (station != null && !Resolve(station.Value, ref stationSpawning)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); // Delta-V: Set desired spawn point type. - var ev = new PlayerSpawningEvent(job, profile, station, spawnPointType); + var ev = new PlayerSpawningEvent(job, profile, station, spawnPointType, session); if (station != null && profile != null) { @@ -146,13 +151,15 @@ public override void Initialize() /// Appearance profile to use for the character. /// The station this player is being spawned on. /// The entity to use, if one already exists. + /// Frontier: The session associated with the entity, if one exists. /// The spawned entity public EntityUid SpawnPlayerMob( EntityCoordinates coordinates, JobComponent? job, HumanoidCharacterProfile? profile, EntityUid? station, - EntityUid? entity = null) + EntityUid? entity = null, + ICommonSession? session = null) // Frontier { _prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype); @@ -194,7 +201,21 @@ public EntityUid SpawnPlayerMob( } var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID); + var initialBankBalance = profile!.BankBalance; //Frontier var bankBalance = profile!.BankBalance; //Frontier + bool hasBalance = false; // Frontier + + // Frontier: get bank account, ensure we can make a withdrawal + // Note: since this is stored per character, we don't have a cached + // reference for randomly generated characters. + PlayerPreferences? prefs = null; + if (session != null && + _preferences.TryGetCachedPreferences(session.UserId, out prefs) && + prefs.IndexOfCharacter(profile) != -1) + { + hasBalance = true; + } + // End Frontier if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto)) { @@ -229,10 +250,11 @@ public EntityUid SpawnPlayerMob( // Handle any extra data here. //Frontier - we handle bank stuff so we are wrapping each item spawn inside our own cached check. + //If the user's preferences haven't been loaded, only give them free items or fallbacks. //This way, we will spawn every item we can afford in the order that they were originally sorted. - if (loadoutProto.Price <= bankBalance) + if (loadoutProto.Price <= bankBalance && (loadoutProto.Price <= 0 || hasBalance)) { - bankBalance -= loadoutProto.Price; + bankBalance -= int.Max(0, loadoutProto.Price); // Treat negatives as zero. EquipStartingGear(entity.Value, startingGear, raiseEvent: false); equippedItems.Add(loadoutProto.ID); } @@ -279,12 +301,18 @@ public EntityUid SpawnPlayerMob( // End of modified code. } - // Frontier: do not re-equip roleLoadout. - // Frontier: DO equip job startingGear. + // Frontier: do not re-equip roleLoadout, make sure we equip job startingGear, + // and deduct loadout costs from a bank account if we have one. if (prototype?.StartingGear is not null) EquipStartingGear(entity.Value, prototype.StartingGear, raiseEvent: false); - EnsureComp(entity.Value); + var bankComp = EnsureComp(entity.Value); + + if (hasBalance) + { + _bank.TryBankWithdraw(session!, prefs!, profile!, initialBankBalance - bankBalance, out var newBalance); + } + // End Frontier } var gearEquippedEv = new StartingGearEquippedEvent(entity.Value); @@ -390,12 +418,17 @@ public sealed class PlayerSpawningEvent : EntityEventArgs /// Delta-V: Desired SpawnPointType, if any. /// public readonly SpawnPointType DesiredSpawnPointType; + /// + /// Frontier: The session associated with the entity, if any. + /// + public readonly ICommonSession? Session; - public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station, SpawnPointType spawnPointType = SpawnPointType.Unset) + public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station, SpawnPointType spawnPointType = SpawnPointType.Unset, ICommonSession? session = null) // Frontier: added session { Job = job; HumanoidCharacterProfile = humanoidCharacterProfile; Station = station; DesiredSpawnPointType = spawnPointType; + Session = session; } } diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index b61dd6d7a7d..42bc783cba5 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Bank.Components; using Content.Shared.Preferences; using Robust.Shared.Player; +using System.Diagnostics.CodeAnalysis; namespace Content.Server.Bank; @@ -28,7 +29,9 @@ public override void Initialize() } /// - /// Attempts to remove money from a character's bank account. This should always be used instead of attempting to modify the bankaccountcomponent directly + /// Attempts to remove money from a character's bank account. + /// This should always be used instead of attempting to modify the BankAccountComponent directly. + /// When successful, the entity's BankAccountComponent will be updated with their current balance. /// /// The UID that the bank account is attached to, typically the player controlled mob /// The integer amount of which to decrease the bank account @@ -37,7 +40,7 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) { if (amount <= 0) { - _log.Info($"{amount} is invalid"); + _log.Info($"TryBankWithdraw: {amount} is invalid"); return false; } @@ -47,8 +50,13 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) return false; } - if (!_playerManager.TryGetSessionByEntity(mobUid, out var session) || - !_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) + if (!_playerManager.TryGetSessionByEntity(mobUid, out var session)) + { + _log.Info($"{mobUid} has no attached session"); + return false; + } + + if (!_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { _log.Info($"{mobUid} has no cached prefs"); return false; @@ -60,24 +68,14 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) return false; } - int balance = profile.BankBalance; - - if (balance < amount) + if (TryBankWithdraw(session, prefs, profile, amount, out var newBalance)) { - _log.Info($"{mobUid} has insufficient funds"); - return false; + bank.Balance = newBalance.Value; + Dirty(mobUid, bank); + _log.Info($"{mobUid} withdrew {amount}"); + return true; } - - balance -= amount; - - var newProfile = profile.WithBankBalance(balance); - var index = prefs.IndexOfCharacter(prefs.SelectedCharacter); - _prefsManager.SetProfile(session.UserId, index, newProfile); - - bank.Balance = balance; - Dirty(mobUid, bank); - _log.Info($"{mobUid} withdrew {amount}"); - return true; + return false; } /// @@ -90,7 +88,7 @@ public bool TryBankDeposit(EntityUid mobUid, int amount) { if (amount <= 0) { - _log.Info($"{amount} is invalid"); + _log.Info($"TryBankDeposit: {amount} is invalid"); return false; } @@ -100,8 +98,13 @@ public bool TryBankDeposit(EntityUid mobUid, int amount) return false; } - if (!_playerManager.TryGetSessionByEntity(mobUid, out var session) || - !_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) + if (!_playerManager.TryGetSessionByEntity(mobUid, out var session)) + { + _log.Info($"{mobUid} has no attached session"); + return false; + } + + if (!_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { _log.Info($"{mobUid} has no cached prefs"); return false; @@ -113,15 +116,87 @@ public bool TryBankDeposit(EntityUid mobUid, int amount) return false; } - int balance = profile.BankBalance + amount; + if (TryBankDeposit(session, prefs, profile, amount, out var newBalance)) + { + bank.Balance = newBalance.Value; + Dirty(mobUid, bank); + _log.Info($"{mobUid} deposited {amount}"); + return true; + } + + return false; + } + + /// + /// Attempts to remove money from a character's bank account without a backing entity. + /// This should only be used in cases where a character doesn't have a backing entity. + /// + /// The session of the player making the withdrawal. + /// The preferences storing the character whose bank will be changed. + /// The profile of the character whose account is being withdrawn. + /// The number of spesos to be withdrawn. + /// The new value of the bank account. + /// true if the transaction was successful, false if it was not. When successful, newBalance contains the character's new balance. + public bool TryBankWithdraw(ICommonSession session, PlayerPreferences prefs, HumanoidCharacterProfile profile, int amount, [NotNullWhen(true)] out int? newBalance) + { + newBalance = null; // Default return + if (amount <= 0) + { + _log.Info($"TryBankWithdraw: {amount} is invalid"); + return false; + } + + int balance = profile.BankBalance; + + if (balance < amount) + { + _log.Info($"{session.UserId} tried to withdraw {amount}, but has insufficient funds ({balance})"); + return false; + } + + balance -= amount; var newProfile = profile.WithBankBalance(balance); - var index = prefs.IndexOfCharacter(prefs.SelectedCharacter); + var index = prefs.IndexOfCharacter(profile); + if (index == -1) + { + _log.Info($"{session.UserId} tried to adjust the balance of {profile.Name}, but they were not in the user's character set."); + return false; + } _prefsManager.SetProfile(session.UserId, index, newProfile); + newBalance = balance; + return true; + } - bank.Balance = balance; - Dirty(mobUid, bank); - _log.Info($"{mobUid} deposited {amount}"); + /// + /// Attempts to add money to a character's bank account. + /// This should only be used in cases where a character doesn't have a backing entity. + /// + /// The session of the player making the deposit. + /// The preferences storing the character whose bank will be changed. + /// The profile of the character whose account is being withdrawn. + /// The number of spesos to be deposited. + /// The new value of the bank account. + /// true if the transaction was successful, false if it was not. When successful, newBalance contains the character's new balance. + public bool TryBankDeposit(ICommonSession session, PlayerPreferences prefs, HumanoidCharacterProfile profile, int amount, [NotNullWhen(true)] out int? newBalance) + { + newBalance = null; // Default return + if (amount <= 0) + { + _log.Info($"TryBankDeposit: {amount} is invalid"); + return false; + } + + newBalance = profile.BankBalance + amount; + + var newProfile = profile.WithBankBalance(newBalance.Value); + var index = prefs.IndexOfCharacter(profile); + if (index == -1) + { + _log.Info($"{session.UserId} tried to adjust the balance of {profile.Name}, but they were not in the user's character set."); + return false; + } + _prefsManager.SetProfile(session.UserId, index, newProfile); return true; } From 0782f7111be36fb16d832f19a8fb6520ad0ef54a Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sat, 7 Sep 2024 11:44:50 -0400 Subject: [PATCH 5/8] fix delta-v/frontier comments --- Content.Server/Station/Systems/StationSpawningSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 8b98ddaef69..0a61e2e144b 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -31,8 +31,8 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using Content.Server.Spawners.Components; -using Content.Shared.Bank.Components; -using Content.Server.Bank; // DeltaV +using Content.Shared.Bank.Components; // DeltaV +using Content.Server.Bank; // Frontier using Content.Server.Preferences.Managers; // Frontier namespace Content.Server.Station.Systems; From 1aed493a02af3ed397ac7f5418154ef7de005841 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sat, 7 Sep 2024 11:50:17 -0400 Subject: [PATCH 6/8] Additional frontier session comments --- .../Station/Systems/StationSpawningSystem.cs | 3 ++- Content.Server/_NF/Bank/BankSystem.cs | 20 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 0a61e2e144b..f477b864b92 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -105,6 +105,7 @@ public override void Initialize() throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); // Delta-V: Set desired spawn point type. + // Frontier: add session var ev = new PlayerSpawningEvent(job, profile, station, spawnPointType, session); if (station != null && profile != null) @@ -429,6 +430,6 @@ public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoid HumanoidCharacterProfile = humanoidCharacterProfile; Station = station; DesiredSpawnPointType = spawnPointType; - Session = session; + Session = session; // Frontier } } diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index 42bc783cba5..d5740729f00 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -46,25 +46,25 @@ public bool TryBankWithdraw(EntityUid mobUid, int amount) if (!TryComp(mobUid, out var bank)) { - _log.Info($"{mobUid} has no bank account"); + _log.Info($"TryBankWithdraw: {mobUid} has no bank account"); return false; } if (!_playerManager.TryGetSessionByEntity(mobUid, out var session)) { - _log.Info($"{mobUid} has no attached session"); + _log.Info($"TryBankWithdraw: {mobUid} has no attached session"); return false; } if (!_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { - _log.Info($"{mobUid} has no cached prefs"); + _log.Info($"TryBankWithdraw: {mobUid} has no cached prefs"); return false; } if (prefs.SelectedCharacter is not HumanoidCharacterProfile profile) { - _log.Info($"{mobUid} has the wrong prefs type"); + _log.Info($"TryBankWithdraw: {mobUid} has the wrong prefs type"); return false; } @@ -94,25 +94,25 @@ public bool TryBankDeposit(EntityUid mobUid, int amount) if (!TryComp(mobUid, out var bank)) { - _log.Info($"{mobUid} has no bank account"); + _log.Info($"TryBankDeposit: {mobUid} has no bank account"); return false; } if (!_playerManager.TryGetSessionByEntity(mobUid, out var session)) { - _log.Info($"{mobUid} has no attached session"); + _log.Info($"TryBankDeposit: {mobUid} has no attached session"); return false; } if (!_prefsManager.TryGetCachedPreferences(session.UserId, out var prefs)) { - _log.Info($"{mobUid} has no cached prefs"); + _log.Info($"TryBankDeposit: {mobUid} has no cached prefs"); return false; } if (prefs.SelectedCharacter is not HumanoidCharacterProfile profile) { - _log.Info($"{mobUid} has the wrong prefs type"); + _log.Info($"TryBankDeposit: {mobUid} has the wrong prefs type"); return false; } @@ -150,7 +150,7 @@ public bool TryBankWithdraw(ICommonSession session, PlayerPreferences prefs, Hum if (balance < amount) { - _log.Info($"{session.UserId} tried to withdraw {amount}, but has insufficient funds ({balance})"); + _log.Info($"TryBankWithdraw: {session.UserId} tried to withdraw {amount}, but has insufficient funds ({balance})"); return false; } @@ -160,7 +160,7 @@ public bool TryBankWithdraw(ICommonSession session, PlayerPreferences prefs, Hum var index = prefs.IndexOfCharacter(profile); if (index == -1) { - _log.Info($"{session.UserId} tried to adjust the balance of {profile.Name}, but they were not in the user's character set."); + _log.Info($"TryBankWithdraw: {session.UserId} tried to adjust the balance of {profile.Name}, but they were not in the user's character set."); return false; } _prefsManager.SetProfile(session.UserId, index, newProfile); From b534d9af00f309cb6d8373eb6523f948092b4d23 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sun, 8 Sep 2024 12:08:36 -0400 Subject: [PATCH 7/8] BankSystem: ComponentInit function --- Content.Server/_NF/Bank/BankSystem.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index d5740729f00..e84e394ff61 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -23,6 +23,7 @@ public override void Initialize() InitializeATM(); InitializeStationATM(); + SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnPlayerLobbyJoin); @@ -228,7 +229,7 @@ public bool TryGetBalance(EntityUid ent, out int balance) } /// - /// Update the bank balance to the current + /// Update the bank balance to the character's current account balance. /// private void UpdateBankBalance(EntityUid mobUid, BankAccountComponent comp) { @@ -240,6 +241,14 @@ private void UpdateBankBalance(EntityUid mobUid, BankAccountComponent comp) Dirty(mobUid, comp); } + /// + /// Component initialized - if the player exists in the entity before the BankAccountComponent, update the player's account. + /// + public void OnInit(EntityUid mobUid, BankAccountComponent comp, ComponentInit _) + { + UpdateBankBalance(mobUid, comp); + } + /// /// Player attached, make sure the bank account is up-to-date. /// From e7eb7eae7d0eaac8e3b83dc302b8a81f1c16141d Mon Sep 17 00:00:00 2001 From: Whatstone Date: Sun, 8 Sep 2024 13:34:05 -0400 Subject: [PATCH 8/8] New event for preferences being loaded --- .../Managers/ServerPreferencesManager.cs | 19 +++++++++++++++++++ Content.Server/_NF/Bank/BankSystem.cs | 11 ++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 3c3803308dc..12a76ad5a16 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -28,6 +28,7 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager, IPostI [Dependency] private readonly IPrototypeManager _protos = default!; [Dependency] private readonly ILogManager _log = default!; [Dependency] private readonly UserDbDataManager _userDb = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = @@ -218,6 +219,10 @@ public void FinishLoad(ICommonSession session) MaxCharacterSlots = MaxCharacterSlots }; _netManager.ServerSendMessage(msg, session.Channel); + + // Frontier: notify other entities that your player data is loaded. + if (session.AttachedEntity != null) + _entityManager.EventBus.RaiseLocalEvent(session.AttachedEntity.Value, new PreferencesLoadedEvent(session, prefsData.Prefs)); } public void OnClientDisconnected(ICommonSession session) @@ -361,4 +366,18 @@ void IPostInjectInit.PostInject() _userDb.AddOnPlayerDisconnect(OnClientDisconnected); } } + + // Frontier: event for notifying that preferences for a particular player have loaded in. + public sealed class PreferencesLoadedEvent : EntityEventArgs + { + public readonly ICommonSession Session; + public readonly PlayerPreferences Prefs; + + public PreferencesLoadedEvent(ICommonSession session, PlayerPreferences prefs) + { + Session = session; + Prefs = prefs; + } + } + // End Frontier } diff --git a/Content.Server/_NF/Bank/BankSystem.cs b/Content.Server/_NF/Bank/BankSystem.cs index e84e394ff61..04a39da4507 100644 --- a/Content.Server/_NF/Bank/BankSystem.cs +++ b/Content.Server/_NF/Bank/BankSystem.cs @@ -23,7 +23,8 @@ public override void Initialize() InitializeATM(); InitializeStationATM(); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnPreferencesLoaded); // For late-add bank accounts + SubscribeLocalEvent(OnInit); // For late-add bank accounts SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnPlayerLobbyJoin); @@ -249,6 +250,14 @@ public void OnInit(EntityUid mobUid, BankAccountComponent comp, ComponentInit _) UpdateBankBalance(mobUid, comp); } + /// + /// Player's preferences loaded (mostly for hotjoin) + /// + public void OnPreferencesLoaded(EntityUid mobUid, BankAccountComponent comp, PreferencesLoadedEvent _) + { + UpdateBankBalance(mobUid, comp); + } + /// /// Player attached, make sure the bank account is up-to-date. ///