diff --git a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs index 0fc0c18b62..aafd0ea630 100644 --- a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs @@ -67,7 +67,7 @@ public sealed partial class ContentAudioSystem _nextAudio = TimeSpan.MaxValue; SetupAmbientSounds(); - _proto.PrototypesReloaded += OnProtoReload; + SubscribeLocalEvent(OnProtoReload); _state.OnStateChanged += OnStateChange; // On round end summary OR lobby cut audio. SubscribeNetworkEvent(OnRoundEndMessage); @@ -86,21 +86,14 @@ public sealed partial class ContentAudioSystem private void ShutdownAmbientMusic() { _configManager.UnsubValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged); - _proto.PrototypesReloaded -= OnProtoReload; _state.OnStateChanged -= OnStateChange; _ambientMusicStream = _audio.Stop(_ambientMusicStream); } private void OnProtoReload(PrototypesReloadedEventArgs obj) { - if (!obj.ByType.ContainsKey(typeof(AmbientMusicPrototype)) && - !obj.ByType.ContainsKey(typeof(RulesPrototype))) - { - return; - } - - _ambientSounds.Clear(); - SetupAmbientSounds(); + if (obj.WasModified() || obj.WasModified()) + SetupAmbientSounds(); } private void OnStateChange(StateChangedEventArgs obj) @@ -114,6 +107,7 @@ public sealed partial class ContentAudioSystem private void SetupAmbientSounds() { + _ambientSounds.Clear(); foreach (var ambience in _proto.EnumeratePrototypes()) { var tracks = _ambientSounds.GetOrNew(ambience.ID); diff --git a/Content.Client/Clothing/Systems/ChameleonClothingSystem.cs b/Content.Client/Clothing/Systems/ChameleonClothingSystem.cs index f067d91051..0ea9bbac09 100644 --- a/Content.Client/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Client/Clothing/Systems/ChameleonClothingSystem.cs @@ -29,18 +29,13 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem SubscribeLocalEvent(HandleState); PrepareAllVariants(); - _proto.PrototypesReloaded += OnProtoReloaded; + SubscribeLocalEvent(OnProtoReloaded); } - public override void Shutdown() + private void OnProtoReloaded(PrototypesReloadedEventArgs args) { - base.Shutdown(); - _proto.PrototypesReloaded -= OnProtoReloaded; - } - - private void OnProtoReloaded(PrototypesReloadedEventArgs _) - { - PrepareAllVariants(); + if (args.WasModified()) + PrepareAllVariants(); } private void HandleState(EntityUid uid, ChameleonClothingComponent component, ref AfterAutoHandleStateEvent args) diff --git a/Content.Client/CrewManifest/CrewManifestSystem.cs b/Content.Client/CrewManifest/CrewManifestSystem.cs index 05f27620c4..d05acb5680 100644 --- a/Content.Client/CrewManifest/CrewManifestSystem.cs +++ b/Content.Client/CrewManifest/CrewManifestSystem.cs @@ -1,4 +1,3 @@ -using Content.Client.GameTicking.Managers; using Content.Shared.CrewManifest; using Content.Shared.Roles; using Robust.Shared.Prototypes; @@ -19,12 +18,7 @@ public sealed class CrewManifestSystem : EntitySystem base.Initialize(); BuildDepartmentLookup(); - _prototypeManager.PrototypesReloaded += OnPrototypesReload; - } - - public override void Shutdown() - { - _prototypeManager.PrototypesReloaded -= OnPrototypesReload; + SubscribeLocalEvent(OnPrototypesReload); } /// @@ -36,16 +30,16 @@ public sealed class CrewManifestSystem : EntitySystem RaiseNetworkEvent(new RequestCrewManifestMessage(netEntity)); } - private void OnPrototypesReload(PrototypesReloadedEventArgs _) + private void OnPrototypesReload(PrototypesReloadedEventArgs args) { - _jobDepartmentLookup.Clear(); - _departments.Clear(); - - BuildDepartmentLookup(); + if (args.WasModified()) + BuildDepartmentLookup(); } private void BuildDepartmentLookup() { + _jobDepartmentLookup.Clear(); + _departments.Clear(); foreach (var department in _prototypeManager.EnumeratePrototypes()) { _departments.Add(department.ID); diff --git a/Content.Client/Parallax/ParallaxSystem.cs b/Content.Client/Parallax/ParallaxSystem.cs index 721dc3a8c2..720da72e80 100644 --- a/Content.Client/Parallax/ParallaxSystem.cs +++ b/Content.Client/Parallax/ParallaxSystem.cs @@ -24,14 +24,13 @@ public sealed class ParallaxSystem : SharedParallaxSystem { base.Initialize(); _overlay.AddOverlay(new ParallaxOverlay()); - _protoManager.PrototypesReloaded += OnReload; - + SubscribeLocalEvent(OnReload); SubscribeLocalEvent(OnAfterAutoHandleState); } private void OnReload(PrototypesReloadedEventArgs obj) { - if (!obj.ByType.ContainsKey(typeof(ParallaxPrototype))) + if (!obj.WasModified()) return; _parallax.UnloadParallax(Fallback); @@ -48,7 +47,6 @@ public sealed class ParallaxSystem : SharedParallaxSystem { base.Shutdown(); _overlay.RemoveOverlay(); - _protoManager.PrototypesReloaded -= OnReload; } private void OnAfterAutoHandleState(EntityUid uid, ParallaxComponent component, ref AfterAutoHandleStateEvent args) diff --git a/Content.Client/UserInterface/Systems/DecalPlacer/DecalPlacerUIController.cs b/Content.Client/UserInterface/Systems/DecalPlacer/DecalPlacerUIController.cs index 5f1914a462..f1b611ac5a 100644 --- a/Content.Client/UserInterface/Systems/DecalPlacer/DecalPlacerUIController.cs +++ b/Content.Client/UserInterface/Systems/DecalPlacer/DecalPlacerUIController.cs @@ -53,7 +53,8 @@ public sealed class DecalPlacerUIController : UIController, IOnStateExited()) + ReloadPrototypes(); } private void ReloadPrototypes() diff --git a/Content.Server/AlertLevel/AlertLevelSystem.cs b/Content.Server/AlertLevel/AlertLevelSystem.cs index b2b63e618e..04e274ceeb 100644 --- a/Content.Server/AlertLevel/AlertLevelSystem.cs +++ b/Content.Server/AlertLevel/AlertLevelSystem.cs @@ -23,15 +23,7 @@ public sealed class AlertLevelSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(OnStationInitialize); - - _prototypeManager.PrototypesReloaded += OnPrototypeReload; - } - - public override void Shutdown() - { - base.Shutdown(); - - _prototypeManager.PrototypesReloaded -= OnPrototypeReload; + SubscribeLocalEvent(OnPrototypeReload); } public override void Update(float time) diff --git a/Content.Server/Audio/ContentAudioSystem.cs b/Content.Server/Audio/ContentAudioSystem.cs index 51bd3183b6..b1db01141b 100644 --- a/Content.Server/Audio/ContentAudioSystem.cs +++ b/Content.Server/Audio/ContentAudioSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.Audio; using Content.Shared.GameTicking; using Robust.Server.Audio; using Robust.Shared.Audio; -using Robust.Shared.Audio.Components; using Robust.Shared.Prototypes; namespace Content.Server.Audio; @@ -11,14 +10,13 @@ namespace Content.Server.Audio; public sealed class ContentAudioSystem : SharedContentAudioSystem { [Dependency] private readonly AudioSystem _serverAudio = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRoundCleanup); SubscribeLocalEvent(OnRoundStart); - _protoManager.PrototypesReloaded += OnProtoReload; + SubscribeLocalEvent(OnProtoReload); } private void OnRoundCleanup(RoundRestartCleanupEvent ev) @@ -28,16 +26,8 @@ public sealed class ContentAudioSystem : SharedContentAudioSystem private void OnProtoReload(PrototypesReloadedEventArgs obj) { - if (!obj.ByType.ContainsKey(typeof(AudioPresetPrototype))) - return; - - _serverAudio.ReloadPresets(); - } - - public override void Shutdown() - { - base.Shutdown(); - _protoManager.PrototypesReloaded -= OnProtoReload; + if (obj.WasModified()) + _serverAudio.ReloadPresets(); } private void OnRoundStart(RoundStartingEvent ev) diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Server/Chat/Systems/ChatSystem.Emote.cs index 7ab86aa108..8bba76dadd 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Server/Chat/Systems/ChatSystem.Emote.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using Content.Shared.Chat.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -7,44 +8,36 @@ namespace Content.Server.Chat.Systems; // emotes using emote prototype public partial class ChatSystem { - private readonly Dictionary _wordEmoteDict = new(); + private FrozenDictionary _wordEmoteDict = FrozenDictionary.Empty; - private void InitializeEmotes() + protected override void OnPrototypeReload(PrototypesReloadedEventArgs obj) { - _prototypeManager.PrototypesReloaded += OnPrototypeReloadEmotes; - CacheEmotes(); - } - - private void ShutdownEmotes() - { - _prototypeManager.PrototypesReloaded -= OnPrototypeReloadEmotes; - } - - private void OnPrototypeReloadEmotes(PrototypesReloadedEventArgs obj) - { - CacheEmotes(); + base.OnPrototypeReload(obj); + if (obj.WasModified()) + CacheEmotes(); } private void CacheEmotes() { - _wordEmoteDict.Clear(); + var dict = new Dictionary(); var emotes = _prototypeManager.EnumeratePrototypes(); foreach (var emote in emotes) { foreach (var word in emote.ChatTriggers) { var lowerWord = word.ToLower(); - if (_wordEmoteDict.ContainsKey(lowerWord)) + if (dict.TryGetValue(lowerWord, out var value)) { - var existingId = _wordEmoteDict[lowerWord].ID; - var errMsg = $"Duplicate of emote word {lowerWord} in emotes {emote.ID} and {existingId}"; - Logger.Error(errMsg); + var errMsg = $"Duplicate of emote word {lowerWord} in emotes {emote.ID} and {value.ID}"; + Log.Error(errMsg); continue; } - _wordEmoteDict.Add(lowerWord, emote); + dict.Add(lowerWord, emote); } } + + _wordEmoteDict = dict.ToFrozenDictionary(); } /// diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 390faab756..c8a43bb3a6 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -69,7 +69,7 @@ public sealed partial class ChatSystem : SharedChatSystem public override void Initialize() { base.Initialize(); - InitializeEmotes(); + CacheEmotes(); _configurationManager.OnValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged, true); @@ -80,7 +80,6 @@ public sealed partial class ChatSystem : SharedChatSystem public override void Shutdown() { base.Shutdown(); - ShutdownEmotes(); _configurationManager.UnsubValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged); _configurationManager.UnsubValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged); _configurationManager.UnsubValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged); @@ -736,7 +735,7 @@ public sealed partial class ChatSystem : SharedChatSystem return ev.Message; } - + public bool CheckIgnoreSpeechBlocker(EntityUid sender, bool ignoreBlocker) { if (ignoreBlocker) diff --git a/Content.Server/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs index 7485c0e901..e2cd72030d 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistryGuideDataSystem.cs @@ -17,8 +17,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem { base.Initialize(); - PrototypeManager.PrototypesReloaded += PrototypeManagerReload; - + SubscribeLocalEvent(PrototypeManagerReload); _player.PlayerStatusChanged += OnPlayerStatusChanged; InitializeServerRegistry(); diff --git a/Content.Server/Holiday/Christmas/RandomGiftSystem.cs b/Content.Server/Holiday/Christmas/RandomGiftSystem.cs index 40b365105d..33d5d0d234 100644 --- a/Content.Server/Holiday/Christmas/RandomGiftSystem.cs +++ b/Content.Server/Holiday/Christmas/RandomGiftSystem.cs @@ -31,7 +31,7 @@ public sealed class RandomGiftSystem : EntitySystem /// public override void Initialize() { - _prototype.PrototypesReloaded += OnPrototypesReloaded; + SubscribeLocalEvent(OnPrototypesReloaded); SubscribeLocalEvent(OnGiftMapInit); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnExamined); @@ -80,7 +80,8 @@ public sealed class RandomGiftSystem : EntitySystem private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj) { - BuildIndex(); + if (obj.WasModified()) + BuildIndex(); } private void BuildIndex() diff --git a/Content.Server/NPC/HTN/HTNSystem.cs b/Content.Server/NPC/HTN/HTNSystem.cs index a7689fbabe..2ba91d5be6 100644 --- a/Content.Server/NPC/HTN/HTNSystem.cs +++ b/Content.Server/NPC/HTN/HTNSystem.cs @@ -10,7 +10,6 @@ using Content.Shared.Administration; using Content.Shared.Mobs; using Content.Shared.NPC; using JetBrains.Annotations; -using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -38,8 +37,7 @@ public sealed class HTNSystem : EntitySystem SubscribeLocalEvent(_npc.OnPlayerNPCDetach); SubscribeLocalEvent(OnHTNShutdown); SubscribeNetworkEvent(OnHTNMessage); - - _prototypeManager.PrototypesReloaded += OnPrototypeLoad; + SubscribeLocalEvent(OnPrototypeLoad); OnLoad(); } @@ -57,12 +55,6 @@ public sealed class HTNSystem : EntitySystem _subscribers.Remove(args.SenderSession); } - public override void Shutdown() - { - base.Shutdown(); - _prototypeManager.PrototypesReloaded -= OnPrototypeLoad; - } - private void OnLoad() { // Clear all NPCs in case they're hanging onto stale tasks diff --git a/Content.Server/NPC/Systems/NpcFactionSystem.cs b/Content.Server/NPC/Systems/NpcFactionSystem.cs index 08c9353411..0657ab4af9 100644 --- a/Content.Server/NPC/Systems/NpcFactionSystem.cs +++ b/Content.Server/NPC/Systems/NpcFactionSystem.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Linq; using Content.Server.NPC.Components; using JetBrains.Annotations; @@ -18,31 +19,23 @@ public sealed partial class NpcFactionSystem : EntitySystem /// /// To avoid prototype mutability we store an intermediary data class that gets used instead. /// - private Dictionary _factions = new(); + private FrozenDictionary _factions = FrozenDictionary.Empty; public override void Initialize() { base.Initialize(); _sawmill = Logger.GetSawmill("faction"); SubscribeLocalEvent(OnFactionStartup); - _protoManager.PrototypesReloaded += OnProtoReload; + SubscribeLocalEvent(OnProtoReload); InitializeException(); RefreshFactions(); } - public override void Shutdown() - { - base.Shutdown(); - _protoManager.PrototypesReloaded -= OnProtoReload; - } - private void OnProtoReload(PrototypesReloadedEventArgs obj) { - if (!obj.ByType.ContainsKey(typeof(NpcFactionPrototype))) - return; - - RefreshFactions(); + if (obj.WasModified()) + RefreshFactions(); } private void OnFactionStartup(EntityUid uid, NpcFactionMemberComponent memberComponent, ComponentStartup args) @@ -237,16 +230,15 @@ public sealed partial class NpcFactionSystem : EntitySystem private void RefreshFactions() { - _factions.Clear(); - foreach (var faction in _protoManager.EnumeratePrototypes()) - { - _factions[faction.ID] = new FactionData() + _factions = _protoManager.EnumeratePrototypes().ToFrozenDictionary( + faction => faction.ID, + faction => new FactionData { Friendly = faction.Friendly.ToHashSet(), - Hostile = faction.Hostile.ToHashSet(), - }; - } + Hostile = faction.Hostile.ToHashSet() + + }); foreach (var comp in EntityQuery(true)) { diff --git a/Content.Server/NameIdentifier/NameIdentifierSystem.cs b/Content.Server/NameIdentifier/NameIdentifierSystem.cs index bde8466d06..6d6e9e6c69 100644 --- a/Content.Server/NameIdentifier/NameIdentifierSystem.cs +++ b/Content.Server/NameIdentifier/NameIdentifierSystem.cs @@ -28,9 +28,9 @@ public sealed class NameIdentifierSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(CleanupIds); + SubscribeLocalEvent(OnReloadPrototypes); InitialSetupPrototypes(); - _prototypeManager.PrototypesReloaded += OnReloadPrototypes; } private void OnComponentShutdown(EntityUid uid, NameIdentifierComponent component, ComponentShutdown args) @@ -46,13 +46,6 @@ public sealed class NameIdentifierSystem : EntitySystem } } - public override void Shutdown() - { - base.Shutdown(); - - _prototypeManager.PrototypesReloaded -= OnReloadPrototypes; - } - /// /// Generates a new unique name/suffix for a given entity and adds it to /// but does not set the entity's name. diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs index daf21d77dd..881663db56 100644 --- a/Content.Server/Parallax/BiomeSystem.cs +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -85,14 +85,13 @@ public sealed partial class BiomeSystem : SharedBiomeSystem SubscribeLocalEvent(OnShuttleFlatten); _configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true); InitializeCommands(); - ProtoManager.PrototypesReloaded += ProtoReload; + SubscribeLocalEvent(ProtoReload); } public override void Shutdown() { base.Shutdown(); _configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange); - ProtoManager.PrototypesReloaded -= ProtoReload; } private void ProtoReload(PrototypesReloadedEventArgs obj) diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 9e85d86bf1..ba69f1ea5f 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -48,7 +48,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem _console.RegisterCommand("dungen", Loc.GetString("cmd-dungen-desc"), Loc.GetString("cmd-dungen-help"), GenerateDungeon, CompletionCallback); _console.RegisterCommand("dungen_preset_vis", Loc.GetString("cmd-dungen_preset_vis-desc"), Loc.GetString("cmd-dungen_preset_vis-help"), DungeonPresetVis, PresetCallback); _console.RegisterCommand("dungen_pack_vis", Loc.GetString("cmd-dungen_pack_vis-desc"), Loc.GetString("cmd-dungen_pack_vis-help"), DungeonPackVis, PackCallback); - _prototype.PrototypesReloaded += PrototypeReload; + SubscribeLocalEvent(PrototypeReload); SubscribeLocalEvent(OnRoundCleanup); SubscribeLocalEvent(OnRoundStart); } @@ -91,8 +91,6 @@ public sealed partial class DungeonSystem : SharedDungeonSystem public override void Shutdown() { base.Shutdown(); - _prototype.PrototypesReloaded -= PrototypeReload; - foreach (var token in _dungeonJobs.Values) { token.Cancel(); diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index d61cf303d6..8afc7e6bd5 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -30,6 +30,7 @@ public sealed class SpreaderSystem : EntitySystem /// /// Remaining number of updates per grid & prototype. /// + // TODO PERFORMANCE Assign each prototype to an index and convert dictionary to array private Dictionary> _gridUpdates = new(); public const float SpreadCooldownSeconds = 1; @@ -42,24 +43,16 @@ public sealed class SpreaderSystem : EntitySystem { SubscribeLocalEvent(OnAirtightChanged); SubscribeLocalEvent(OnGridInit); + SubscribeLocalEvent(OnPrototypeReload); SubscribeLocalEvent(OnTerminating); SetupPrototypes(); - _prototype.PrototypesReloaded += OnPrototypeReload; - } - - public override void Shutdown() - { - base.Shutdown(); - _prototype.PrototypesReloaded -= OnPrototypeReload; } private void OnPrototypeReload(PrototypesReloadedEventArgs obj) { - if (!obj.ByType.ContainsKey(typeof(EdgeSpreaderPrototype))) - return; - - SetupPrototypes(); + if (obj.WasModified()) + SetupPrototypes(); } private void SetupPrototypes() diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 83dc9382a3..424a4670ba 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -7,7 +8,8 @@ namespace Content.Shared.Alert; public abstract class AlertsSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private readonly Dictionary _typeToAlert = new(); + + private FrozenDictionary _typeToAlert = default!; public IReadOnlyDictionary? GetActiveAlerts(EntityUid euid) { @@ -170,9 +172,8 @@ public abstract class AlertsSystem : EntitySystem SubscribeLocalEvent(OnPlayerAttached); SubscribeNetworkEvent(HandleClickAlert); - + SubscribeLocalEvent(HandlePrototypesReloaded); LoadPrototypes(); - _prototypeManager.PrototypesReloaded += HandlePrototypesReloaded; } protected virtual void HandleComponentShutdown(EntityUid uid, AlertsComponent component, ComponentShutdown args) @@ -185,29 +186,25 @@ public abstract class AlertsSystem : EntitySystem RaiseLocalEvent(uid, new AlertSyncEvent(uid), true); } - public override void Shutdown() - { - _prototypeManager.PrototypesReloaded -= HandlePrototypesReloaded; - - base.Shutdown(); - } - private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj) { - LoadPrototypes(); + if (obj.WasModified()) + LoadPrototypes(); } protected virtual void LoadPrototypes() { - _typeToAlert.Clear(); + var dict = new Dictionary(); foreach (var alert in _prototypeManager.EnumeratePrototypes()) { - if (!_typeToAlert.TryAdd(alert.AlertType, alert)) + if (!dict.TryAdd(alert.AlertType, alert)) { Log.Error("Found alert with duplicate alertType {0} - all alerts must have" + " a unique alerttype, this one will be skipped", alert.AlertType); } } + + _typeToAlert = dict.ToFrozenDictionary(); } /// diff --git a/Content.Shared/Chat/SharedChatSystem.cs b/Content.Shared/Chat/SharedChatSystem.cs index f749214f1b..69918f8098 100644 --- a/Content.Shared/Chat/SharedChatSystem.cs +++ b/Content.Shared/Chat/SharedChatSystem.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using Content.Shared.Popups; using Content.Shared.Radio; using Content.Shared.Speech; @@ -36,35 +37,26 @@ public abstract class SharedChatSystem : EntitySystem /// /// Cache of the keycodes for faster lookup. /// - private Dictionary _keyCodes = new(); + private FrozenDictionary _keyCodes = default!; public override void Initialize() { base.Initialize(); DebugTools.Assert(_prototypeManager.HasIndex(CommonChannel)); - _prototypeManager.PrototypesReloaded += OnPrototypeReload; + SubscribeLocalEvent(OnPrototypeReload); CacheRadios(); } - private void OnPrototypeReload(PrototypesReloadedEventArgs obj) + protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj) { - if (obj.ByType.ContainsKey(typeof(RadioChannelPrototype))) + if (obj.WasModified()) CacheRadios(); } private void CacheRadios() { - _keyCodes.Clear(); - - foreach (var proto in _prototypeManager.EnumeratePrototypes()) - { - _keyCodes.Add(proto.KeyCode, proto); - } - } - - public override void Shutdown() - { - _prototypeManager.PrototypesReloaded -= OnPrototypeReload; + _keyCodes = _prototypeManager.EnumeratePrototypes() + .ToFrozenDictionary(x => x.KeyCode); } /// diff --git a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs index 1bf7e7888b..05e176da5b 100644 --- a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs +++ b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Chemistry.Components; @@ -7,6 +8,7 @@ using Content.Shared.FixedPoint; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Shared.Chemistry.Reaction { @@ -22,23 +24,22 @@ namespace Content.Shared.Chemistry.Reaction [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; /// - /// A cache of all existant chemical reactions indexed by one of their - /// required reactants. + /// A cache of all reactions indexed by at most ONE of their required reactants. + /// I.e., even if a reaction has more than one reagent, it will only ever appear once in this dictionary. /// - private IDictionary> _reactions = default!; + private FrozenDictionary> _reactionsSingle = default!; + + /// + /// A cache of all reactions indexed by one of their required reactants. + /// + private FrozenDictionary> _reactions = default!; public override void Initialize() { base.Initialize(); InitializeReactionCache(); - _prototypeManager.PrototypesReloaded += OnPrototypesReloaded; - } - - public override void Shutdown() - { - base.Shutdown(); - _prototypeManager.PrototypesReloaded -= OnPrototypesReloaded; + SubscribeLocalEvent(OnPrototypesReloaded); } /// @@ -46,34 +47,27 @@ namespace Content.Shared.Chemistry.Reaction /// private void InitializeReactionCache() { - _reactions = new Dictionary>(); - - var reactions = _prototypeManager.EnumeratePrototypes(); - foreach(var reaction in reactions) + // Construct single-reaction dictionary. + var dict = new Dictionary>(); + foreach(var reaction in _prototypeManager.EnumeratePrototypes()) { - CacheReaction(reaction); + // For this dictionary we only need to cache based on the first reagent. + var reagent = reaction.Reactants.Keys.First(); + var list = dict.GetOrNew(reagent); + list.Add(reaction); } - } + _reactionsSingle = dict.ToFrozenDictionary(); - /// - /// Caches a reaction by its first required reagent. - /// Used to build the reaction cache. - /// - /// A reaction prototype to cache. - private void CacheReaction(ReactionPrototype reaction) - { - var reagents = reaction.Reactants.Keys; - foreach(var reagent in reagents) + dict.Clear(); + foreach(var reaction in _prototypeManager.EnumeratePrototypes()) { - if(!_reactions.TryGetValue(reagent, out var cache)) + foreach (var reagent in reaction.Reactants.Keys) { - cache = new List(); - _reactions.Add(reagent, cache); + var list = dict.GetOrNew(reagent); + list.Add(reaction); } - - cache.Add(reaction); - return; // Only need to cache based on the first reagent. } + _reactions = dict.ToFrozenDictionary(); } /// @@ -82,20 +76,8 @@ namespace Content.Shared.Chemistry.Reaction /// The set of modified prototypes. private void OnPrototypesReloaded(PrototypesReloadedEventArgs eventArgs) { - if (!eventArgs.ByType.TryGetValue(typeof(ReactionPrototype), out var set)) - return; - - foreach (var (reactant, cache) in _reactions) - { - cache.RemoveAll((reaction) => set.Modified.ContainsKey(reaction.ID)); - if (cache.Count == 0) - _reactions.Remove(reactant); - } - - foreach (var prototype in set.Modified.Values) - { - CacheReaction((ReactionPrototype) prototype); - } + if (eventArgs.WasModified()) + InitializeReactionCache(); } /// @@ -285,7 +267,7 @@ namespace Content.Shared.Chemistry.Reaction SortedSet reactions = new(); foreach (var reactant in solution.Contents) { - if (_reactions.TryGetValue(reactant.Reagent.Prototype, out var reactantReactions)) + if (_reactionsSingle.TryGetValue(reactant.Reagent.Prototype, out var reactantReactions)) reactions.UnionWith(reactantReactions); } @@ -297,7 +279,7 @@ namespace Content.Shared.Chemistry.Reaction return; } - Logger.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops."); + Log.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops."); } } diff --git a/Content.Shared/Entry/EntryPoint.cs b/Content.Shared/Entry/EntryPoint.cs index 225687aabd..baae144ac5 100644 --- a/Content.Shared/Entry/EntryPoint.cs +++ b/Content.Shared/Entry/EntryPoint.cs @@ -83,6 +83,9 @@ namespace Content.Shared.Entry private void PrototypeReload(PrototypesReloadedEventArgs obj) { + if (!obj.WasModified()) + return; + // Need to re-allocate tiledefs due to how prototype reloads work foreach (var def in _prototypeManager.EnumeratePrototypes()) { diff --git a/Content.Shared/Humanoid/Markings/MarkingManager.cs b/Content.Shared/Humanoid/Markings/MarkingManager.cs index 68f7cfe362..7bf0be998b 100644 --- a/Content.Shared/Humanoid/Markings/MarkingManager.cs +++ b/Content.Shared/Humanoid/Markings/MarkingManager.cs @@ -1,4 +1,6 @@ +using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.Humanoid.Prototypes; using Robust.Shared.Prototypes; @@ -9,33 +11,41 @@ namespace Content.Shared.Humanoid.Markings [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly List _index = new(); - private readonly Dictionary> _markingDict = new(); - private readonly Dictionary _markings = new(); + public FrozenDictionary> CategorizedMarkings = default!; + public FrozenDictionary Markings = default!; public void Initialize() { _prototypeManager.PrototypesReloaded += OnPrototypeReload; + CachePrototypes(); + } + + private void CachePrototypes() + { + _index.Clear(); + var markingDict = new Dictionary>(); foreach (var category in Enum.GetValues()) { - _markingDict.Add(category, new Dictionary()); + markingDict.Add(category, new()); } foreach (var prototype in _prototypeManager.EnumeratePrototypes()) { _index.Add(prototype); - _markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype); - _markings.Add(prototype.ID, prototype); + markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype); } + + Markings = _prototypeManager.EnumeratePrototypes().ToFrozenDictionary(x => x.ID); + CategorizedMarkings = markingDict.ToFrozenDictionary( + x => x.Key, + x => x.Value.ToFrozenDictionary()); } - public IReadOnlyDictionary Markings => _markings; - public IReadOnlyDictionary> CategorizedMarkings => _markingDict; - - public IReadOnlyDictionary MarkingsByCategory(MarkingCategories category) + public FrozenDictionary MarkingsByCategory(MarkingCategories category) { // all marking categories are guaranteed to have a dict entry - return _markingDict[category]; + return CategorizedMarkings[category]; } /// @@ -143,7 +153,7 @@ namespace Content.Shared.Humanoid.Markings public bool TryGetMarking(Marking marking, [NotNullWhen(true)] out MarkingPrototype? markingResult) { - return _markings.TryGetValue(marking.MarkingId, out markingResult); + return Markings.TryGetValue(marking.MarkingId, out markingResult); } /// @@ -178,17 +188,8 @@ namespace Content.Shared.Humanoid.Markings private void OnPrototypeReload(PrototypesReloadedEventArgs args) { - if(!args.ByType.TryGetValue(typeof(MarkingPrototype), out var set)) - return; - - - _index.RemoveAll(i => set.Modified.ContainsKey(i.ID)); - - foreach (var prototype in set.Modified.Values) - { - var markingPrototype = (MarkingPrototype) prototype; - _index.Add(markingPrototype); - } + if (args.WasModified()) + CachePrototypes(); } public bool CanBeApplied(string species, Sex sex, Marking marking, IPrototypeManager? prototypeManager = null) diff --git a/Content.Shared/Roles/Jobs/SharedJobSystem.cs b/Content.Shared/Roles/Jobs/SharedJobSystem.cs index 0acda7425f..fe0f9d115b 100644 --- a/Content.Shared/Roles/Jobs/SharedJobSystem.cs +++ b/Content.Shared/Roles/Jobs/SharedJobSystem.cs @@ -22,19 +22,14 @@ public abstract class SharedJobSystem : EntitySystem public override void Initialize() { base.Initialize(); - _protoManager.PrototypesReloaded += OnProtoReload; + SubscribeLocalEvent(OnProtoReload); SetupTrackerLookup(); } - public override void Shutdown() - { - base.Shutdown(); - _protoManager.PrototypesReloaded -= OnProtoReload; - } - private void OnProtoReload(PrototypesReloadedEventArgs obj) { - SetupTrackerLookup(); + if (obj.WasModified()) + SetupTrackerLookup(); } private void SetupTrackerLookup()