Compare commits

...

8 Commits

Author SHA1 Message Date
DrSmugleaf
cbf01f0aa5 Fix ComponentChangedSerialized test (#1543) 2021-02-09 21:24:28 +01:00
Pieter-Jan Briers
6973d43006 Network timing synchronization.
Lidgren NetTime.Now is now synchronized to IGameTiming.RealTime.

NetChannel is now a nested class of NetManager.

Add IGameTiming.ServerTime, INetChannel.RemoteTimeOffset, INetChannel.RemoteTime
2021-02-09 14:28:20 +01:00
Pieter-Jan Briers
c5a961d9a0 Even better: use argStr. 2021-02-09 14:28:20 +01:00
Acruid
b4f7d71ee7 Server console can forward commands to a client. 2021-02-09 04:16:15 -08:00
metalgearsloth
cd3684e575 Guard against client appearancecomp changes (#1540)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-09 13:13:05 +01:00
Pieter-Jan Briers
317b8ce965 Fix sudo command mangling quotes. 2021-02-09 12:29:23 +01:00
Pieter-Jan Briers
ff96140afa Fix messed up \n. 2021-02-09 03:17:27 +01:00
Pieter-Jan Briers
bade95f6b7 Fix unit tests 2021-02-09 02:37:27 +01:00
14 changed files with 189 additions and 104 deletions

View File

@@ -36,7 +36,7 @@ namespace Robust.Client.Console
Message = message;
}
}
/// <inheritdoc cref="IClientConsoleHost" />
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
{
@@ -47,12 +47,20 @@ namespace Robust.Client.Console
{
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
Reset();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
private void ProcessCommand(MsgConCmd message)
{
string? text = message.Text;
LogManager.GetSawmill(SawmillName).Info($"SERVER:{text}");
ExecuteCommand(null, text);
}
/// <inheritdoc />
public void Reset()
{
@@ -104,14 +112,14 @@ namespace Robust.Client.Console
args.RemoveAt(0);
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
}
else if (!NetManager.IsConnected)
else
WriteError(null, "Unknown command: " + commandName);
}
/// <inheritdoc />
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
if (!NetManager.IsConnected)
if (!NetManager.IsConnected) // we don't care about session on client
return;
var msg = NetManager.CreateNetMessage<MsgConCmd>();

View File

@@ -78,6 +78,8 @@ namespace Robust.Client.GameObjects
private void SetData(object key, object value)
{
if (data.TryGetValue(key, out var existing) && existing.Equals(value)) return;
data[key] = value;
MarkDirty();
@@ -85,10 +87,9 @@ namespace Robust.Client.GameObjects
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState == null)
if (curState is not AppearanceComponentState actualState)
return;
var actualState = (AppearanceComponentState) curState;
data = actualState.Data;
MarkDirty();
}

View File

@@ -52,7 +52,7 @@ namespace Robust.Client.UserInterface.CustomControls
_contents.Text = $@"Paused: {_gameTiming.Paused}, CurTick: {_gameTiming.CurTick}/{_gameTiming.CurTick-1}, CurServerTick: {_gameState.CurServerTick}, Pred: {_gameTiming.CurTick.Value - _gameState.CurServerTick.Value-1}
CurTime: {_gameTiming.CurTime:hh\:mm\:ss\.ff}, RealTime: {_gameTiming.RealTime:hh\:mm\:ss\.ff}, CurFrame: {_gameTiming.CurFrame}
TickTimingAdjustment: {_gameTiming.TickTimingAdjustment}";
ServerTime: {_gameTiming.ServerTime}, TickTimingAdjustment: {_gameTiming.TickTimingAdjustment}";
MinimumSizeChanged();
}

View File

@@ -28,7 +28,12 @@ namespace Robust.Server.Console
/// <inheritdoc />
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
//TODO: Server -> Client remote execute, just like how the client forwards the command
if (!NetManager.IsConnected || session is null)
return;
var msg = NetManager.CreateNetMessage<MsgConCmd>();
msg.Text = command;
NetManager.ServerSendMessage(msg, ((IPlayerSession)session).ConnectedClient);
}
/// <inheritdoc />
@@ -52,16 +57,13 @@ namespace Robust.Server.Console
/// <inheritdoc />
public void Initialize()
{
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>",(shell, _, args) =>
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>",(shell, argStr, _) =>
{
string command = args[0];
var cArgs = args[1..].Select(CommandParsing.Escape);
var localShell = shell.ConsoleHost.LocalShell;
var sudoShell = new SudoShell(this, localShell, shell);
ExecuteInShell(sudoShell, $"{command} {string.Join(' ', cArgs)}");
ExecuteInShell(sudoShell, argStr.Substring("sudo ".Length));
});
LoadConsoleCommands();
// setup networking with clients

View File

