Skip to content
50 changes: 50 additions & 0 deletions Robust.Client/Configuration/ClientNetConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Robust.Shared.Network;
using Robust.Shared.Replays;
using Robust.Shared.Utility;
using Robust.Shared.Player;
using Robust.Client.Player;

namespace Robust.Client.Configuration;

Expand All @@ -15,6 +17,7 @@ internal sealed class ClientNetConfigurationManager : NetConfigurationManager, I
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IReplayRecordingManager _replay = default!;
[Dependency] private readonly IPlayerManager _player = default!;

private bool _receivedInitialNwVars = false;

Expand Down Expand Up @@ -136,4 +139,51 @@ private void ApplyClientNetVarChange(List<(string name, object value)> networked

/// <inheritdoc />
public override T GetClientCVar<T>(INetChannel channel, string name) => GetCVar<T>(name);

public override void OnClientCVarChanges<T>(string name, Action<T, ICommonSession> onChanged)
{
if (_player.LocalSession is not { } localSession)
{
Sawmill.Error("Got null local session for client!");
return;
}

OnValueChanged<T>(name, (x) => onChanged(x, localSession), true);
}

/// <inheritdoc />
public override void OnClientCVarChanges<T>(string name, ClientCVarChanged<T> onChanged)
{
if (_player.LocalSession is not { } localSession)
{
Sawmill.Error("Got null local session for client!");
return;
}

OnValueChanged<T>(name, (T newValue, in CVarChangeInfo info) => onChanged(localSession, newValue, in info), true);
}

/// <inheritdoc />
public override void UnsubClientCVarChanges<T>(string name, Action<T, ICommonSession> onChanged)
{
if (_player.LocalSession is not { } localSession)
{
Sawmill.Error("Got null local session for client!");
return;
}

UnsubValueChanged<T>(name, (x) => onChanged(x, localSession));
}

/// <inheritdoc />
public override void UnsubClientCVarChanges<T>(string name, ClientCVarChanged<T> onChanged)
{
if (_player.LocalSession is not { } localSession)
{
Sawmill.Error("Got null local session for client!");
return;
}

UnsubValueChanged<T>(name, (T newValue, in CVarChangeInfo info) => onChanged(localSession, newValue, in info));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk if this will work. I'll write down in this thread after testing

}
}
195 changes: 181 additions & 14 deletions Robust.Server/Configuration/ServerNetConfigurationManager.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
using Robust.Server.Player;
using Robust.Shared.Collections;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Player;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
using Robust.Shared.IoC;
using Robust.Shared.Replays;

namespace Robust.Server.Configuration;

internal sealed class ServerNetConfigurationManager : NetConfigurationManager, IServerNetConfigurationManager
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

private readonly Dictionary<INetChannel, Dictionary<string, object>> _replicatedCVars = new();

private readonly Dictionary<string, ReplicatedCVarInvokes> _replicatedInvokes = new();


public override void SetupNetworking()
{
base.SetupNetworking();
Expand All @@ -26,7 +34,10 @@ public override void SetupNetworking()
public override void Shutdown()
{
base.Shutdown();


_replicatedCVars.Clear();
_replicatedInvokes.Clear();
}

private void PeerConnected(object? sender, NetChannelArgs e)
Expand Down Expand Up @@ -106,31 +117,187 @@ protected override void ApplyNetVarChange(
return;
}

using var _ = Lock.ReadGuard();
var cVarChanges = new List<CVarChangeInfo>();
using (Lock.ReadGuard())
{
foreach (var (name, value) in networkedVars)
{
if (!_configVars.TryGetValue(name, out var cVar))
{
Sawmill.Warning($"{msgChannel} tried to replicate an unknown CVar '{name}.'");
continue;
}

if (!cVar.Registered)
{
Sawmill.Warning($"{msgChannel} tried to replicate an unregistered CVar '{name}.'");
continue;
}

foreach (var (name, value) in networkedVars)
if ((cVar.Flags & CVar.REPLICATED) != 0)
{
clientCVars.TryGetValue(name, out var oldValue);
cVarChanges.Add(new(name, tick, value, oldValue ?? value));
clientCVars[name] = value;
Sawmill.Debug($"name={name}, val={value}");
}
else
{
Sawmill.Warning($"{msgChannel} tried to replicate an un-replicated CVar '{name}.'");
}
}
}

foreach (var info in cVarChanges)
{
InvokeClientCvarChange(info, msgChannel);
}
}

private void InvokeClientCvarChange(CVarChangeInfo info, INetChannel msgChannel)
{
if (!_playerManager.TryGetSessionByChannel(msgChannel, out var session))
{
if (!_configVars.TryGetValue(name, out var cVar))
Sawmill.Error($"Got client cvar change for NetChannel {msgChannel.UserId} without session!");
return;
}

if (!_replicatedInvokes.TryGetValue(info.Name, out var cVarInvokes))
return;

foreach (var entry in cVarInvokes.ClientChangeInvoke.Entries)
{
try
{
entry.Value!.Invoke(info.NewValue, session, in info);
}
catch (Exception e)
{
Sawmill.Warning($"{msgChannel} tried to replicate an unknown CVar '{name}.'");
continue;
Sawmill.Error($"Error while running {nameof(ClientValueChangedDelegate)} for replicated CVars callback: {e}");
}
}
}

