Use ECS prototype-reload events (#22613)

* Use ECS prototype-reload events

* better constructors

* Maybe this fixes tests?
This commit is contained in:
Leon Friedrich
2023-12-22 09:13:45 -05:00
committed by GitHub
parent 053c1e877f
commit b6bd82caa6
23 changed files with 135 additions and 242 deletions

View File

@@ -67,7 +67,7 @@ public sealed partial class ContentAudioSystem
_nextAudio = TimeSpan.MaxValue; _nextAudio = TimeSpan.MaxValue;
SetupAmbientSounds(); SetupAmbientSounds();
_proto.PrototypesReloaded += OnProtoReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
_state.OnStateChanged += OnStateChange; _state.OnStateChanged += OnStateChange;
// On round end summary OR lobby cut audio. // On round end summary OR lobby cut audio.
SubscribeNetworkEvent<RoundEndMessageEvent>(OnRoundEndMessage); SubscribeNetworkEvent<RoundEndMessageEvent>(OnRoundEndMessage);
@@ -86,21 +86,14 @@ public sealed partial class ContentAudioSystem
private void ShutdownAmbientMusic() private void ShutdownAmbientMusic()
{ {
_configManager.UnsubValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged); _configManager.UnsubValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged);
_proto.PrototypesReloaded -= OnProtoReload;
_state.OnStateChanged -= OnStateChange; _state.OnStateChanged -= OnStateChange;
_ambientMusicStream = _audio.Stop(_ambientMusicStream); _ambientMusicStream = _audio.Stop(_ambientMusicStream);
} }
private void OnProtoReload(PrototypesReloadedEventArgs obj) private void OnProtoReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.ByType.ContainsKey(typeof(AmbientMusicPrototype)) && if (obj.WasModified<AmbientMusicPrototype>() || obj.WasModified<RulesPrototype>())
!obj.ByType.ContainsKey(typeof(RulesPrototype))) SetupAmbientSounds();
{
return;
}
_ambientSounds.Clear();
SetupAmbientSounds();
} }
private void OnStateChange(StateChangedEventArgs obj) private void OnStateChange(StateChangedEventArgs obj)
@@ -114,6 +107,7 @@ public sealed partial class ContentAudioSystem
private void SetupAmbientSounds() private void SetupAmbientSounds()
{ {
_ambientSounds.Clear();
foreach (var ambience in _proto.EnumeratePrototypes<AmbientMusicPrototype>()) foreach (var ambience in _proto.EnumeratePrototypes<AmbientMusicPrototype>())
{ {
var tracks = _ambientSounds.GetOrNew(ambience.ID); var tracks = _ambientSounds.GetOrNew(ambience.ID);

View File

@@ -29,18 +29,13 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
SubscribeLocalEvent<ChameleonClothingComponent, AfterAutoHandleStateEvent>(HandleState); SubscribeLocalEvent<ChameleonClothingComponent, AfterAutoHandleStateEvent>(HandleState);
PrepareAllVariants(); PrepareAllVariants();
_proto.PrototypesReloaded += OnProtoReloaded; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReloaded);
} }
public override void Shutdown() private void OnProtoReloaded(PrototypesReloadedEventArgs args)
{ {
base.Shutdown(); if (args.WasModified<EntityPrototype>())
_proto.PrototypesReloaded -= OnProtoReloaded; PrepareAllVariants();
}
private void OnProtoReloaded(PrototypesReloadedEventArgs _)
{
PrepareAllVariants();
} }
private void HandleState(EntityUid uid, ChameleonClothingComponent component, ref AfterAutoHandleStateEvent args) private void HandleState(EntityUid uid, ChameleonClothingComponent component, ref AfterAutoHandleStateEvent args)

View File

@@ -1,4 +1,3 @@
using Content.Client.GameTicking.Managers;
using Content.Shared.CrewManifest; using Content.Shared.CrewManifest;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -19,12 +18,7 @@ public sealed class CrewManifestSystem : EntitySystem
base.Initialize(); base.Initialize();
BuildDepartmentLookup(); BuildDepartmentLookup();
_prototypeManager.PrototypesReloaded += OnPrototypesReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReload);
}
public override void Shutdown()
{
_prototypeManager.PrototypesReloaded -= OnPrototypesReload;
} }
/// <summary> /// <summary>
@@ -36,16 +30,16 @@ public sealed class CrewManifestSystem : EntitySystem
RaiseNetworkEvent(new RequestCrewManifestMessage(netEntity)); RaiseNetworkEvent(new RequestCrewManifestMessage(netEntity));
} }
private void OnPrototypesReload(PrototypesReloadedEventArgs _) private void OnPrototypesReload(PrototypesReloadedEventArgs args)
{ {
_jobDepartmentLookup.Clear(); if (args.WasModified<DepartmentPrototype>())
_departments.Clear(); BuildDepartmentLookup();
BuildDepartmentLookup();
} }
private void BuildDepartmentLookup() private void BuildDepartmentLookup()
{ {
_jobDepartmentLookup.Clear();
_departments.Clear();
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>()) foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{ {
_departments.Add(department.ID); _departments.Add(department.ID);

View File

@@ -24,14 +24,13 @@ public sealed class ParallaxSystem : SharedParallaxSystem
{ {
base.Initialize(); base.Initialize();
_overlay.AddOverlay(new ParallaxOverlay()); _overlay.AddOverlay(new ParallaxOverlay());
_protoManager.PrototypesReloaded += OnReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnReload);
SubscribeLocalEvent<ParallaxComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState); SubscribeLocalEvent<ParallaxComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
} }
private void OnReload(PrototypesReloadedEventArgs obj) private void OnReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.ByType.ContainsKey(typeof(ParallaxPrototype))) if (!obj.WasModified<ParallaxPrototype>())
return; return;
_parallax.UnloadParallax(Fallback); _parallax.UnloadParallax(Fallback);
@@ -48,7 +47,6 @@ public sealed class ParallaxSystem : SharedParallaxSystem
{ {
base.Shutdown(); base.Shutdown();
_overlay.RemoveOverlay<ParallaxOverlay>(); _overlay.RemoveOverlay<ParallaxOverlay>();
_protoManager.PrototypesReloaded -= OnReload;
} }
private void OnAfterAutoHandleState(EntityUid uid, ParallaxComponent component, ref AfterAutoHandleStateEvent args) private void OnAfterAutoHandleState(EntityUid uid, ParallaxComponent component, ref AfterAutoHandleStateEvent args)

