diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 185d019945..740767d534 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -5,8 +5,8 @@ using Content.Server.Lathe.Components; using Content.Server.Materials; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Server.Research; using Content.Server.Research.Components; +using Content.Server.Research.Systems; using Content.Server.UserInterface; using Content.Shared.Lathe; using Content.Shared.Materials; diff --git a/Content.Server/Research/Components/ResearchClientComponent.cs b/Content.Server/Research/Components/ResearchClientComponent.cs index 89d5aa43f8..322139fb4c 100644 --- a/Content.Server/Research/Components/ResearchClientComponent.cs +++ b/Content.Server/Research/Components/ResearchClientComponent.cs @@ -1,8 +1,7 @@ namespace Content.Server.Research.Components { [RegisterComponent] - [Virtual] - public class ResearchClientComponent : Component + public sealed class ResearchClientComponent : Component { public bool ConnectedToServer => Server != null; diff --git a/Content.Server/Research/Components/ResearchPointSourceComponent.cs b/Content.Server/Research/Components/ResearchPointSourceComponent.cs index 5dcbb6e8e0..5ca0b8c4da 100644 --- a/Content.Server/Research/Components/ResearchPointSourceComponent.cs +++ b/Content.Server/Research/Components/ResearchPointSourceComponent.cs @@ -1,24 +1,12 @@ namespace Content.Server.Research.Components { [RegisterComponent] - public sealed class ResearchPointSourceComponent : ResearchClientComponent + public sealed class ResearchPointSourceComponent : Component { - [DataField("pointspersecond")] - private int _pointsPerSecond; - [DataField("active")] - private bool _active; - [ViewVariables(VVAccess.ReadWrite)] - public int PointsPerSecond - { - get => _pointsPerSecond; - set => _pointsPerSecond = value; - } + [DataField("pointspersecond"), ViewVariables(VVAccess.ReadWrite)] + public int PointsPerSecond; - [ViewVariables(VVAccess.ReadWrite)] - public bool Active - { - get => _active; - set => _active = value; - } + [DataField("active"), ViewVariables(VVAccess.ReadWrite)] + public bool Active; } } diff --git a/Content.Server/Research/Components/ResearchServerComponent.cs b/Content.Server/Research/Components/ResearchServerComponent.cs index 4a9460cf8f..ac7105c85e 100644 --- a/Content.Server/Research/Components/ResearchServerComponent.cs +++ b/Content.Server/Research/Components/ResearchServerComponent.cs @@ -1,22 +1,45 @@ +using Content.Server.Research.Systems; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + namespace Content.Server.Research.Components { + [Access(typeof(ResearchSystem))] [RegisterComponent] public sealed class ResearchServerComponent : Component { - [ViewVariables(VVAccess.ReadWrite)] public string ServerName => _serverName; + [DataField("servername"), ViewVariables(VVAccess.ReadWrite)] + public string ServerName = "RDSERVER"; - [DataField("servername")] - private string _serverName = "RDSERVER"; - - [ViewVariables(VVAccess.ReadWrite)] [DataField("points")] - public int Points = 0; - - [ViewVariables(VVAccess.ReadOnly)] public int Id { get; set; } + [DataField("points"), ViewVariables(VVAccess.ReadWrite)] + public int Points; [ViewVariables(VVAccess.ReadOnly)] - public List PointSources { get; } = new(); + public int Id; [ViewVariables(VVAccess.ReadOnly)] - public List Clients { get; } = new(); + public List Clients = new(); + + [DataField("nextUpdateTime", customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdateTime = TimeSpan.Zero; + + [DataField("researchConsoleUpdateTime"), ViewVariables(VVAccess.ReadWrite)] + public readonly TimeSpan ResearchConsoleUpdateTime = TimeSpan.FromSeconds(1); } + + /// + /// Event raised on a server's clients when the point value of the server is changed. + /// + /// + /// + /// + [ByRefEvent] + public readonly record struct ResearchServerPointsChangedEvent(EntityUid Server, int Total, int Delta); + + /// + /// Event raised every second to calculate the amount of points added to the server. + /// + /// + /// + [ByRefEvent] + public record struct ResearchServerGetPointsPerSecondEvent(EntityUid Server, int Points); } diff --git a/Content.Server/Research/Disk/ResearchDiskSystem.cs b/Content.Server/Research/Disk/ResearchDiskSystem.cs index c9d154ca19..ef55acdee7 100644 --- a/Content.Server/Research/Disk/ResearchDiskSystem.cs +++ b/Content.Server/Research/Disk/ResearchDiskSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Interaction; using Content.Server.Research.Components; using Content.Server.Popups; +using Content.Server.Research.Systems; using Robust.Shared.Player; namespace Content.Server.Research.Disk @@ -8,6 +9,7 @@ namespace Content.Server.Research.Disk public sealed class ResearchDiskSystem : EntitySystem { [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly ResearchSystem _research = default!; public override void Initialize() { base.Initialize(); @@ -22,7 +24,7 @@ namespace Content.Server.Research.Disk if (!TryComp(args.Target, out var server)) return; - server.Points += component.Points; + _research.ChangePointsOnServer(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 ca2205b6a6..80aa0eca46 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Client.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Client.cs @@ -1,7 +1,8 @@ +using System.Linq; using Content.Server.Research.Components; using Content.Shared.Research.Components; -namespace Content.Server.Research; +namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { @@ -50,8 +51,9 @@ public sealed partial class ResearchSystem private void OnClientStartup(EntityUid uid, ResearchClientComponent component, ComponentStartup args) { - if (Servers.Count > 0) - RegisterClientServer(component, Servers[0]); + var allServers = EntityQuery(true).ToArray(); + if (allServers.Length > 0) + RegisterClientServer(component, allServers[0]); } private void OnClientShutdown(EntityUid uid, ResearchClientComponent component, ComponentShutdown args) @@ -66,22 +68,23 @@ public sealed partial class ResearchSystem private void UpdateClientInterface(ResearchClientComponent component) { - var state = new ResearchClientBoundInterfaceState(Servers.Count, GetServerNames(), + var names = GetServerNames(); + var state = new ResearchClientBoundInterfaceState(names.Length, names, GetServerIds(), component.ConnectedToServer ? component.Server!.Id : -1); - _uiSystem.GetUiOrNull(component.Owner, ResearchClientUiKey.Key)?.SetState(state); + _uiSystem.TrySetUiState(component.Owner, ResearchClientUiKey.Key, state); } private bool RegisterClientServer(ResearchClientComponent component, ResearchServerComponent? server = null) { - if (server == null) return false; - return RegisterServerClient(server, component); + return server != null && RegisterServerClient(server, component.Owner, component); } private void UnregisterClientServer(ResearchClientComponent component) { - if (component.Server == null) return; + if (component.Server == null) + return; - UnregisterServerClient(component.Server, component); + UnregisterServerClient(component.Server, component.Owner, component); } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Console.cs b/Content.Server/Research/Systems/ResearchSystem.Console.cs index 6772e55de9..360439ec17 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Console.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Console.cs @@ -4,7 +4,7 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using Robust.Server.Player; -namespace Content.Server.Research; +namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { @@ -13,6 +13,7 @@ public sealed partial class ResearchSystem SubscribeLocalEvent(OnConsoleUnlock); SubscribeLocalEvent(OnConsoleSync); SubscribeLocalEvent(OnConsoleSelect); + SubscribeLocalEvent(OnPointsChanged); } private void OnConsoleSelect(EntityUid uid, ResearchConsoleComponent component, ConsoleServerSelectionMessage args) @@ -45,9 +46,11 @@ public sealed partial class ResearchSystem if (!_prototypeManager.TryIndex(args.Id, out TechnologyPrototype? tech) || client.Server == null || - !CanUnlockTechnology(client.Server, tech)) return; + !CanUnlockTechnology(client.Server, tech)) + return; - if (!UnlockTechnology(client.Server, tech)) return; + if (!UnlockTechnology(client.Server, tech)) + return; SyncWithServer(database); Dirty(database); @@ -69,6 +72,15 @@ public sealed partial class ResearchSystem var pointsPerSecond = clientComponent.ConnectedToServer ? PointsPerSecond(clientComponent.Server) : 0; state = new ResearchConsoleBoundInterfaceState(points, pointsPerSecond); } - _uiSystem.GetUiOrNull(component.Owner, ResearchConsoleUiKey.Key)?.SetState(state); + + _uiSystem.TrySetUiState(component.Owner, ResearchConsoleUiKey.Key, state); } + + private void OnPointsChanged(EntityUid uid, ResearchConsoleComponent component, ref ResearchServerPointsChangedEvent args) + { + if (!_uiSystem.IsUiOpen(uid, ResearchConsoleUiKey.Key)) + return; + UpdateConsoleInterface(component); + } + } diff --git a/Content.Server/Research/Systems/ResearchSystem.PointSource.cs b/Content.Server/Research/Systems/ResearchSystem.PointSource.cs index 0803d7ac4c..a1829cc10f 100644 --- a/Content.Server/Research/Systems/ResearchSystem.PointSource.cs +++ b/Content.Server/Research/Systems/ResearchSystem.PointSource.cs @@ -1,10 +1,21 @@ using Content.Server.Power.EntitySystems; using Content.Server.Research.Components; -namespace Content.Server.Research; +namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { + private void InitializeSource() + { + SubscribeLocalEvent(OnGetPointsPerSecond); + } + + private void OnGetPointsPerSecond(EntityUid uid, ResearchPointSourceComponent component, ref ResearchServerGetPointsPerSecondEvent args) + { + if (CanProduce(component)) + args.Points += component.PointsPerSecond; + } + public bool CanProduce(ResearchPointSourceComponent component) { return component.Active && this.IsPowered(component.Owner, EntityManager); diff --git a/Content.Server/Research/Systems/ResearchSystem.Server.cs b/Content.Server/Research/Systems/ResearchSystem.Server.cs index b3d7aba1c9..16f27b39e8 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Server.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Server.cs @@ -1,28 +1,11 @@ using Content.Server.Power.EntitySystems; using Content.Server.Research.Components; -using Content.Server.Station.Systems; using Content.Shared.Research.Prototypes; -namespace Content.Server.Research; +namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { - private void InitializeServer() - { - SubscribeLocalEvent(OnServerStartup); - SubscribeLocalEvent(OnServerShutdown); - } - - private void OnServerShutdown(EntityUid uid, ResearchServerComponent component, ComponentShutdown args) - { - UnregisterServer(component); - } - - private void OnServerStartup(EntityUid uid, ResearchServerComponent component, ComponentStartup args) - { - RegisterServer(component); - } - private bool CanRun(ResearchServerComponent component) { return this.IsPowered(component.Owner, EntityManager); @@ -30,41 +13,37 @@ public sealed partial class ResearchSystem private void UpdateServer(ResearchServerComponent component, int time) { - if (!CanRun(component)) return; - component.Points += PointsPerSecond(component) * time; + if (!CanRun(component)) + return; + ChangePointsOnServer(component.Owner, PointsPerSecond(component) * time, component); } - public bool RegisterServerClient(ResearchServerComponent component, ResearchClientComponent clientComponent) + public bool RegisterServerClient(ResearchServerComponent component, EntityUid client, ResearchClientComponent? clientComponent = null) { - // TODO: This is shit but I'm just trying to fix RND for now until it gets bulldozed - if (TryComp(clientComponent.Owner, out var source)) - { - if (component.PointSources.Contains(source)) return false; - component.PointSources.Add(source); - source.Server = component; - } + if (!Resolve(client, ref clientComponent)) + return false; - if (component.Clients.Contains(clientComponent)) return false; - component.Clients.Add(clientComponent); + if (component.Clients.Contains(client)) + return false; + component.Clients.Add(client); clientComponent.Server = component; return true; } - public void UnregisterServerClient(ResearchServerComponent component, ResearchClientComponent clientComponent) + public void UnregisterServerClient(ResearchServerComponent component, EntityUid client, ResearchClientComponent? clientComponent = null) { - if (TryComp(clientComponent.Owner, out var source)) - { - component.PointSources.Remove(source); - } + if (!Resolve(client, ref clientComponent)) + return; - component.Clients.Remove(clientComponent); + component.Clients.Remove(client); clientComponent.Server = null; } public bool IsTechnologyUnlocked(ResearchServerComponent component, TechnologyPrototype prototype, TechnologyDatabaseComponent? databaseComponent = null) { - if (!Resolve(component.Owner, ref databaseComponent, false)) return false; + if (!Resolve(component.Owner, ref databaseComponent, false)) + return false; return databaseComponent.IsTechnologyUnlocked(prototype.ID); } @@ -84,12 +63,14 @@ public sealed partial class ResearchSystem public bool UnlockTechnology(ResearchServerComponent component, TechnologyPrototype prototype, TechnologyDatabaseComponent? databaseComponent = null) { - if (!Resolve(component.Owner, ref databaseComponent, false)) return false; + if (!Resolve(component.Owner, ref databaseComponent, false)) + return false; - if (!CanUnlockTechnology(component, prototype, databaseComponent)) return false; + if (!CanUnlockTechnology(component, prototype, databaseComponent)) + return false; var result = UnlockTechnology(databaseComponent, prototype); if (result) - component.Points -= prototype.RequiredPoints; + ChangePointsOnServer(component.Owner, -prototype.RequiredPoints, component); return result; } @@ -97,14 +78,26 @@ public sealed partial class ResearchSystem { var points = 0; - if (CanRun(component)) + if (!CanRun(component)) + return points; + var ev = new ResearchServerGetPointsPerSecondEvent(component.Owner, points); + foreach (var client in component.Clients) { - foreach (var source in component.PointSources) - { - if (CanProduce(source)) points += source.PointsPerSecond; - } + RaiseLocalEvent(client, ref ev); } - return points; + return ev.Points; + } + + public void ChangePointsOnServer(EntityUid uid, int points, ResearchServerComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + component.Points += points; + var ev = new ResearchServerPointsChangedEvent(uid, component.Points, points); + foreach (var client in component.Clients) + { + RaiseLocalEvent(client, ref ev); + } } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Technology.cs b/Content.Server/Research/Systems/ResearchSystem.Technology.cs index 52ebe22c2d..1a82f48716 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Technology.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Technology.cs @@ -3,7 +3,7 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using Robust.Shared.GameStates; -namespace Content.Server.Research; +namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { @@ -22,13 +22,15 @@ public sealed partial class ResearchSystem /// adding all technologies from the other that /// this one doesn't have. /// + /// /// 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) { foreach (var tech in otherDatabase.TechnologyIds) { - if (!component.IsTechnologyUnlocked(tech)) AddTechnology(component, tech); + if (!component.IsTechnologyUnlocked(tech)) + AddTechnology(component, tech); } if (twoway) @@ -45,8 +47,10 @@ public sealed partial class ResearchSystem /// Whether it could sync or not public bool SyncWithServer(TechnologyDatabaseComponent component, ResearchClientComponent? clientComponent = null) { - if (!Resolve(component.Owner, ref clientComponent, false)) return false; - if (!TryComp(clientComponent.Server?.Owner, out var clientDatabase)) return false; + if (!Resolve(component.Owner, ref clientComponent, false)) + return false; + if (!TryComp(clientComponent.Server?.Owner, out var clientDatabase)) + return false; Sync(component, clientDatabase); @@ -56,11 +60,13 @@ public sealed partial class ResearchSystem /// /// If possible, unlocks a technology on this database. /// + /// /// /// public bool UnlockTechnology(TechnologyDatabaseComponent component, TechnologyPrototype technology) { - if (!component.CanUnlockTechnology(technology)) return false; + if (!component.CanUnlockTechnology(technology)) + return false; AddTechnology(component, technology.ID); Dirty(component); @@ -70,6 +76,7 @@ public sealed partial class ResearchSystem /// /// Adds a technology to the database without checking if it could be unlocked. /// + /// /// public void AddTechnology(TechnologyDatabaseComponent component, string technology) { diff --git a/Content.Server/Research/Systems/ResearchSystem.cs b/Content.Server/Research/Systems/ResearchSystem.cs index 41266c7349..398ee577d5 100644 --- a/Content.Server/Research/Systems/ResearchSystem.cs +++ b/Content.Server/Research/Systems/ResearchSystem.cs @@ -1,50 +1,34 @@ +using System.Linq; using Content.Server.Research.Components; -using Content.Shared.Research.Components; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; -namespace Content.Server.Research +namespace Content.Server.Research.Systems { [UsedImplicitly] public sealed partial class ResearchSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - private const int ResearchConsoleUIUpdateTime = 5; - - private float _timer = ResearchConsoleUIUpdateTime; - private readonly List _servers = new(); - public IReadOnlyList Servers => _servers; - public override void Initialize() { base.Initialize(); InitializeClient(); InitializeConsole(); - InitializeServer(); + InitializeSource(); InitializeTechnology(); } - public bool RegisterServer(ResearchServerComponent server) - { - if (_servers.Contains(server)) return false; - _servers.Add(server); - _servers[^1].Id = _servers.Count - 1; - return true; - } - - public void UnregisterServer(ResearchServerComponent server) - { - _servers.Remove(server); - } - public ResearchServerComponent? GetServerById(int id) { - foreach (var server in Servers) + foreach (var server in EntityQuery()) { - if (server.Id == id) return server; + if (server.Id == id) + return server; } return null; @@ -52,11 +36,12 @@ namespace Content.Server.Research public string[] GetServerNames() { - var list = new string[Servers.Count]; + var allServers = EntityQuery(true).ToArray(); + var list = new string[allServers.Length]; - for (var i = 0; i < Servers.Count; i++) + for (var i = 0; i < allServers.Length; i++) { - list[i] = Servers[i].ServerName; + list[i] = allServers[i].ServerName; } return list; @@ -64,11 +49,12 @@ namespace Content.Server.Research public int[] GetServerIds() { - var list = new int[Servers.Count]; + var allServers = EntityQuery(true).ToArray(); + var list = new int[allServers.Length]; - for (var i = 0; i < Servers.Count; i++) + for (var i = 0; i < allServers.Length; i++) { - list[i] = Servers[i].Id; + list[i] = allServers[i].Id; } return list; @@ -76,22 +62,13 @@ namespace Content.Server.Research public override void Update(float frameTime) { - _timer += frameTime; - - while (_timer > ResearchConsoleUIUpdateTime) + foreach (var server in EntityQuery()) { - foreach (var server in _servers) - { - UpdateServer(server, ResearchConsoleUIUpdateTime); - } + if (server.NextUpdateTime > _timing.CurTime) + continue; + server.NextUpdateTime = _timing.CurTime + server.ResearchConsoleUpdateTime; - foreach (var console in EntityManager.EntityQuery()) - { - if (!_uiSystem.IsUiOpen(console.Owner, ResearchConsoleUiKey.Key)) continue; - UpdateConsoleInterface(console); - } - - _timer -= ResearchConsoleUIUpdateTime; + UpdateServer(server, (int) server.ResearchConsoleUpdateTime.TotalSeconds); } } } diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs index 83fdd5a8a1..49d6af7fa8 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs @@ -4,8 +4,8 @@ using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Events; using Content.Server.Paper; using Content.Server.Power.Components; -using Content.Server.Research; using Content.Server.Research.Components; +using Content.Server.Research.Systems; using Content.Server.UserInterface; using Content.Server.Xenoarchaeology.Equipment.Components; using Content.Server.Xenoarchaeology.XenoArtifacts; @@ -38,6 +38,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly ArtifactSystem _artifact = default!; [Dependency] private readonly PaperSystem _paper = default!; + [Dependency] private readonly ResearchSystem _research = default!; /// public override void Initialize() @@ -332,7 +333,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem /// private void OnDestroyButton(EntityUid uid, AnalysisConsoleComponent component, AnalysisConsoleDestroyButtonPressedMessage args) { - if (!TryComp(uid, out var client) || client.Server == null || component.AnalyzerEntity == null) + if (!TryComp(uid, out var client) || client.Server is not { } server || component.AnalyzerEntity == null) return; var entToDestroy = GetArtifactForAnalysis(component.AnalyzerEntity); @@ -345,7 +346,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem ResetAnalyzer(component.AnalyzerEntity.Value); } - client.Server.Points += _artifact.GetResearchPointValue(entToDestroy.Value); + _research.ChangePointsOnServer(server.Owner, _artifact.GetResearchPointValue(entToDestroy.Value), server); EntityManager.DeleteEntity(entToDestroy.Value); _audio.PlayPvs(component.DestroySound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f));