@@ -1,4 +1,5 @@
using System.Net;
using System;
using System.Net;
using Robust.Shared.Network;
namespace Robust.Shared.Interfaces.Network
@@ -34,6 +35,16 @@ namespace Robust.Shared.Interfaces.Network
LoginType AuthType { get; }
/// <summary>
/// Offset between local RealTime and remote RealTime.
/// </summary>
TimeSpan RemoteTimeOffset { get; }
/// <summary>
/// Remote RealTime.
/// </summary>
TimeSpan RemoteTime { get; }
/// <summary>
/// Average round trip time in milliseconds between the remote peer and us.
/// </summary>

View File

@@ -31,6 +31,15 @@ namespace Robust.Shared.Interfaces.Timing
/// </summary>
TimeSpan RealTime { get; }
/// <summary>
/// The <see cref="RealTime"/> of the server.
/// </summary>
/// <remarks>
/// 0 if we are the client and we are not connected to a server.
/// <see cref="RealTime"/> if we are the server.
/// </remarks>
TimeSpan ServerTime { get; }
/// <summary>
/// The simulated time it took to render the last frame.
/// </summary>

View File

@@ -1,84 +0,0 @@
using System;
using System.Net;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
namespace Robust.Shared.Network
{
/// <summary>
/// A network connection from this local peer to a remote peer.
/// </summary>
internal class NetChannel : INetChannel
{
private readonly NetManager _manager;
private readonly NetConnection _connection;
/// <inheritdoc />
public long ConnectionId => _connection.RemoteUniqueIdentifier;
/// <inheritdoc />
public INetManager NetPeer => _manager;
public string UserName { get; }
public LoginType AuthType { get; }
/// <inheritdoc />
public short Ping => (short) Math.Round(_connection.AverageRoundtripTime * 1000);
/// <inheritdoc />
public bool IsConnected => _connection.Status == NetConnectionStatus.Connected;
/// <inheritdoc />
public IPEndPoint RemoteEndPoint => _connection.RemoteEndPoint;
/// <summary>
/// Exposes the lidgren connection.
/// </summary>
public NetConnection Connection => _connection;
public NetUserId UserId { get; }
// Only used on server, contains the encryption to use for this channel.
public NetEncryption? Encryption { get; set; }
/// <summary>
/// Creates a new instance of a NetChannel.
/// </summary>
/// <param name="manager">The server this channel belongs to.</param>
/// <param name="connection">The raw NetConnection to the remote peer.</param>
internal NetChannel(NetManager manager, NetConnection connection, NetUserId userId, string userName, LoginType loginType)
{
_manager = manager;
_connection = connection;
UserId = userId;
UserName = userName;
AuthType = loginType;
}
/// <inheritdoc />
public T CreateNetMessage<T>()
where T : NetMessage
{
return _manager.CreateNetMessage<T>();
}
/// <inheritdoc />
public void SendMessage(NetMessage message)
{
if (_manager.IsClient)
{
_manager.ClientSendMessage(message);
return;
}
_manager.ServerSendMessage(message, this);
}
/// <inheritdoc />
public void Disconnect(string reason)
{
if (_connection.Status == NetConnectionStatus.Connected)
_connection.Disconnect(reason);
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Net;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
namespace Robust.Shared.Network
{
public partial class NetManager
{
private class NetChannel : INetChannel
{
private readonly NetManager _manager;
private readonly NetConnection _connection;
/// <inheritdoc />
public long ConnectionId => _connection.RemoteUniqueIdentifier;
/// <inheritdoc />
public INetManager NetPeer => _manager;
public string UserName { get; }
public LoginType AuthType { get; }
public TimeSpan RemoteTimeOffset => TimeSpan.FromSeconds(_connection.RemoteTimeOffset);
public TimeSpan RemoteTime => _manager._timing.RealTime + RemoteTimeOffset;
/// <inheritdoc />
public short Ping => (short) Math.Round(_connection.AverageRoundtripTime * 1000);
/// <inheritdoc />
public bool IsConnected => _connection.Status == NetConnectionStatus.Connected;
/// <inheritdoc />
public IPEndPoint RemoteEndPoint => _connection.RemoteEndPoint;
/// <summary>
/// Exposes the lidgren connection.
/// </summary>
public NetConnection Connection => _connection;
public NetUserId UserId { get; }
// Only used on server, contains the encryption to use for this channel.
public NetEncryption? Encryption { get; set; }
/// <summary>
/// Creates a new instance of a NetChannel.
/// </summary>
/// <param name="manager">The server this channel belongs to.</param>
/// <param name="connection">The raw NetConnection to the remote peer.</param>
internal NetChannel(NetManager manager, NetConnection connection, NetUserId userId, string userName,
LoginType loginType)
{
_manager = manager;
_connection = connection;
UserId = userId;
UserName = userName;
AuthType = loginType;
}
/// <inheritdoc />
public T CreateNetMessage<T>()
where T : NetMessage
{
return _manager.CreateNetMessage<T>();
}
/// <inheritdoc />
public void SendMessage(NetMessage message)
{
if (_manager.IsClient)
{
_manager.ClientSendMessage(message);
return;
}
_manager.ServerSendMessage(message, this);
}
/// <inheritdoc />
public void Disconnect(string reason)
{
if (_connection.Status == NetConnectionStatus.Connected)
_connection.Disconnect(reason);
}
}
}
}

View File

@@ -105,7 +105,7 @@ namespace Robust.Shared.Network
// Launcher gives the client the public RSA key of the server BUT
// that doesn't persist if the server restarts.
// In that case, the decrypt can fail here.
connection.Disconnect("Token decryption failed./nPlease reconnect to this server from the launcher.");
connection.Disconnect("Token decryption failed.\nPlease reconnect to this server from the launcher.");
return;
}

View File

@@ -14,6 +14,7 @@ using Prometheus;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
@@ -112,6 +113,7 @@ namespace Robust.Shared.Network
[Dependency] private readonly IConfigurationManagerInternal _config = default!;
[Dependency] private readonly IAuthManager _authManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
/// <summary>
/// Holds lookup table for NetMessage.Id -> NetMessage.Type
@@ -235,6 +237,8 @@ namespace Robust.Shared.Network
throw new InvalidOperationException("NetManager has already been initialized.");
}
SynchronizeNetTime();
IsServer = isServer;
_config.OnValueChanged(CVars.NetVerbose, NetVerboseChanged);
@@ -265,6 +269,24 @@ namespace Robust.Shared.Network
}
}
private void SynchronizeNetTime()
{
// Synchronize Lidgren NetTime with our RealTime.
for (var i = 0; i < 10; i++)
{
// Try and set this in a loop to avoid any JIT hang fuckery or similar.
// Loop until the time is within acceptable margin.
// Fixing this "properly" would basically require re-architecturing Lidgren to do DI stuff
// so we can more sanely wire these together.
NetTime.SetNow(_timing.RealTime.TotalSeconds);
var dev = TimeSpan.FromSeconds(NetTime.Now) - _timing.RealTime;
if (Math.Abs(dev.TotalMilliseconds) < 0.05)
break;
}
}
private void UpdateNetMessageFunctions(MsgStringTableEntries.Entry[] entries)
{
foreach (var entry in entries)

View File

@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using Robust.Shared.Log;
namespace Robust.Shared.Timing
{
@@ -20,6 +20,8 @@ namespace Robust.Shared.Timing
private readonly List<long> _realFrameTimes = new(NumFrames);
private TimeSpan _lastRealTime;
[Dependency] private readonly INetManager _netManager = default!;
/// <summary>
/// Default constructor.
/// </summary>
@@ -87,6 +89,25 @@ namespace Robust.Shared.Timing
/// </summary>
public TimeSpan RealTime => _realTimer.Elapsed;
public TimeSpan ServerTime
{
get
{
if (_netManager.IsServer)
{
return RealTime;
}
var clientNetManager = (IClientNetManager) _netManager;
if (clientNetManager.ServerChannel == null)
{
return TimeSpan.Zero;
}
return clientNetManager.ServerChannel.RemoteTime;
}
}
/// <summary>
/// The simulated time it took to render the last frame.
/// </summary>

View File

@@ -5,6 +5,8 @@ using System.Net;
using System.Threading.Channels;
using System.Threading.Tasks;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Utility;
@@ -14,6 +16,7 @@ namespace Robust.UnitTesting
{
internal sealed class IntegrationNetManager : IClientNetManager, IServerNetManager
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
public bool IsServer { get; private set; }
public bool IsClient => !IsServer;
public bool IsRunning { get; private set; }
@@ -371,6 +374,8 @@ namespace Robust.UnitTesting
public NetUserId UserId { get; }
public string UserName { get; }
public LoginType AuthType => LoginType.GuestAssigned;
public TimeSpan RemoteTimeOffset => TimeSpan.Zero; // TODO: Fix this
public TimeSpan RemoteTime => _owner._gameTiming.RealTime + RemoteTimeOffset;
public short Ping => default;
public IntegrationNetChannel(IntegrationNetManager owner, ChannelWriter<object> otherChannel, int uid,

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using Robust.Server.Reflection;
@@ -12,11 +11,13 @@ using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Shared.GameObjects
{
@@ -38,6 +39,8 @@ namespace Robust.UnitTesting.Shared.GameObjects
container.Register<IReflectionManager, ServerReflectionManager>();
container.Register<IRobustSerializer, RobustSerializer>();
container.Register<IRobustMappedStringSerializer, RobustMappedStringSerializer>();
container.Register<IAuthManager, AuthManager>();
container.Register<IGameTiming, GameTiming>();
container.BuildGraph();
var cfg = container.Resolve<IConfigurationManagerInternal>();