View File

@@ -53,7 +53,8 @@ public sealed class DecalPlacerUIController : UIController, IOnStateExited<Gamep
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj) private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
{ {
ReloadPrototypes(); if (obj.WasModified<DecalPrototype>())
ReloadPrototypes();
} }
private void ReloadPrototypes() private void ReloadPrototypes()

View File

@@ -23,15 +23,7 @@ public sealed class AlertLevelSystem : EntitySystem
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<StationInitializedEvent>(OnStationInitialize); SubscribeLocalEvent<StationInitializedEvent>(OnStationInitialize);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeReload);
_prototypeManager.PrototypesReloaded += OnPrototypeReload;
}
public override void Shutdown()
{
base.Shutdown();
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
} }
public override void Update(float time) public override void Update(float time)

View File

@@ -3,7 +3,6 @@ using Content.Shared.Audio;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Robust.Server.Audio; using Robust.Server.Audio;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server.Audio; namespace Content.Server.Audio;
@@ -11,14 +10,13 @@ namespace Content.Server.Audio;
public sealed class ContentAudioSystem : SharedContentAudioSystem public sealed class ContentAudioSystem : SharedContentAudioSystem
{ {
[Dependency] private readonly AudioSystem _serverAudio = default!; [Dependency] private readonly AudioSystem _serverAudio = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup); SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart); SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
_protoManager.PrototypesReloaded += OnProtoReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
} }
private void OnRoundCleanup(RoundRestartCleanupEvent ev) private void OnRoundCleanup(RoundRestartCleanupEvent ev)
@@ -28,16 +26,8 @@ public sealed class ContentAudioSystem : SharedContentAudioSystem
private void OnProtoReload(PrototypesReloadedEventArgs obj) private void OnProtoReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.ByType.ContainsKey(typeof(AudioPresetPrototype))) if (obj.WasModified<AudioPresetPrototype>())
return; _serverAudio.ReloadPresets();
_serverAudio.ReloadPresets();
}
public override void Shutdown()
{
base.Shutdown();
_protoManager.PrototypesReloaded -= OnProtoReload;
} }
private void OnRoundStart(RoundStartingEvent ev) private void OnRoundStart(RoundStartingEvent ev)

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using Content.Shared.Chat.Prototypes; using Content.Shared.Chat.Prototypes;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -7,44 +8,36 @@ namespace Content.Server.Chat.Systems;
// emotes using emote prototype // emotes using emote prototype
public partial class ChatSystem public partial class ChatSystem
{ {
private readonly Dictionary<string, EmotePrototype> _wordEmoteDict = new(); private FrozenDictionary<string, EmotePrototype> _wordEmoteDict = FrozenDictionary<string, EmotePrototype>.Empty;
private void InitializeEmotes() protected override void OnPrototypeReload(PrototypesReloadedEventArgs obj)
{ {
_prototypeManager.PrototypesReloaded += OnPrototypeReloadEmotes; base.OnPrototypeReload(obj);
CacheEmotes(); if (obj.WasModified<EmotePrototype>())
} CacheEmotes();
private void ShutdownEmotes()
{
_prototypeManager.PrototypesReloaded -= OnPrototypeReloadEmotes;
}
private void OnPrototypeReloadEmotes(PrototypesReloadedEventArgs obj)
{
CacheEmotes();
} }
private void CacheEmotes() private void CacheEmotes()
{ {
_wordEmoteDict.Clear(); var dict = new Dictionary<string, EmotePrototype>();
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>(); var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
foreach (var emote in emotes) foreach (var emote in emotes)
{ {
foreach (var word in emote.ChatTriggers) foreach (var word in emote.ChatTriggers)
{ {
var lowerWord = word.ToLower(); 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 {value.ID}";
var errMsg = $"Duplicate of emote word {lowerWord} in emotes {emote.ID} and {existingId}"; Log.Error(errMsg);
Logger.Error(errMsg);
continue; continue;
} }
_wordEmoteDict.Add(lowerWord, emote); dict.Add(lowerWord, emote);
} }
} }
_wordEmoteDict = dict.ToFrozenDictionary();
} }
/// <summary> /// <summary>

