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
This commit is contained in:
Pieter-Jan Briers
2021-02-09 14:28:04 +01:00
parent c5a961d9a0
commit 6973d43006
9 changed files with 160 additions and 89 deletions

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

@@ -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

@@ -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,