using System; using System.Collections.Generic; using Robust.Shared.Collections; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Network; using Robust.Shared.Network.Messages; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Shared.Configuration { /// /// A networked configuration manager that controls the replication of /// console variables between client and server. /// [NotContentImplementable] public interface INetConfigurationManager : IConfigurationManager { /// /// Sets up the networking for the config manager. /// void SetupNetworking(); /// /// Gets the list of networked cvars that need to be sent to when connecting to server or client. /// /// If true, includes all replicated cvars. I.e., clients would include cvars that were /// received from the server, instead of only the ones that need to be sent to the server. /// List<(string name, object value)> GetReplicatedVars(bool all = false); /// /// Called every tick to process any incoming network messages. /// void TickProcessMessages(); /// /// Flushes any NwCVar messages in the receive buffer. /// void FlushMessages(); /// /// Get a replicated client CVar for a specific client. When used client-side, this simply returns the local cvar. /// /// CVar type. /// channel of the connected client. /// Name of the CVar. /// Replicated CVar of the client. T GetClientCVar(INetChannel channel, string name); /// /// Get a replicated client CVar for a specific client. /// /// CVar type. /// channel of the connected client. /// The CVar. /// Replicated CVar of the client. T GetClientCVar(INetChannel channel, CVarDef definition) where T : notnull => GetClientCVar(channel, definition.Name); } internal interface INetConfigurationManagerInternal : INetConfigurationManager, IConfigurationManagerInternal { } /// internal abstract partial class NetConfigurationManager : ConfigurationManager, INetConfigurationManagerInternal { [Dependency] protected INetManager NetManager = null!; [Dependency] protected IGameTiming Timing = null!; private readonly List _netVarsMessages = new(); protected ISawmill Sawmill = default!; public override void Shutdown() { base.Shutdown(); FlushMessages(); } /// public virtual void SetupNetworking() { Sawmill = Logger.GetSawmill("cfg"); Sawmill.Level = LogLevel.Info; NetManager.RegisterNetMessage(HandleNetVarMessage); } protected virtual void HandleNetVarMessage(MsgConVars message) { _netVarsMessages.Add(message); } /// public void TickProcessMessages() { if (!Timing.InSimulation || Timing.InPrediction) return; // _netVarsMessages is not in any particular ordering. // Copy any messages to apply to a separate list so we can sort before going through it. ValueList toApply = default; for (var i = 0; i < _netVarsMessages.Count; i++) { var msg = _netVarsMessages[i]; if (msg.Tick > Timing.CurTick) continue; toApply.Add(msg); _netVarsMessages.RemoveSwap(i); i--; } if (toApply.Count == 0) return; toApply.Sort((a, b) => a.Tick.CompareTo(b.Tick)); foreach (var msg in toApply) { ApplyNetVarChange(msg.MsgChannel, msg.NetworkedVars, msg.Tick); if(msg.Tick != default && msg.Tick < Timing.CurTick) Sawmill.Warning($"{msg.MsgChannel}: Received late nwVar message ({msg.Tick} < {Timing.CurTick} )."); } } /// public void FlushMessages() { _netVarsMessages.Sort(((a, b) => a.Tick.Value.CompareTo(b.Tick.Value))); foreach (var msg in _netVarsMessages) { ApplyNetVarChange(msg.MsgChannel, msg.NetworkedVars, msg.Tick); } _netVarsMessages.Clear(); } protected abstract void ApplyNetVarChange( INetChannel msgChannel, List<(string name, object value)> networkedVars, GameTick tick); /// public void SyncConnectingClient(INetChannel client) { DebugTools.Assert(NetManager.IsConnected); DebugTools.Assert(NetManager.IsServer); Sawmill.Info($"{client}: Sending server info..."); var msg = new MsgConVars(); msg.Tick = Timing.CurTick; msg.NetworkedVars = GetReplicatedVars(); NetManager.ServerSendMessage(msg, client); } public List<(string name, object value)> GetReplicatedVars(bool all = false) { using var _ = Lock.ReadGuard(); var nwVars = new List<(string name, object value)>(); foreach (var cVar in _configVars.Values) { if (!cVar.Registered) continue; if ((cVar.Flags & CVar.REPLICATED) == 0) continue; if (!all) { if (NetManager.IsClient) { if ((cVar.Flags & CVar.SERVER) != 0) continue; } else if ((cVar.Flags & CVar.CLIENT) != 0) continue; } nwVars.Add((cVar.Name, GetConfigVarValue(cVar))); Sawmill.Debug($"name={cVar.Name}, val={(cVar.Value ?? cVar.DefaultValue)}"); } return nwVars; } /// public abstract T GetClientCVar(INetChannel channel, string name); } }