View File

@@ -69,7 +69,7 @@ public sealed partial class ChatSystem : SharedChatSystem
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
InitializeEmotes(); CacheEmotes();
_configurationManager.OnValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged, true);
_configurationManager.OnValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged, true);
_configurationManager.OnValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged, true);
@@ -80,7 +80,6 @@ public sealed partial class ChatSystem : SharedChatSystem
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
ShutdownEmotes();
_configurationManager.UnsubValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged); _configurationManager.UnsubValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged);
_configurationManager.UnsubValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged); _configurationManager.UnsubValueChanged(CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged);
_configurationManager.UnsubValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged); _configurationManager.UnsubValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged);
@@ -736,7 +735,7 @@ public sealed partial class ChatSystem : SharedChatSystem
return ev.Message; return ev.Message;
} }
public bool CheckIgnoreSpeechBlocker(EntityUid sender, bool ignoreBlocker) public bool CheckIgnoreSpeechBlocker(EntityUid sender, bool ignoreBlocker)
{ {
if (ignoreBlocker) if (ignoreBlocker)

View File

@@ -17,8 +17,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
{ {
base.Initialize(); base.Initialize();
PrototypeManager.PrototypesReloaded += PrototypeManagerReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(PrototypeManagerReload);
_player.PlayerStatusChanged += OnPlayerStatusChanged; _player.PlayerStatusChanged += OnPlayerStatusChanged;
InitializeServerRegistry(); InitializeServerRegistry();

View File

@@ -31,7 +31,7 @@ public sealed class RandomGiftSystem : EntitySystem
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
_prototype.PrototypesReloaded += OnPrototypesReloaded; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
SubscribeLocalEvent<RandomGiftComponent, MapInitEvent>(OnGiftMapInit); SubscribeLocalEvent<RandomGiftComponent, MapInitEvent>(OnGiftMapInit);
SubscribeLocalEvent<RandomGiftComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<RandomGiftComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<RandomGiftComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<RandomGiftComponent, ExaminedEvent>(OnExamined);
@@ -80,7 +80,8 @@ public sealed class RandomGiftSystem : EntitySystem
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj) private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
{ {
BuildIndex(); if (obj.WasModified<EntityPrototype>())
BuildIndex();
} }
private void BuildIndex() private void BuildIndex()

View File

@@ -10,7 +10,6 @@ using Content.Shared.Administration;
using Content.Shared.Mobs; using Content.Shared.Mobs;
using Content.Shared.NPC; using Content.Shared.NPC;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -38,8 +37,7 @@ public sealed class HTNSystem : EntitySystem
SubscribeLocalEvent<HTNComponent, PlayerDetachedEvent>(_npc.OnPlayerNPCDetach); SubscribeLocalEvent<HTNComponent, PlayerDetachedEvent>(_npc.OnPlayerNPCDetach);
SubscribeLocalEvent<HTNComponent, ComponentShutdown>(OnHTNShutdown); SubscribeLocalEvent<HTNComponent, ComponentShutdown>(OnHTNShutdown);
SubscribeNetworkEvent<RequestHTNMessage>(OnHTNMessage); SubscribeNetworkEvent<RequestHTNMessage>(OnHTNMessage);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeLoad);
_prototypeManager.PrototypesReloaded += OnPrototypeLoad;
OnLoad(); OnLoad();
} }
@@ -57,12 +55,6 @@ public sealed class HTNSystem : EntitySystem
_subscribers.Remove(args.SenderSession); _subscribers.Remove(args.SenderSession);
} }
public override void Shutdown()
{
base.Shutdown();
_prototypeManager.PrototypesReloaded -= OnPrototypeLoad;
}
private void OnLoad() private void OnLoad()
{ {
// Clear all NPCs in case they're hanging onto stale tasks // Clear all NPCs in case they're hanging onto stale tasks

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Linq; using System.Linq;
using Content.Server.NPC.Components; using Content.Server.NPC.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -18,31 +19,23 @@ public sealed partial class NpcFactionSystem : EntitySystem
/// <summary> /// <summary>
/// To avoid prototype mutability we store an intermediary data class that gets used instead. /// To avoid prototype mutability we store an intermediary data class that gets used instead.
/// </summary> /// </summary>
private Dictionary<string, FactionData> _factions = new(); private FrozenDictionary<string, FactionData> _factions = FrozenDictionary<string, FactionData>.Empty;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_sawmill = Logger.GetSawmill("faction"); _sawmill = Logger.GetSawmill("faction");
SubscribeLocalEvent<NpcFactionMemberComponent, ComponentStartup>(OnFactionStartup); SubscribeLocalEvent<NpcFactionMemberComponent, ComponentStartup>(OnFactionStartup);
_protoManager.PrototypesReloaded += OnProtoReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
InitializeException(); InitializeException();
RefreshFactions(); RefreshFactions();
} }
public override void Shutdown()
{
base.Shutdown();
_protoManager.PrototypesReloaded -= OnProtoReload;
}
private void OnProtoReload(PrototypesReloadedEventArgs obj) private void OnProtoReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.ByType.ContainsKey(typeof(NpcFactionPrototype))) if (obj.WasModified<NpcFactionPrototype>())
return; RefreshFactions();
RefreshFactions();
} }
private void OnFactionStartup(EntityUid uid, NpcFactionMemberComponent memberComponent, ComponentStartup args) private void OnFactionStartup(EntityUid uid, NpcFactionMemberComponent memberComponent, ComponentStartup args)
@@ -237,16 +230,15 @@ public sealed partial class NpcFactionSystem : EntitySystem
private void RefreshFactions() private void RefreshFactions()
{ {
_factions.Clear();
foreach (var faction in _protoManager.EnumeratePrototypes<NpcFactionPrototype>()) _factions = _protoManager.EnumeratePrototypes<NpcFactionPrototype>().ToFrozenDictionary(
{ faction => faction.ID,
_factions[faction.ID] = new FactionData() faction => new FactionData
{ {
Friendly = faction.Friendly.ToHashSet(), Friendly = faction.Friendly.ToHashSet(),
Hostile = faction.Hostile.ToHashSet(), Hostile = faction.Hostile.ToHashSet()
};
} });
foreach (var comp in EntityQuery<NpcFactionMemberComponent>(true)) foreach (var comp in EntityQuery<NpcFactionMemberComponent>(true))
{ {

View File

@@ -28,9 +28,9 @@ public sealed class NameIdentifierSystem : EntitySystem
SubscribeLocalEvent<NameIdentifierComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<NameIdentifierComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<NameIdentifierComponent, ComponentShutdown>(OnComponentShutdown); SubscribeLocalEvent<NameIdentifierComponent, ComponentShutdown>(OnComponentShutdown);
SubscribeLocalEvent<RoundRestartCleanupEvent>(CleanupIds); SubscribeLocalEvent<RoundRestartCleanupEvent>(CleanupIds);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnReloadPrototypes);
InitialSetupPrototypes(); InitialSetupPrototypes();
_prototypeManager.PrototypesReloaded += OnReloadPrototypes;
} }
private void OnComponentShutdown(EntityUid uid, NameIdentifierComponent component, ComponentShutdown args) 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;
}
/// <summary> /// <summary>
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/> /// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
/// but does not set the entity's name. /// but does not set the entity's name.