/// <inheritdoc />
public override void OnClientCVarChanges<T>(string name, Action<T, ICommonSession> onChanged)
{
if (!_configVars.TryGetValue(name, out var cVar))
{
Sawmill.Error($"Tried to subscribe an unknown CVar '{name}.'");
return;
}

if (!cVar.Registered)
if (!cVar.Flags.HasFlag(CVar.REPLICATED) || !cVar.Flags.HasFlag(CVar.CLIENT))
{
Sawmill.Error($"Tried to subscribe server to client cvar '{name}' but cvar don't have flags CLIENT | REPLICATED");
return;
}

using (Lock.WriteGuard())
{
if (!_replicatedInvokes.TryGetValue(name, out var cVarInvokes))
{
cVarInvokes = new ReplicatedCVarInvokes { };
cVarInvokes.ClientChangeInvoke.AddInPlace((object value, ICommonSession session, in CVarChangeInfo _) => onChanged((T)value, session), onChanged);

_replicatedInvokes.Add(name, cVarInvokes);
}
else
{
Sawmill.Warning($"{msgChannel} tried to replicate an unregistered CVar '{name}.'");
continue;
cVarInvokes.ClientChangeInvoke.AddInPlace((object value, ICommonSession session, in CVarChangeInfo _) => onChanged((T)value, session), onChanged);
}
}
}

/// <inheritdoc />
public override void OnClientCVarChanges<T>(string name, ClientCVarChanged<T> onChanged)
{
if (!_configVars.TryGetValue(name, out var cVar))
{
Sawmill.Error($"Tried to subscribe an unknown CVar '{name}.'");
return;
}

if ((cVar.Flags & CVar.REPLICATED) != 0)
if (!cVar.Flags.HasFlag(CVar.REPLICATED) || !cVar.Flags.HasFlag(CVar.CLIENT))
{
Sawmill.Error($"Tried to subscribe server to client cvar '{name}' but cvar don't have flags CLIENT | REPLICATED");
return;
}

using (Lock.WriteGuard())
{
if (!_replicatedInvokes.TryGetValue(name, out var cVarInvokes))
{
clientCVars[name] = value;
Sawmill.Debug($"name={name}, val={value}");
cVarInvokes = new ReplicatedCVarInvokes { };
cVarInvokes.ClientChangeInvoke.AddInPlace((object value, ICommonSession session, in CVarChangeInfo info) => onChanged(session, (T)value, info), onChanged);

_replicatedInvokes.Add(name, cVarInvokes);
}
else
{
Sawmill.Warning($"{msgChannel} tried to replicate an un-replicated CVar '{name}.'");
cVarInvokes.ClientChangeInvoke.AddInPlace((object value, ICommonSession session, in CVarChangeInfo info) => onChanged(session, (T)value , info), onChanged);
}
}
}

/// <inheritdoc />
public override void UnsubClientCVarChanges<T>(string name, Action<T, ICommonSession> onValueChanged)
{
if (!_configVars.TryGetValue(name, out var cVar))
{
Sawmill.Error($"Tried to unsubscribe an unknown CVar '{name}.'");
return;
}

if (!cVar.Flags.HasFlag(CVar.REPLICATED) || !cVar.Flags.HasFlag(CVar.CLIENT))
{
Sawmill.Error($"Tried to unsubscribe client cvar '{name}' without flags CLIENT | REPLICATED");
return;
}

using (Lock.WriteGuard())
{
if (!_replicatedInvokes.TryGetValue(name, out var cVarInvokes))
{
Sawmill.Warning($"Trying to unsubscribe for cvar {name} changes that dont have any subscriptions at all!");
return;
}

cVarInvokes.ClientChangeInvoke.RemoveInPlace(onValueChanged);
}
}

/// <inheritdoc />
public override void UnsubClientCVarChanges<T>(string name, ClientCVarChanged<T> onChanged)
{
if (!_configVars.TryGetValue(name, out var cVar))
{
Sawmill.Error($"Tried to unsubscribe an unknown CVar '{name}.'");
return;
}

if (!cVar.Flags.HasFlag(CVar.REPLICATED) || !cVar.Flags.HasFlag(CVar.CLIENT))
{
Sawmill.Error($"Tried to unsubscribe client cvar '{name}' without flags CLIENT | REPLICATED");
return;
}

using (Lock.WriteGuard())
{
if (!_replicatedInvokes.TryGetValue(name, out var cVarInvokes))
{
Sawmill.Warning($"Trying to unsubscribe for cvar {name} changes that dont have any subscriptions at all!");
return;
}

cVarInvokes.ClientChangeInvoke.RemoveInPlace(onChanged);
}
}

private delegate void DisconnectDelegate(ICommonSession session);

private sealed class ReplicatedCVarInvokes
{
public InvokeList<ClientValueChangedDelegate> ClientChangeInvoke = new();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Moq;
using NUnit.Framework;
using Robust.Server.Configuration;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Log;
Expand Down Expand Up @@ -142,6 +143,7 @@ private IConfigurationManager MakeCfg()
var collection = new DependencyCollection();
collection.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
collection.RegisterInstance<INetManager>(new Mock<INetManager>().Object);
collection.RegisterInstance<IPlayerManager>(new Mock<IPlayerManager>().Object);
collection.Register<ConfigurationManager, ServerNetConfigurationManager>();
collection.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
collection.Register<IGameTiming, GameTiming>();
Expand Down
Loading
Loading