From 4eee1ee9b247b66eb8958728d8995819d3bd88f8 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 25 Dec 2022 16:22:23 -0500 Subject: [PATCH] clean up infinite researchsystem shitcode (#13136) * clean up infinite researchsystem shitcode * fml some more shit * make syncing work logically * naming naming naming --- .../Lathe/UI/LatheBoundUserInterface.cs | 5 +- .../UI/DiskConsoleBoundUserInterface.cs | 4 +- .../UI/ResearchConsoleBoundUserInterface.cs | 2 +- Content.Server/Lathe/LatheSystem.cs | 24 +-- .../Components/ResearchClientComponent.cs | 11 -- .../Components/ResearchConsoleComponent.cs | 9 +- .../ResearchPointSourceComponent.cs | 19 +- .../Research/Disk/ResearchDiskSystem.cs | 5 +- .../Research/Systems/ResearchSystem.Client.cs | 102 ++++++---- .../Systems/ResearchSystem.Console.cs | 71 +++---- .../Systems/ResearchSystem.PointSource.cs | 1 + .../Research/Systems/ResearchSystem.Server.cs | 174 ++++++++++++------ .../Systems/ResearchSystem.Technology.cs | 145 ++++++++++----- .../Research/Systems/ResearchSystem.cs | 27 +-- .../Systems/DiskConsoleSystem.cs | 24 ++- .../Systems/TechnologyDiskSystem.cs | 3 +- .../Systems/ArtifactAnalyzerSystem.cs | 9 +- Content.Shared/Lathe/LatheMessages.cs | 15 +- ...omponent.cs => ResearchClientComponent.cs} | 29 ++- .../Components/ResearchServerComponent.cs | 37 +++- .../SharedResearchConsoleComponent.cs | 6 +- .../Components/TechnologyDatabaseComponent.cs | 18 ++ .../Research/Systems/SharedResearchSystem.cs | 50 ++--- 23 files changed, 497 insertions(+), 293 deletions(-) delete mode 100644 Content.Server/Research/Components/ResearchClientComponent.cs rename Content.Shared/Research/Components/{SharedResearchClientComponent.cs => ResearchClientComponent.cs} (69%) rename {Content.Server => Content.Shared}/Research/Components/ResearchServerComponent.cs (60%) diff --git a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs index 08850896d0..ce79e8e4e4 100644 --- a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs +++ b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Lathe; +using Content.Shared.Research.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; @@ -32,11 +33,11 @@ namespace Content.Client.Lathe.UI }; _menu.OnServerListButtonPressed += _ => { - SendMessage(new LatheServerSelectionMessage()); + SendMessage(new ConsoleServerSelectionMessage()); }; _menu.OnServerSyncButtonPressed += _ => { - SendMessage(new LatheServerSyncMessage()); + SendMessage(new ConsoleServerSyncMessage()); }; _menu.RecipeQueueAction += (recipe, amount) => { diff --git a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs index da167960f9..b3bce0c99b 100644 --- a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs +++ b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs @@ -1,5 +1,5 @@ -using Content.Shared.Lathe; using Content.Shared.Research; +using Content.Shared.Research.Components; using Robust.Client.GameObjects; namespace Content.Client.Research.UI @@ -24,7 +24,7 @@ namespace Content.Client.Research.UI _menu.OnServerButtonPressed += () => { - SendMessage(new LatheServerSelectionMessage()); + SendMessage(new ConsoleServerSelectionMessage()); }; _menu.OnPrintButtonPressed += () => { diff --git a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs index e6a8d349ad..c9a3fb4379 100644 --- a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs +++ b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs @@ -68,7 +68,7 @@ namespace Content.Client.Research.UI if (_technologyDatabase == null) return false; - return _research.CanUnlockTechnology(_technologyDatabase.Owner, technology, _technologyDatabase); + return _research.ArePrerequesitesUnlocked(_technologyDatabase.Owner, technology, _technologyDatabase); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index caeeac62ea..8d1ddda670 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -5,8 +5,6 @@ using Content.Server.Lathe.Components; using Content.Server.Materials; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Server.Research.Components; -using Content.Server.Research.Systems; using Content.Server.UserInterface; using Content.Shared.Lathe; using Content.Shared.Materials; @@ -14,7 +12,6 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using JetBrains.Annotations; using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -28,7 +25,6 @@ namespace Content.Server.Lathe [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UserInterfaceSystem _uiSys = default!; - [Dependency] private readonly ResearchSystem _researchSys = default!; [Dependency] private readonly MaterialStorageSystem _materialStorage = default!; public override void Initialize() @@ -40,6 +36,7 @@ namespace Content.Server.Lathe SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnPartsRefresh); SubscribeLocalEvent(OnUpgradeExamine); + SubscribeLocalEvent(OnDatabaseModified); SubscribeLocalEvent(OnLatheQueueRecipeMessage); SubscribeLocalEvent(OnLatheSyncRequestMessage); @@ -48,8 +45,6 @@ namespace Content.Server.Lathe SubscribeLocalEvent((u,c,_) => UpdateUserInterfaceState(u,c)); SubscribeLocalEvent(OnGetRecipes); - SubscribeLocalEvent(OnLatheServerSelectionMessage); - SubscribeLocalEvent(OnLatheServerSyncMessage); } public override void Update(float frameTime) @@ -276,6 +271,11 @@ namespace Content.Server.Lathe args.AddPercentageUpgrade("lathe-component-upgrade-material-use", component.MaterialUseMultiplier); } + private void OnDatabaseModified(EntityUid uid, LatheComponent component, ref TechnologyDatabaseModifiedEvent args) + { + UpdateUserInterfaceState(uid, component); + } + protected override bool HasRecipe(EntityUid uid, LatheRecipePrototype recipe, LatheComponent component) { return GetAvailableRecipes(component).Contains(recipe.ID); @@ -300,18 +300,6 @@ namespace Content.Server.Lathe { UpdateUserInterfaceState(uid, component); } - - private void OnLatheServerSelectionMessage(EntityUid uid, ResearchClientComponent component, LatheServerSelectionMessage args) - { - _uiSys.TryOpen(uid, ResearchClientUiKey.Key, (IPlayerSession) args.Session); - } - - private void OnLatheServerSyncMessage(EntityUid uid, TechnologyDatabaseComponent component, LatheServerSyncMessage args) - { - _researchSys.SyncWithServer(component); - UpdateUserInterfaceState(uid); - } - #endregion } } diff --git a/Content.Server/Research/Components/ResearchClientComponent.cs b/Content.Server/Research/Components/ResearchClientComponent.cs deleted file mode 100644 index 322139fb4c..0000000000 --- a/Content.Server/Research/Components/ResearchClientComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Server.Research.Components -{ - [RegisterComponent] - public sealed class ResearchClientComponent : Component - { - public bool ConnectedToServer => Server != null; - - [ViewVariables(VVAccess.ReadOnly)] - public ResearchServerComponent? Server { get; set; } - } -} diff --git a/Content.Server/Research/Components/ResearchConsoleComponent.cs b/Content.Server/Research/Components/ResearchConsoleComponent.cs index 30663b0819..39910a3283 100644 --- a/Content.Server/Research/Components/ResearchConsoleComponent.cs +++ b/Content.Server/Research/Components/ResearchConsoleComponent.cs @@ -1,5 +1,8 @@ -namespace Content.Server.Research.Components +namespace Content.Server.Research.Components; + +[RegisterComponent] +public sealed class ResearchConsoleComponent : Component { - [RegisterComponent] - public sealed class ResearchConsoleComponent : Component {} + } + diff --git a/Content.Server/Research/Components/ResearchPointSourceComponent.cs b/Content.Server/Research/Components/ResearchPointSourceComponent.cs index 5ca0b8c4da..10fc27ba87 100644 --- a/Content.Server/Research/Components/ResearchPointSourceComponent.cs +++ b/Content.Server/Research/Components/ResearchPointSourceComponent.cs @@ -1,12 +1,11 @@ -namespace Content.Server.Research.Components -{ - [RegisterComponent] - public sealed class ResearchPointSourceComponent : Component - { - [DataField("pointspersecond"), ViewVariables(VVAccess.ReadWrite)] - public int PointsPerSecond; +namespace Content.Server.Research.Components; - [DataField("active"), ViewVariables(VVAccess.ReadWrite)] - public bool Active; - } +[RegisterComponent] +public sealed class ResearchPointSourceComponent : Component +{ + [DataField("pointspersecond"), ViewVariables(VVAccess.ReadWrite)] + public int PointsPerSecond; + + [DataField("active"), ViewVariables(VVAccess.ReadWrite)] + public bool Active; } diff --git a/Content.Server/Research/Disk/ResearchDiskSystem.cs b/Content.Server/Research/Disk/ResearchDiskSystem.cs index c1a5a12bee..9d257c89da 100644 --- a/Content.Server/Research/Disk/ResearchDiskSystem.cs +++ b/Content.Server/Research/Disk/ResearchDiskSystem.cs @@ -1,10 +1,9 @@ using System.Linq; using Content.Shared.Interaction; -using Content.Server.Research.Components; using Content.Server.Popups; using Content.Shared.Research.Prototypes; using Content.Server.Research.Systems; -using Robust.Shared.Player; +using Content.Shared.Research.Components; using Robust.Shared.Prototypes; namespace Content.Server.Research.Disk @@ -29,7 +28,7 @@ namespace Content.Server.Research.Disk if (!TryComp(args.Target, out var server)) return; - _research.ChangePointsOnServer(server.Owner, component.Points, server); + _research.AddPointsToServer(server.Owner, component.Points, server); _popupSystem.PopupEntity(Loc.GetString("research-disk-inserted", ("points", component.Points)), args.Target.Value, args.User); EntityManager.QueueDeleteEntity(uid); } diff --git a/Content.Server/Research/Systems/ResearchSystem.Client.cs b/Content.Server/Research/Systems/ResearchSystem.Client.cs index 80aa0eca46..d2b96be9ea 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Client.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Client.cs @@ -1,6 +1,8 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.Research.Components; +using Content.Server.Power.EntitySystems; using Content.Shared.Research.Components; +using Robust.Server.Player; namespace Content.Server.Research.Systems; @@ -11,80 +13,116 @@ public sealed partial class ResearchSystem SubscribeLocalEvent(OnClientStartup); SubscribeLocalEvent(OnClientShutdown); SubscribeLocalEvent(OnClientUIOpen); + SubscribeLocalEvent(OnConsoleSync); + SubscribeLocalEvent(OnConsoleSelect); SubscribeLocalEvent(OnClientSyncMessage); SubscribeLocalEvent(OnClientSelected); SubscribeLocalEvent(OnClientDeselected); + SubscribeLocalEvent(OnClientRegistrationChanged); } #region UI - private void OnClientDeselected(EntityUid uid, ResearchClientComponent component, ResearchClientServerDeselectedMessage args) - { - UnregisterClientServer(component); - UpdateClientInterface(component); - - if (TryComp(uid, out var console)) - { - UpdateConsoleInterface(console, component); - } - } - private void OnClientSelected(EntityUid uid, ResearchClientComponent component, ResearchClientServerSelectedMessage args) { - UnregisterClientServer(component); - RegisterClientServer(component, GetServerById(args.ServerId)); - UpdateClientInterface(component); + var server = GetServerById(args.ServerId); + if (server == null) + return; - if (TryComp(uid, out var console)) - { - UpdateConsoleInterface(console, component); - } + UnregisterClient(uid, clientComponent: component); + RegisterClient(uid, server.Owner, component, server); + } + + private void OnClientDeselected(EntityUid uid, ResearchClientComponent component, ResearchClientServerDeselectedMessage args) + { + UnregisterClient(uid, clientComponent: component); } private void OnClientSyncMessage(EntityUid uid, ResearchClientComponent component, ResearchClientSyncMessage args) { - UpdateClientInterface(component); + UpdateClientInterface(uid, component); } + private void OnConsoleSelect(EntityUid uid, ResearchClientComponent component, ConsoleServerSelectionMessage args) + { + if (!HasComp(uid) || !this.IsPowered(uid, EntityManager)) + return; + + _uiSystem.TryToggleUi(uid, ResearchClientUiKey.Key, (IPlayerSession) args.Session); + } + + private void OnConsoleSync(EntityUid uid, ResearchClientComponent component, ConsoleServerSyncMessage args) + { + if (!this.IsPowered(uid, EntityManager)) + return; + + SyncClientWithServer(uid); + } #endregion + private void OnClientRegistrationChanged(EntityUid uid, ResearchClientComponent component, ref ResearchRegistrationChangedEvent args) + { + UpdateClientInterface(uid, component); + } + private void OnClientStartup(EntityUid uid, ResearchClientComponent component, ComponentStartup args) { var allServers = EntityQuery(true).ToArray(); if (allServers.Length > 0) - RegisterClientServer(component, allServers[0]); + RegisterClient(uid, allServers[0].Owner, component, allServers[0]); } private void OnClientShutdown(EntityUid uid, ResearchClientComponent component, ComponentShutdown args) { - UnregisterClientServer(component); + UnregisterClient(uid, component); } private void OnClientUIOpen(EntityUid uid, ResearchClientComponent component, BoundUIOpenedEvent args) { - UpdateClientInterface(component); + UpdateClientInterface(uid, component); } - private void UpdateClientInterface(ResearchClientComponent component) + private void UpdateClientInterface(EntityUid uid, ResearchClientComponent? component = null) { + if (!Resolve(uid, ref component, false)) + return; + + if (!TryGetClientServer(uid, out _, out var serverComponent, component)) + return; + var names = GetServerNames(); var state = new ResearchClientBoundInterfaceState(names.Length, names, - GetServerIds(), component.ConnectedToServer ? component.Server!.Id : -1); + GetServerIds(), component.ConnectedToServer ? serverComponent.Id : -1); _uiSystem.TrySetUiState(component.Owner, ResearchClientUiKey.Key, state); } - private bool RegisterClientServer(ResearchClientComponent component, ResearchServerComponent? server = null) + /// + /// Tries to get the server belonging to a client + /// + /// The client + /// It's server. Null if false. + /// The server's ResearchServerComponent. Null if false + /// The client's Researchclient component + /// If the server was successfully retrieved. + public bool TryGetClientServer(EntityUid uid, [NotNullWhen(returnValue: true)] out EntityUid? server, + [NotNullWhen(returnValue: true)] out ResearchServerComponent? serverComponent, ResearchClientComponent? component = null) { - return server != null && RegisterServerClient(server, component.Owner, component); - } + server = null; + serverComponent = null; + + if (!Resolve(uid, ref component, false)) + return false; - private void UnregisterClientServer(ResearchClientComponent component) - { if (component.Server == null) - return; + return false; - UnregisterServerClient(component.Server, component.Owner, component); + if (!TryComp(component.Server, out var sc)) + return false; + + server = component.Server; + serverComponent = sc; + return true; } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Console.cs b/Content.Server/Research/Systems/ResearchSystem.Console.cs index 360439ec17..9e2f29dafc 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Console.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Console.cs @@ -1,8 +1,6 @@ using Content.Server.Power.EntitySystems; using Content.Server.Research.Components; using Content.Shared.Research.Components; -using Content.Shared.Research.Prototypes; -using Robust.Server.Player; namespace Content.Server.Research.Systems; @@ -11,66 +9,39 @@ public sealed partial class ResearchSystem private void InitializeConsole() { SubscribeLocalEvent(OnConsoleUnlock); - SubscribeLocalEvent(OnConsoleSync); - SubscribeLocalEvent(OnConsoleSelect); SubscribeLocalEvent(OnPointsChanged); - } - - private void OnConsoleSelect(EntityUid uid, ResearchConsoleComponent component, ConsoleServerSelectionMessage args) - { - if (!HasComp(uid) || - !HasComp(uid) || - !this.IsPowered(uid, EntityManager)) - return; - - _uiSystem.TryOpen(uid, ResearchClientUiKey.Key, (IPlayerSession) args.Session); - } - - private void OnConsoleSync(EntityUid uid, ResearchConsoleComponent component, ConsoleServerSyncMessage args) - { - if (!TryComp(uid, out var database) || - !HasComp(uid) || - !this.IsPowered(uid, EntityManager)) - return; - - SyncWithServer(database); - UpdateConsoleInterface(component); + SubscribeLocalEvent(OnConsoleRegistrationChanged); + SubscribeLocalEvent(OnConsoleDatabaseModified); } private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, ConsoleUnlockTechnologyMessage args) { - if (!TryComp(uid, out var database) || - !TryComp(uid, out var client) || - !this.IsPowered(uid, EntityManager)) + if (!this.IsPowered(uid, EntityManager)) return; - if (!_prototypeManager.TryIndex(args.Id, out TechnologyPrototype? tech) || - client.Server == null || - !CanUnlockTechnology(client.Server, tech)) + if (!UnlockTechnology(uid, args.Id)) return; - if (!UnlockTechnology(client.Server, tech)) - return; - - SyncWithServer(database); - Dirty(database); - UpdateConsoleInterface(component); + SyncClientWithServer(uid); + UpdateConsoleInterface(uid, component); } - private void UpdateConsoleInterface(ResearchConsoleComponent component, ResearchClientComponent? clientComponent = null) + private void UpdateConsoleInterface(EntityUid uid, ResearchConsoleComponent? component = null, ResearchClientComponent? clientComponent = null) { + if (!Resolve(uid, ref component, ref clientComponent, false)) + return; + ResearchConsoleBoundInterfaceState state; - if (!Resolve(component.Owner, ref clientComponent, false) || - clientComponent.Server == null) + if (TryGetClientServer(uid, out var server, out var serverComponent, clientComponent)) { - state = new ResearchConsoleBoundInterfaceState(default, default); + var points = clientComponent.ConnectedToServer ? serverComponent.Points : 0; + var pointsPerSecond = clientComponent.ConnectedToServer ? PointsPerSecond(server.Value, serverComponent) : 0; + state = new ResearchConsoleBoundInterfaceState(points, pointsPerSecond); } else { - var points = clientComponent.ConnectedToServer ? clientComponent.Server.Points : 0; - var pointsPerSecond = clientComponent.ConnectedToServer ? PointsPerSecond(clientComponent.Server) : 0; - state = new ResearchConsoleBoundInterfaceState(points, pointsPerSecond); + state = new ResearchConsoleBoundInterfaceState(default, default); } _uiSystem.TrySetUiState(component.Owner, ResearchConsoleUiKey.Key, state); @@ -80,7 +51,17 @@ public sealed partial class ResearchSystem { if (!_uiSystem.IsUiOpen(uid, ResearchConsoleUiKey.Key)) return; - UpdateConsoleInterface(component); + UpdateConsoleInterface(uid, component); } + private void OnConsoleRegistrationChanged(EntityUid uid, ResearchConsoleComponent component, ref ResearchRegistrationChangedEvent args) + { + SyncClientWithServer(uid); + UpdateConsoleInterface(uid, component); + } + + private void OnConsoleDatabaseModified(EntityUid uid, ResearchConsoleComponent component, ref TechnologyDatabaseModifiedEvent args) + { + UpdateConsoleInterface(uid, component); + } } diff --git a/Content.Server/Research/Systems/ResearchSystem.PointSource.cs b/Content.Server/Research/Systems/ResearchSystem.PointSource.cs index a1829cc10f..ed8e2eb0a8 100644 --- a/Content.Server/Research/Systems/ResearchSystem.PointSource.cs +++ b/Content.Server/Research/Systems/ResearchSystem.PointSource.cs @@ -1,5 +1,6 @@ using Content.Server.Power.EntitySystems; using Content.Server.Research.Components; +using Content.Shared.Research.Components; namespace Content.Server.Research.Systems; diff --git a/Content.Server/Research/Systems/ResearchSystem.Server.cs b/Content.Server/Research/Systems/ResearchSystem.Server.cs index c69a9872fe..acaf70e07d 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Server.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Server.cs @@ -1,97 +1,164 @@ +using System.Linq; using Content.Server.Power.EntitySystems; -using Content.Server.Research.Components; using Content.Shared.Research.Components; -using Content.Shared.Research.Prototypes; namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { - private bool CanRun(ResearchServerComponent component) + private void InitializeServer() { - return this.IsPowered(component.Owner, EntityManager); + SubscribeLocalEvent(OnServerStartup); + SubscribeLocalEvent(OnServerShutdown); + SubscribeLocalEvent(OnServerDatabaseModified); } - private void UpdateServer(ResearchServerComponent component, int time) + private void OnServerStartup(EntityUid uid, ResearchServerComponent component, ComponentStartup args) { - if (!CanRun(component)) + var unusedId = EntityQuery(true) + .Max(s => s.Id) + 1; + component.Id = unusedId; + Dirty(component); + } + + private void OnServerShutdown(EntityUid uid, ResearchServerComponent component, ComponentShutdown args) + { + foreach (var client in new List(component.Clients)) + { + UnregisterClient(client, uid, serverComponent: component, dirtyServer: false); + } + } + + private void OnServerDatabaseModified(EntityUid uid, ResearchServerComponent component, ref TechnologyDatabaseModifiedEvent args) + { + foreach (var client in component.Clients) + { + RaiseLocalEvent(client, ref args); + } + } + + private bool CanRun(EntityUid uid) + { + return this.IsPowered(uid, EntityManager); + } + + private void UpdateServer(EntityUid uid, int time, ResearchServerComponent? component = null) + { + if (!Resolve(uid, ref component)) return; - ChangePointsOnServer(component.Owner, PointsPerSecond(component) * time, component); + + if (!CanRun(uid)) + return; + AddPointsToServer(uid, PointsPerSecond(uid, component) * time, component); } - public bool RegisterServerClient(ResearchServerComponent component, EntityUid client, ResearchClientComponent? clientComponent = null) + /// + /// Registers a client to the specified server. + /// + /// The client being registered + /// The server the client is being registered to + /// + /// + /// Whether or not to dirty the server component after registration + /// Whether or not the client was successfully registered to the server + public bool RegisterClient(EntityUid client, EntityUid server, ResearchClientComponent? clientComponent = null, + ResearchServerComponent? serverComponent = null, bool dirtyServer = true) { - if (!Resolve(client, ref clientComponent)) + if (!Resolve(client, ref clientComponent) || !Resolve(server, ref serverComponent)) return false; - if (component.Clients.Contains(client)) + if (serverComponent.Clients.Contains(client)) return false; - component.Clients.Add(client); - clientComponent.Server = component; + + serverComponent.Clients.Add(client); + clientComponent.Server = server; + + if (dirtyServer) + Dirty(serverComponent); + + var ev = new ResearchRegistrationChangedEvent(server); + RaiseLocalEvent(client, ref ev); return true; } - public void UnregisterServerClient(ResearchServerComponent component, EntityUid client, ResearchClientComponent? clientComponent = null) + /// + /// Unregisterse a client from its server + /// + /// + /// + /// + public void UnregisterClient(EntityUid client, ResearchClientComponent? clientComponent = null, bool dirtyServer = true) { if (!Resolve(client, ref clientComponent)) return; - component.Clients.Remove(client); + if (clientComponent.Server is not { } server) + return; + + UnregisterClient(client, server, clientComponent, dirtyServer: dirtyServer); + } + + /// + /// Unregisters a client from its server + /// + /// + /// + /// + /// + /// + public void UnregisterClient(EntityUid client, EntityUid server, ResearchClientComponent? clientComponent = null, + ResearchServerComponent? serverComponent = null, bool dirtyServer = true) + { + if (!Resolve(client, ref clientComponent) || !Resolve(server, ref serverComponent)) + return; + + serverComponent.Clients.Remove(client); clientComponent.Server = null; + + if (dirtyServer) + { + Dirty(serverComponent); + } + + var ev = new ResearchRegistrationChangedEvent(null); + RaiseLocalEvent(client, ref ev); } - public bool IsTechnologyUnlocked(ResearchServerComponent component, TechnologyPrototype prototype, - TechnologyDatabaseComponent? databaseComponent = null) - { - if (!Resolve(component.Owner, ref databaseComponent, false)) - return false; - return IsTechnologyUnlocked(databaseComponent.Owner, prototype.ID, databaseComponent); - } - - public bool CanUnlockTechnology(ResearchServerComponent component, TechnologyPrototype technology, TechnologyDatabaseComponent? databaseComponent = null) - { - if (!Resolve(component.Owner, ref databaseComponent, false)) - return false; - - if (!CanUnlockTechnology(databaseComponent.Owner, technology, databaseComponent) || - component.Points < technology.RequiredPoints || - IsTechnologyUnlocked(component, technology, databaseComponent)) - return false; - - return true; - } - - public bool UnlockTechnology(ResearchServerComponent component, TechnologyPrototype prototype, - TechnologyDatabaseComponent? databaseComponent = null) - { - if (!Resolve(component.Owner, ref databaseComponent, false)) - return false; - - if (!CanUnlockTechnology(component, prototype, databaseComponent)) - return false; - var result = UnlockTechnology(databaseComponent, prototype); - if (result) - ChangePointsOnServer(component.Owner, -prototype.RequiredPoints, component); - return result; - } - - public int PointsPerSecond(ResearchServerComponent component) + /// + /// Gets the amount of points generated by all the server's sources in a second. + /// + /// + /// + /// + public int PointsPerSecond(EntityUid uid, ResearchServerComponent? component = null) { var points = 0; - if (!CanRun(component)) + if (!Resolve(uid, ref component)) return points; + + if (!CanRun(uid)) + return points; + var ev = new ResearchServerGetPointsPerSecondEvent(component.Owner, points); foreach (var client in component.Clients) { RaiseLocalEvent(client, ref ev); } - return ev.Points; } - public void ChangePointsOnServer(EntityUid uid, int points, ResearchServerComponent? component = null) + /// + /// Adds a specified number of points to a server. + /// + /// The server + /// The amount of points being added + /// + public void AddPointsToServer(EntityUid uid, int points, ResearchServerComponent? component = null) { + if (points == 0) + return; + if (!Resolve(uid, ref component)) return; component.Points += points; @@ -100,5 +167,6 @@ public sealed partial class ResearchSystem { RaiseLocalEvent(client, ref ev); } + Dirty(component); } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Technology.cs b/Content.Server/Research/Systems/ResearchSystem.Technology.cs index 37c63c8bd4..85057dafc8 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Technology.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Technology.cs @@ -1,30 +1,25 @@ -using System.Linq; -using Content.Server.Research.Components; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; -using Robust.Shared.GameStates; namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { /// - /// Synchronizes this database against other, - /// adding all technologies from the other that - /// this one doesn't have. + /// Syncs the primary entity's database to that of the secondary entity's database. /// - /// - /// The other database - /// Whether the other database should be synced against this one too or not. - public void Sync(TechnologyDatabaseComponent component, TechnologyDatabaseComponent otherDatabase, bool twoway = true) + public void Sync(EntityUid primaryUid, EntityUid otherUid, TechnologyDatabaseComponent? primaryDb = null, TechnologyDatabaseComponent? otherDb = null) { - otherDatabase.TechnologyIds = otherDatabase.TechnologyIds.Union(component.TechnologyIds).ToList(); - otherDatabase.RecipeIds = otherDatabase.RecipeIds.Union(component.RecipeIds).ToList(); + if (!Resolve(primaryUid, ref primaryDb) || !Resolve(otherUid, ref otherDb)) + return; - if (twoway) - Sync(otherDatabase, component, false); + primaryDb.TechnologyIds = otherDb.TechnologyIds; + primaryDb.RecipeIds = otherDb.RecipeIds; - Dirty(component); + Dirty(primaryDb); + + var ev = new TechnologyDatabaseModifiedEvent(); + RaiseLocalEvent(primaryDb.Owner, ref ev); } /// @@ -33,46 +28,74 @@ public sealed partial class ResearchSystem /// syncs against the research server, and the server against the local database. /// /// Whether it could sync or not - public bool SyncWithServer(TechnologyDatabaseComponent component, ResearchClientComponent? clientComponent = null) + public bool SyncClientWithServer(EntityUid uid, TechnologyDatabaseComponent? databaseComponent = null, ResearchClientComponent? clientComponent = null) { - if (!Resolve(component.Owner, ref clientComponent, false)) - return false; - if (!TryComp(clientComponent.Server?.Owner, out var clientDatabase)) + if (!Resolve(uid, ref databaseComponent, ref clientComponent, false)) return false; - Sync(component, clientDatabase); + if (!TryComp(clientComponent.Server, out var serverDatabase)) + return false; + Sync(uid, clientComponent.Server.Value, databaseComponent, serverDatabase); return true; } /// - /// If possible, unlocks a technology on this database. + /// Tries to add a technology to a database, checking if it is able to /// - /// - /// - /// - public bool UnlockTechnology(TechnologyDatabaseComponent component, TechnologyPrototype technology) + /// If the technology was successfully added + public bool UnlockTechnology(EntityUid client, string prototypeid, ResearchClientComponent? component = null, + TechnologyDatabaseComponent? databaseComponent = null) { - if (!CanUnlockTechnology(component.Owner, technology, component)) return false; + if (!_prototypeManager.TryIndex(prototypeid, out var prototype)) + { + Logger.Error("invalid technology prototype"); + return false; + } + return UnlockTechnology(client, prototype, component, databaseComponent); + } - AddTechnology(component, technology.ID); + /// + /// Tries to add a technology to a database, checking if it is able to + /// + /// If the technology was successfully added + public bool UnlockTechnology(EntityUid client, TechnologyPrototype prototype, ResearchClientComponent? component = null, + TechnologyDatabaseComponent? databaseComponent = null) + { + if (!Resolve(client, ref component, ref databaseComponent, false)) + return false; + + if (!CanUnlockTechnology(client, prototype, databaseComponent)) + return false; + + if (component.Server is not { } server) + return false; + AddTechnology(server, prototype.ID); + AddPointsToServer(server, -prototype.RequiredPoints); return true; } /// /// Adds a technology to the database without checking if it could be unlocked. /// - /// - /// - public void AddTechnology(TechnologyDatabaseComponent component, string technology) + public void AddTechnology(EntityUid uid, string technology, TechnologyDatabaseComponent? component = null) { + if (!Resolve(uid, ref component)) + return; + if (!_prototypeManager.TryIndex(technology, out var prototype)) return; - AddTechnology(component, prototype); + AddTechnology(uid, prototype, component); } - public void AddTechnology(TechnologyDatabaseComponent component, TechnologyPrototype technology) + /// + /// Adds a technology to the database without checking if it could be unlocked. + /// + public void AddTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? component = null) { + if (!Resolve(uid, ref component)) + return; + component.TechnologyIds.Add(technology.ID); foreach (var unlock in technology.UnlockedRecipes) { @@ -82,23 +105,63 @@ public sealed partial class ResearchSystem } Dirty(component); - if (!TryComp(component.Owner, out var server)) - return; - foreach (var client in server.Clients) - { - if (!TryComp(client, out var console)) - continue; - UpdateConsoleInterface(console); - } + var ev = new TechnologyDatabaseModifiedEvent(); + RaiseLocalEvent(uid, ref ev); } - public void AddLatheRecipe(TechnologyDatabaseComponent component, string recipe, bool dirty = true) + /// + /// Adds a lathe recipe to the specified technology database + /// without checking if it can be unlocked. + /// + public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseComponent? component = null, bool dirty = true) { + if (!Resolve(uid, ref component)) + return; + if (component.RecipeIds.Contains(recipe)) return; component.RecipeIds.Add(recipe); if (dirty) Dirty(component); + + var ev = new TechnologyDatabaseModifiedEvent(); + RaiseLocalEvent(uid, ref ev); + } + + /// + /// Returns whether a technology can be unlocked on this database, + /// taking parent technologies into account. + /// + /// Whether it could be unlocked or not + public bool CanUnlockTechnology(EntityUid uid, string technology, TechnologyDatabaseComponent? database = null, ResearchClientComponent? client = null) + { + if (!_prototypeManager.TryIndex(technology, out var prototype)) + return false; + return CanUnlockTechnology(uid, prototype, database, client); + } + + /// + /// Returns whether a technology can be unlocked on this database, + /// taking parent technologies into account. + /// + /// Whether it could be unlocked or not + public bool CanUnlockTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? database = null, ResearchClientComponent? client = null) + { + if (!Resolve(uid, ref database, ref client)) + return false; + + if (!TryGetClientServer(uid, out _, out var serverComponent, client)) + return false; + + if (serverComponent.Points < technology.RequiredPoints) + return false; + + if (IsTechnologyUnlocked(uid, technology, database)) + return false; + + if (!ArePrerequesitesUnlocked(uid, technology, database)) + return false; + return true; } } diff --git a/Content.Server/Research/Systems/ResearchSystem.cs b/Content.Server/Research/Systems/ResearchSystem.cs index 2486edd229..dcd8710cbd 100644 --- a/Content.Server/Research/Systems/ResearchSystem.cs +++ b/Content.Server/Research/Systems/ResearchSystem.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Server.Research.Components; +using Content.Shared.Research.Components; using Content.Shared.Research.Systems; using JetBrains.Annotations; using Robust.Server.GameObjects; @@ -18,20 +18,17 @@ namespace Content.Server.Research.Systems public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStartup); - InitializeClient(); InitializeConsole(); InitializeSource(); + InitializeServer(); } - private void OnStartup(EntityUid uid, ResearchServerComponent component, ComponentStartup args) - { - var unusedId = EntityQuery(true) - .Max(s => s.Id) + 1; - component.Id = unusedId; - } - + /// + /// Gets a server based on it's unique numeric id. + /// + /// + /// public ResearchServerComponent? GetServerById(int id) { foreach (var server in EntityQuery()) @@ -43,6 +40,10 @@ namespace Content.Server.Research.Systems return null; } + /// + /// Gets the names of all the servers. + /// + /// public string[] GetServerNames() { var allServers = EntityQuery(true).ToArray(); @@ -56,6 +57,10 @@ namespace Content.Server.Research.Systems return list; } + /// + /// Gets the ids of all the servers + /// + /// public int[] GetServerIds() { var allServers = EntityQuery(true).ToArray(); @@ -77,7 +82,7 @@ namespace Content.Server.Research.Systems continue; server.NextUpdateTime = _timing.CurTime + server.ResearchConsoleUpdateTime; - UpdateServer(server, (int) server.ResearchConsoleUpdateTime.TotalSeconds); + UpdateServer(server.Owner, (int) server.ResearchConsoleUpdateTime.TotalSeconds, server); } } } diff --git a/Content.Server/Research/TechnologyDisk/Systems/DiskConsoleSystem.cs b/Content.Server/Research/TechnologyDisk/Systems/DiskConsoleSystem.cs index 26c9288292..341a8d489b 100644 --- a/Content.Server/Research/TechnologyDisk/Systems/DiskConsoleSystem.cs +++ b/Content.Server/Research/TechnologyDisk/Systems/DiskConsoleSystem.cs @@ -1,8 +1,8 @@ -using Content.Server.Research.Components; -using Content.Server.Research.Systems; +using Content.Server.Research.Systems; using Content.Server.Research.TechnologyDisk.Components; using Content.Server.UserInterface; using Content.Shared.Research; +using Content.Shared.Research.Components; using Robust.Server.GameObjects; using Robust.Shared.Timing; @@ -20,6 +20,7 @@ public sealed class DiskConsoleSystem : EntitySystem { SubscribeLocalEvent(OnPrintDisk); SubscribeLocalEvent(OnPointsChanged); + SubscribeLocalEvent(OnRegistrationChanged); SubscribeLocalEvent(OnBeforeUiOpen); SubscribeLocalEvent(OnShutdown); @@ -41,17 +42,21 @@ public sealed class DiskConsoleSystem : EntitySystem private void OnPrintDisk(EntityUid uid, DiskConsoleComponent component, DiskConsolePrintDiskMessage args) { - if (!TryComp(uid, out var client) || client.Server == null) + if (HasComp(uid)) return; - if (client.Server.Points < component.PricePerDisk) + if (!_research.TryGetClientServer(uid, out var server, out var serverComp)) return; - _research.ChangePointsOnServer(client.Server.Owner, -component.PricePerDisk, client.Server); + if (serverComp.Points < component.PricePerDisk) + return; + + _research.AddPointsToServer(server.Value, -component.PricePerDisk, serverComp); _audio.PlayPvs(component.PrintSound, uid); var printing = EnsureComp(uid); printing.FinishTime = _timing.CurTime + component.PrintDuration; + UpdateUserInterface(uid, component); } private void OnPointsChanged(EntityUid uid, DiskConsoleComponent component, ref ResearchServerPointsChangedEvent args) @@ -59,6 +64,11 @@ public sealed class DiskConsoleSystem : EntitySystem UpdateUserInterface(uid, component); } + private void OnRegistrationChanged(EntityUid uid, DiskConsoleComponent component, ref ResearchRegistrationChangedEvent args) + { + UpdateUserInterface(uid, component); + } + private void OnBeforeUiOpen(EntityUid uid, DiskConsoleComponent component, BeforeActivatableUIOpenEvent args) { UpdateUserInterface(uid, component); @@ -70,9 +80,9 @@ public sealed class DiskConsoleSystem : EntitySystem return; var totalPoints = 0; - if (TryComp(uid, out var client) && client.Server != null) + if (_research.TryGetClientServer(uid, out _, out var server)) { - totalPoints = client.Server.Points; + totalPoints = server.Points; } var canPrint = !HasComp(uid) && totalPoints >= component.PricePerDisk; diff --git a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs index e098a222b9..2f4bce403e 100644 --- a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs +++ b/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Server.Popups; -using Content.Server.Research.Components; using Content.Server.Research.Systems; using Content.Server.Research.TechnologyDisk.Components; using Content.Shared.Examine; @@ -39,7 +38,7 @@ public sealed class TechnologyDiskSystem : EntitySystem { foreach (var recipe in component.Recipes) { - _research.AddLatheRecipe(database, recipe, false); + _research.AddLatheRecipe(target, recipe, database, false); } Dirty(database); } diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs index 49d6af7fa8..83d565905b 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs @@ -4,7 +4,6 @@ using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Events; using Content.Server.Paper; using Content.Server.Power.Components; -using Content.Server.Research.Components; using Content.Server.Research.Systems; using Content.Server.UserInterface; using Content.Server.Xenoarchaeology.Equipment.Components; @@ -20,7 +19,6 @@ using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Physics.Events; -using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -333,7 +331,10 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem /// private void OnDestroyButton(EntityUid uid, AnalysisConsoleComponent component, AnalysisConsoleDestroyButtonPressedMessage args) { - if (!TryComp(uid, out var client) || client.Server is not { } server || component.AnalyzerEntity == null) + if (component.AnalyzerEntity == null) + return; + + if (!_research.TryGetClientServer(uid, out var server, out var serverComponent)) return; var entToDestroy = GetArtifactForAnalysis(component.AnalyzerEntity); @@ -346,7 +347,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem ResetAnalyzer(component.AnalyzerEntity.Value); } - _research.ChangePointsOnServer(server.Owner, _artifact.GetResearchPointValue(entToDestroy.Value), server); + _research.AddPointsToServer(server.Value, _artifact.GetResearchPointValue(entToDestroy.Value), serverComponent); EntityManager.DeleteEntity(entToDestroy.Value); _audio.PlayPvs(component.DestroySound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f)); diff --git a/Content.Shared/Lathe/LatheMessages.cs b/Content.Shared/Lathe/LatheMessages.cs index 09624f82f8..7b28c3f5b0 100644 --- a/Content.Shared/Lathe/LatheMessages.cs +++ b/Content.Shared/Lathe/LatheMessages.cs @@ -24,19 +24,10 @@ public sealed class LatheUpdateState : BoundUserInterfaceState /// Sent to the server to sync material storage and the recipe queue. /// [Serializable, NetSerializable] -public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage { } +public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage +{ -/// -/// Sent to the server to sync the lathe's technology database with the research server. -/// -[Serializable, NetSerializable] -public sealed class LatheServerSyncMessage : BoundUserInterfaceMessage { } - -/// -/// Sent to the server to open the ResearchClient UI. -/// -[Serializable, NetSerializable] -public sealed class LatheServerSelectionMessage : BoundUserInterfaceMessage { } +} /// /// Sent to the server when a client queues a new recipe. diff --git a/Content.Shared/Research/Components/SharedResearchClientComponent.cs b/Content.Shared/Research/Components/ResearchClientComponent.cs similarity index 69% rename from Content.Shared/Research/Components/SharedResearchClientComponent.cs rename to Content.Shared/Research/Components/ResearchClientComponent.cs index bd64d9f98c..477fb8f600 100644 --- a/Content.Shared/Research/Components/SharedResearchClientComponent.cs +++ b/Content.Shared/Research/Components/ResearchClientComponent.cs @@ -2,15 +2,34 @@ namespace Content.Shared.Research.Components { + /// + /// This is an entity that is able to connect to a + /// + [RegisterComponent] + public sealed class ResearchClientComponent : Component + { + public bool ConnectedToServer => Server != null; + + /// + /// The server the client is connected to + /// + [ViewVariables(VVAccess.ReadOnly)] + public EntityUid? Server { get; set; } + } + + /// + /// Raised on the client whenever its server is changed + /// + /// Its new server + [ByRefEvent] + public readonly record struct ResearchRegistrationChangedEvent(EntityUid? Server); + /// /// Sent to the server when the client deselects a research server. /// [Serializable, NetSerializable] public sealed class ResearchClientServerDeselectedMessage : BoundUserInterfaceMessage { - public ResearchClientServerDeselectedMessage() - { - } } /// @@ -33,10 +52,6 @@ namespace Content.Shared.Research.Components [Serializable, NetSerializable] public sealed class ResearchClientSyncMessage : BoundUserInterfaceMessage { - - public ResearchClientSyncMessage() - { - } } [NetSerializable, Serializable] diff --git a/Content.Server/Research/Components/ResearchServerComponent.cs b/Content.Shared/Research/Components/ResearchServerComponent.cs similarity index 60% rename from Content.Server/Research/Components/ResearchServerComponent.cs rename to Content.Shared/Research/Components/ResearchServerComponent.cs index ac7105c85e..220c6ff412 100644 --- a/Content.Server/Research/Components/ResearchServerComponent.cs +++ b/Content.Shared/Research/Components/ResearchServerComponent.cs @@ -1,21 +1,36 @@ -using Content.Server.Research.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.Research.Components +namespace Content.Shared.Research.Components { - [Access(typeof(ResearchSystem))] - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed class ResearchServerComponent : Component { + /// + /// The name of the server + /// [DataField("servername"), ViewVariables(VVAccess.ReadWrite)] public string ServerName = "RDSERVER"; + /// + /// The amount of points on the server. + /// [DataField("points"), ViewVariables(VVAccess.ReadWrite)] public int Points; + /// + /// A unique numeric id representing the server + /// [ViewVariables(VVAccess.ReadOnly)] public int Id; + /// + /// Entities connected to the server + /// + /// + /// This is not safe to read clientside + /// [ViewVariables(VVAccess.ReadOnly)] public List Clients = new(); @@ -26,6 +41,20 @@ namespace Content.Server.Research.Components public readonly TimeSpan ResearchConsoleUpdateTime = TimeSpan.FromSeconds(1); } + [Serializable, NetSerializable] + public sealed class ResearchServerState : ComponentState + { + public string ServerName; + public int Points; + public int Id; + public ResearchServerState(string serverName, int points, int id) + { + ServerName = serverName; + Points = points; + Id = id; + } + } + /// /// Event raised on a server's clients when the point value of the server is changed. /// diff --git a/Content.Shared/Research/Components/SharedResearchConsoleComponent.cs b/Content.Shared/Research/Components/SharedResearchConsoleComponent.cs index b7b2a4e9be..a8304e97fb 100644 --- a/Content.Shared/Research/Components/SharedResearchConsoleComponent.cs +++ b/Content.Shared/Research/Components/SharedResearchConsoleComponent.cs @@ -22,15 +22,13 @@ namespace Content.Shared.Research.Components [Serializable, NetSerializable] public sealed class ConsoleServerSyncMessage : BoundUserInterfaceMessage { - public ConsoleServerSyncMessage() - {} + } [Serializable, NetSerializable] public sealed class ConsoleServerSelectionMessage : BoundUserInterfaceMessage { - public ConsoleServerSelectionMessage() - {} + } [Serializable, NetSerializable] diff --git a/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs b/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs index 3101212b3b..0970d3b077 100644 --- a/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs +++ b/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs @@ -8,13 +8,31 @@ namespace Content.Shared.Research.Components [RegisterComponent, NetworkedComponent] public sealed class TechnologyDatabaseComponent : Component { + /// + /// The ids of all the technologies which have been unlocked. + /// [DataField("technologyIds", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List TechnologyIds = new(); + /// + /// The ids of all the lathe recipes which have been unlocked. + /// This is maintained alongside the TechnologyIds + /// [DataField("recipeIds", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List RecipeIds = new(); } + /// + /// Event raised on the database whenever its + /// technologies or recipes are modified. + /// + /// + /// This event is forwarded from the + /// server to all of it's clients. + /// + [ByRefEvent] + public readonly record struct TechnologyDatabaseModifiedEvent; + [Serializable, NetSerializable] public sealed class TechnologyDatabaseState : ComponentState { diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs index 8d4355b9cb..224535f7ad 100644 --- a/Content.Shared/Research/Systems/SharedResearchSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs @@ -1,22 +1,35 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; namespace Content.Shared.Research.Systems; public abstract class SharedResearchSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnServerGetState); + SubscribeLocalEvent(OnServerHandleState); SubscribeLocalEvent(OnTechnologyGetState); SubscribeLocalEvent(OnTechnologyHandleState); } + private void OnServerGetState(EntityUid uid, ResearchServerComponent component, ref ComponentGetState args) + { + args.State = new ResearchServerState(component.ServerName, component.Points, component.Id); + } + + private void OnServerHandleState(EntityUid uid, ResearchServerComponent component, ref ComponentHandleState args) + { + if (args.Current is not ResearchServerState state) + return; + component.ServerName = state.ServerName; + component.Points = state.Points; + component.Id = state.Id; + } + private void OnTechnologyHandleState(EntityUid uid, TechnologyDatabaseComponent component, ref ComponentHandleState args) { if (args.Current is not TechnologyDatabaseState state) @@ -34,40 +47,35 @@ public abstract class SharedResearchSystem : EntitySystem /// Returns whether a technology is unlocked on this database or not. /// /// Whether it is unlocked or not - public bool IsTechnologyUnlocked(EntityUid uid, string technologyId, TechnologyDatabaseComponent? component = null) + public bool IsTechnologyUnlocked(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? component = null) { - return Resolve(uid, ref component) && component.TechnologyIds.Contains(technologyId); + return Resolve(uid, ref component) && IsTechnologyUnlocked(uid, technology.ID, component); } /// /// Returns whether a technology is unlocked on this database or not. /// /// Whether it is unlocked or not - public bool IsTechnologyUnlocked(EntityUid uid, TechnologyPrototype technologyId, TechnologyDatabaseComponent? component = null) + public bool IsTechnologyUnlocked(EntityUid uid, string technologyId, TechnologyDatabaseComponent? component = null) { - return Resolve(uid, ref component) && IsTechnologyUnlocked(uid, technologyId.ID, component); + return Resolve(uid, ref component, false) && component.TechnologyIds.Contains(technologyId); } /// - /// Returns whether a technology can be unlocked on this database, - /// taking parent technologies into account. + /// Returns whether or not all the prerequisite + /// technologies for a technology are unlocked. /// - /// Whether it could be unlocked or not - public bool CanUnlockTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? component = null) + /// + /// + /// + /// Whether or not the prerequesites are present + public bool ArePrerequesitesUnlocked(EntityUid uid, TechnologyPrototype prototype, TechnologyDatabaseComponent? component = null) { if (!Resolve(uid, ref component)) return false; - - if (IsTechnologyUnlocked(uid, technology.ID, component)) - return false; - - foreach (var technologyId in technology.RequiredTechnologies) + foreach (var technologyId in prototype.RequiredTechnologies) { - _prototypeManager.TryIndex(technologyId, out TechnologyPrototype? requiredTechnology); - if (requiredTechnology == null) - return false; - - if (!IsTechnologyUnlocked(uid, requiredTechnology.ID, component)) + if (!IsTechnologyUnlocked(uid, technologyId, component)) return false; } return true;