View File

@@ -85,14 +85,13 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
SubscribeLocalEvent<ShuttleFlattenEvent>(OnShuttleFlatten); SubscribeLocalEvent<ShuttleFlattenEvent>(OnShuttleFlatten);
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true); _configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true);
InitializeCommands(); InitializeCommands();
ProtoManager.PrototypesReloaded += ProtoReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(ProtoReload);
} }
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange); _configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange);
ProtoManager.PrototypesReloaded -= ProtoReload;
} }
private void ProtoReload(PrototypesReloadedEventArgs obj) private void ProtoReload(PrototypesReloadedEventArgs obj)

View File

@@ -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", 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_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); _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<PrototypesReloadedEventArgs>(PrototypeReload);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup); SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart); SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
} }
@@ -91,8 +91,6 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
_prototype.PrototypesReloaded -= PrototypeReload;
foreach (var token in _dungeonJobs.Values) foreach (var token in _dungeonJobs.Values)
{ {
token.Cancel(); token.Cancel();

View File

@@ -30,6 +30,7 @@ public sealed class SpreaderSystem : EntitySystem
/// <summary> /// <summary>
/// Remaining number of updates per grid & prototype. /// Remaining number of updates per grid & prototype.
/// </summary> /// </summary>
// TODO PERFORMANCE Assign each prototype to an index and convert dictionary to array
private Dictionary<EntityUid, Dictionary<string, int>> _gridUpdates = new(); private Dictionary<EntityUid, Dictionary<string, int>> _gridUpdates = new();
public const float SpreadCooldownSeconds = 1; public const float SpreadCooldownSeconds = 1;
@@ -42,24 +43,16 @@ public sealed class SpreaderSystem : EntitySystem
{ {
SubscribeLocalEvent<AirtightChanged>(OnAirtightChanged); SubscribeLocalEvent<AirtightChanged>(OnAirtightChanged);
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit); SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeReload);
SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating); SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating);
SetupPrototypes(); SetupPrototypes();
_prototype.PrototypesReloaded += OnPrototypeReload;
}
public override void Shutdown()
{
base.Shutdown();
_prototype.PrototypesReloaded -= OnPrototypeReload;
} }
private void OnPrototypeReload(PrototypesReloadedEventArgs obj) private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.ByType.ContainsKey(typeof(EdgeSpreaderPrototype))) if (obj.WasModified<EdgeSpreaderPrototype>())
return; SetupPrototypes();
SetupPrototypes();
} }
private void SetupPrototypes() private void SetupPrototypes()

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -7,7 +8,8 @@ namespace Content.Shared.Alert;
public abstract class AlertsSystem : EntitySystem public abstract class AlertsSystem : EntitySystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly Dictionary<AlertType, AlertPrototype> _typeToAlert = new();
private FrozenDictionary<AlertType, AlertPrototype> _typeToAlert = default!;
public IReadOnlyDictionary<AlertKey, AlertState>? GetActiveAlerts(EntityUid euid) public IReadOnlyDictionary<AlertKey, AlertState>? GetActiveAlerts(EntityUid euid)
{ {
@@ -170,9 +172,8 @@ public abstract class AlertsSystem : EntitySystem
SubscribeLocalEvent<AlertsComponent, PlayerAttachedEvent>(OnPlayerAttached); SubscribeLocalEvent<AlertsComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeNetworkEvent<ClickAlertEvent>(HandleClickAlert); SubscribeNetworkEvent<ClickAlertEvent>(HandleClickAlert);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(HandlePrototypesReloaded);
LoadPrototypes(); LoadPrototypes();
_prototypeManager.PrototypesReloaded += HandlePrototypesReloaded;
} }
protected virtual void HandleComponentShutdown(EntityUid uid, AlertsComponent component, ComponentShutdown args) 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); RaiseLocalEvent(uid, new AlertSyncEvent(uid), true);
} }
public override void Shutdown()
{
_prototypeManager.PrototypesReloaded -= HandlePrototypesReloaded;
base.Shutdown();
}
private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj) private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj)
{ {
LoadPrototypes(); if (obj.WasModified<AlertPrototype>())
LoadPrototypes();
} }
protected virtual void LoadPrototypes() protected virtual void LoadPrototypes()
{ {
_typeToAlert.Clear(); var dict = new Dictionary<AlertType, AlertPrototype>();
foreach (var alert in _prototypeManager.EnumeratePrototypes<AlertPrototype>()) foreach (var alert in _prototypeManager.EnumeratePrototypes<AlertPrototype>())
{ {
if (!_typeToAlert.TryAdd(alert.AlertType, alert)) if (!dict.TryAdd(alert.AlertType, alert))
{ {
Log.Error("Found alert with duplicate alertType {0} - all alerts must have" + Log.Error("Found alert with duplicate alertType {0} - all alerts must have" +
" a unique alerttype, this one will be skipped", alert.AlertType); " a unique alerttype, this one will be skipped", alert.AlertType);
} }
} }
_typeToAlert = dict.ToFrozenDictionary();
} }
/// <summary> /// <summary>

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Radio; using Content.Shared.Radio;
using Content.Shared.Speech; using Content.Shared.Speech;
@@ -36,35 +37,26 @@ public abstract class SharedChatSystem : EntitySystem
/// <summary> /// <summary>
/// Cache of the keycodes for faster lookup. /// Cache of the keycodes for faster lookup.
/// </summary> /// </summary>
private Dictionary<char, RadioChannelPrototype> _keyCodes = new(); private FrozenDictionary<char, RadioChannelPrototype> _keyCodes = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
DebugTools.Assert(_prototypeManager.HasIndex<RadioChannelPrototype>(CommonChannel)); DebugTools.Assert(_prototypeManager.HasIndex<RadioChannelPrototype>(CommonChannel));
_prototypeManager.PrototypesReloaded += OnPrototypeReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeReload);
CacheRadios(); CacheRadios();
} }
private void OnPrototypeReload(PrototypesReloadedEventArgs obj) protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj)
{ {
if (obj.ByType.ContainsKey(typeof(RadioChannelPrototype))) if (obj.WasModified<RadioChannelPrototype>())
CacheRadios(); CacheRadios();
} }
private void CacheRadios() private void CacheRadios()
{ {
_keyCodes.Clear(); _keyCodes = _prototypeManager.EnumeratePrototypes<RadioChannelPrototype>()
.ToFrozenDictionary(x => x.KeyCode);
foreach (var proto in _prototypeManager.EnumeratePrototypes<RadioChannelPrototype>())
{
_keyCodes.Add(proto.KeyCode, proto);
}
}
public override void Shutdown()
{
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
} }
/// <summary> /// <summary>

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Linq; using System.Linq;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
@@ -7,6 +8,7 @@ using Content.Shared.FixedPoint;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.Chemistry.Reaction namespace Content.Shared.Chemistry.Reaction
{ {
@@ -22,23 +24,22 @@ namespace Content.Shared.Chemistry.Reaction
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
/// <summary> /// <summary>
/// A cache of all existant chemical reactions indexed by one of their /// A cache of all reactions indexed by at most ONE of their required reactants.
/// required reactants. /// I.e., even if a reaction has more than one reagent, it will only ever appear once in this dictionary.
/// </summary> /// </summary>
private IDictionary<string, List<ReactionPrototype>> _reactions = default!; private FrozenDictionary<string, List<ReactionPrototype>> _reactionsSingle = default!;
/// <summary>
/// A cache of all reactions indexed by one of their required reactants.
/// </summary>
private FrozenDictionary<string, List<ReactionPrototype>> _reactions = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
InitializeReactionCache(); InitializeReactionCache();
_prototypeManager.PrototypesReloaded += OnPrototypesReloaded; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
}
public override void Shutdown()
{
base.Shutdown();
_prototypeManager.PrototypesReloaded -= OnPrototypesReloaded;
} }
/// <summary> /// <summary>
@@ -46,34 +47,27 @@ namespace Content.Shared.Chemistry.Reaction
/// </summary> /// </summary>
private void InitializeReactionCache() private void InitializeReactionCache()
{ {
_reactions = new Dictionary<string, List<ReactionPrototype>>(); // Construct single-reaction dictionary.
var dict = new Dictionary<string, List<ReactionPrototype>>();
var reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>(); foreach(var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
foreach(var reaction in reactions)
{ {
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();
/// <summary> dict.Clear();
/// Caches a reaction by its first required reagent. foreach(var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
/// Used to build the reaction cache.
/// </summary>
/// <param name="reaction">A reaction prototype to cache.</param>
private void CacheReaction(ReactionPrototype reaction)
{
var reagents = reaction.Reactants.Keys;
foreach(var reagent in reagents)
{ {
if(!_reactions.TryGetValue(reagent, out var cache)) foreach (var reagent in reaction.Reactants.Keys)
{ {
cache = new List<ReactionPrototype>(); var list = dict.GetOrNew(reagent);
_reactions.Add(reagent, cache); list.Add(reaction);
} }
cache.Add(reaction);
return; // Only need to cache based on the first reagent.
} }
_reactions = dict.ToFrozenDictionary();
} }
/// <summary> /// <summary>
@@ -82,20 +76,8 @@ namespace Content.Shared.Chemistry.Reaction
/// <param name="eventArgs">The set of modified prototypes.</param> /// <param name="eventArgs">The set of modified prototypes.</param>
private void OnPrototypesReloaded(PrototypesReloadedEventArgs eventArgs) private void OnPrototypesReloaded(PrototypesReloadedEventArgs eventArgs)
{ {
if (!eventArgs.ByType.TryGetValue(typeof(ReactionPrototype), out var set)) if (eventArgs.WasModified<ReactionPrototype>())
return; InitializeReactionCache();
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);
}
} }
/// <summary> /// <summary>
@@ -285,7 +267,7 @@ namespace Content.Shared.Chemistry.Reaction
SortedSet<ReactionPrototype> reactions = new(); SortedSet<ReactionPrototype> reactions = new();
foreach (var reactant in solution.Contents) 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); reactions.UnionWith(reactantReactions);
} }
@@ -297,7 +279,7 @@ namespace Content.Shared.Chemistry.Reaction
return; 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.");
} }
} }

View File

@@ -83,6 +83,9 @@ namespace Content.Shared.Entry
private void PrototypeReload(PrototypesReloadedEventArgs obj) private void PrototypeReload(PrototypesReloadedEventArgs obj)
{ {
if (!obj.WasModified<ContentTileDefinition>())
return;
// Need to re-allocate tiledefs due to how prototype reloads work // Need to re-allocate tiledefs due to how prototype reloads work
foreach (var def in _prototypeManager.EnumeratePrototypes<ContentTileDefinition>()) foreach (var def in _prototypeManager.EnumeratePrototypes<ContentTileDefinition>())
{ {

View File

@@ -1,4 +1,6 @@
using System.Collections.Frozen;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Humanoid.Prototypes; using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -9,33 +11,41 @@ namespace Content.Shared.Humanoid.Markings
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly List<MarkingPrototype> _index = new(); private readonly List<MarkingPrototype> _index = new();
private readonly Dictionary<MarkingCategories, Dictionary<string, MarkingPrototype>> _markingDict = new(); public FrozenDictionary<MarkingCategories, FrozenDictionary<string, MarkingPrototype>> CategorizedMarkings = default!;
private readonly Dictionary<string, MarkingPrototype> _markings = new(); public FrozenDictionary<string, MarkingPrototype> Markings = default!;
public void Initialize() public void Initialize()
{ {
_prototypeManager.PrototypesReloaded += OnPrototypeReload; _prototypeManager.PrototypesReloaded += OnPrototypeReload;
CachePrototypes();
}
private void CachePrototypes()
{
_index.Clear();
var markingDict = new Dictionary<MarkingCategories, Dictionary<string, MarkingPrototype>>();
foreach (var category in Enum.GetValues<MarkingCategories>()) foreach (var category in Enum.GetValues<MarkingCategories>())
{ {
_markingDict.Add(category, new Dictionary<string, MarkingPrototype>()); markingDict.Add(category, new());
} }
foreach (var prototype in _prototypeManager.EnumeratePrototypes<MarkingPrototype>()) foreach (var prototype in _prototypeManager.EnumeratePrototypes<MarkingPrototype>())
{ {
_index.Add(prototype); _index.Add(prototype);
_markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype); markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype);
_markings.Add(prototype.ID, prototype);
} }
Markings = _prototypeManager.EnumeratePrototypes<MarkingPrototype>().ToFrozenDictionary(x => x.ID);
CategorizedMarkings = markingDict.ToFrozenDictionary(
x => x.Key,
x => x.Value.ToFrozenDictionary());
} }
public IReadOnlyDictionary<string, MarkingPrototype> Markings => _markings; public FrozenDictionary<string, MarkingPrototype> MarkingsByCategory(MarkingCategories category)
public IReadOnlyDictionary<MarkingCategories, Dictionary<string, MarkingPrototype>> CategorizedMarkings => _markingDict;
public IReadOnlyDictionary<string, MarkingPrototype> MarkingsByCategory(MarkingCategories category)
{ {
// all marking categories are guaranteed to have a dict entry // all marking categories are guaranteed to have a dict entry
return _markingDict[category]; return CategorizedMarkings[category];
} }
/// <summary> /// <summary>
@@ -143,7 +153,7 @@ namespace Content.Shared.Humanoid.Markings
public bool TryGetMarking(Marking marking, [NotNullWhen(true)] out MarkingPrototype? markingResult) public bool TryGetMarking(Marking marking, [NotNullWhen(true)] out MarkingPrototype? markingResult)
{ {
return _markings.TryGetValue(marking.MarkingId, out markingResult); return Markings.TryGetValue(marking.MarkingId, out markingResult);
} }
/// <summary> /// <summary>
@@ -178,17 +188,8 @@ namespace Content.Shared.Humanoid.Markings
private void OnPrototypeReload(PrototypesReloadedEventArgs args) private void OnPrototypeReload(PrototypesReloadedEventArgs args)
{ {
if(!args.ByType.TryGetValue(typeof(MarkingPrototype), out var set)) if (args.WasModified<MarkingPrototype>())
return; CachePrototypes();
_index.RemoveAll(i => set.Modified.ContainsKey(i.ID));
foreach (var prototype in set.Modified.Values)
{
var markingPrototype = (MarkingPrototype) prototype;
_index.Add(markingPrototype);
}
} }
public bool CanBeApplied(string species, Sex sex, Marking marking, IPrototypeManager? prototypeManager = null) public bool CanBeApplied(string species, Sex sex, Marking marking, IPrototypeManager? prototypeManager = null)

View File

@@ -22,19 +22,14 @@ public abstract class SharedJobSystem : EntitySystem
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_protoManager.PrototypesReloaded += OnProtoReload; SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
SetupTrackerLookup(); SetupTrackerLookup();
} }
public override void Shutdown()
{
base.Shutdown();
_protoManager.PrototypesReloaded -= OnProtoReload;
}
private void OnProtoReload(PrototypesReloadedEventArgs obj) private void OnProtoReload(PrototypesReloadedEventArgs obj)
{ {
SetupTrackerLookup(); if (obj.WasModified<JobPrototype>())
SetupTrackerLookup();
} }
private void SetupTrackerLookup() private void SetupTrackerLookup()