Shared String Dictionary ctd. (#1126)

This commit is contained in:
Tyler Young
2020-06-12 22:09:48 -04:00
committed by GitHub
parent 15ec0e7bd9
commit 9662d52f90
24 changed files with 1855 additions and 1548 deletions

View File

@@ -80,11 +80,11 @@ namespace Robust.Client
_commandLineArgs = args;
}
public bool Startup()
public bool Startup(Func<ILogHandler>? logHandlerFactory = null)
{
ReadInitialLaunchState();
SetupLogging(_logManager);
SetupLogging(_logManager, logHandlerFactory ?? (() => new ConsoleLogHandler()));
_taskManager.Initialize();
@@ -286,9 +286,9 @@ namespace Robust.Client
_modLoader.BroadcastUpdate(ModUpdateLevel.FramePostEngine, frameEventArgs);
}
internal static void SetupLogging(ILogManager logManager)
internal static void SetupLogging(ILogManager logManager, Func<ILogHandler> logHandlerFactory)
{
logManager.RootSawmill.AddHandler(new ConsoleLogHandler());
logManager.RootSawmill.AddHandler(logHandlerFactory());
logManager.GetSawmill("res.typecheck").Level = LogLevel.Info;
logManager.GetSawmill("res.tex").Level = LogLevel.Info;

View File

@@ -1,5 +1,7 @@
using System.Net;
using System;
using System.Net;
using Robust.Client.Input;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Timing;
namespace Robust.Client.Interfaces
@@ -15,7 +17,7 @@ namespace Robust.Client.Interfaces
{
void SetCommandLineArgs(CommandLineArgs args);
bool LoadConfigAndUserData { get; set; }
bool Startup();
bool Startup(Func<ILogHandler>? logHandlerFactory = null);
void MainLoop(GameController.DisplayMode mode);
void KeyDown(KeyEventArgs keyEvent);
void KeyUp(KeyEventArgs keyEvent);

View File

@@ -36,6 +36,8 @@ using Robust.Server.ServerStatus;
using Robust.Shared;
using Robust.Shared.Network.Messages;
using Robust.Server.DataMetrics;
using Robust.Server.Interfaces.Maps;
using Robust.Shared.Serialization;
using Stopwatch = Robust.Shared.Timing.Stopwatch;
namespace Robust.Server
@@ -80,7 +82,8 @@ namespace Robust.Server
private readonly Stopwatch _uptimeStopwatch = new Stopwatch();
private CommandLineArgs _commandLineArgs = default!;
private FileLogHandler fileLogHandler = default!;
private Func<ILogHandler>? _logHandlerFactory;
private ILogHandler? _logHandler;
private IGameLoop _mainLoop = default!;
private TimeSpan _lastTitleUpdate;
@@ -103,7 +106,7 @@ namespace Robust.Server
Logger.InfoS("srv", "Restarting Server...");
Cleanup();
Start();
Start(_logHandlerFactory);
}
/// <inheritdoc />
@@ -117,8 +120,11 @@ namespace Robust.Server
_shutdownReason = reason;
_mainLoop.Running = false;
_log.RootSawmill.RemoveHandler(fileLogHandler);
fileLogHandler.Dispose();
if (_logHandler != null)
{
_log.RootSawmill.RemoveHandler(_logHandler);
(_logHandler as IDisposable)?.Dispose();
}
}
public void SetCommandLineArgs(CommandLineArgs args)
@@ -127,7 +133,7 @@ namespace Robust.Server
}
/// <inheritdoc />
public bool Start()
public bool Start(Func<ILogHandler>? logHandlerFactory = null)
{
// Sets up the configMgr
// If a config file path was passed, use it literally.
@@ -160,24 +166,40 @@ namespace Robust.Server
//Sets up Logging
_config.RegisterCVar("log.enabled", true, CVar.ARCHIVE);
_config.RegisterCVar("log.path", "logs", CVar.ARCHIVE);
_config.RegisterCVar("log.format", "log_%(date)s-T%(time)s.txt", CVar.ARCHIVE);
_config.RegisterCVar("log.level", LogLevel.Info, CVar.ARCHIVE);
var logPath = _config.GetCVar<string>("log.path");
var logFormat = _config.GetCVar<string>("log.format");
var logFilename = logFormat.Replace("%(date)s", DateTime.Now.ToString("yyyy-MM-dd"))
.Replace("%(time)s", DateTime.Now.ToString("hh-mm-ss"));
var fullPath = Path.Combine(logPath, logFilename);
_logHandlerFactory = logHandlerFactory;
if (!Path.IsPathRooted(fullPath))
var logHandler = logHandlerFactory?.Invoke() ?? null;
var logEnabled = _config.GetCVar<bool>("log.enabled");
if (logEnabled && logHandler == null)
{
logPath = PathHelpers.ExecutableRelativeFile(fullPath);
var logPath = _config.GetCVar<string>("log.path");
var logFormat = _config.GetCVar<string>("log.format");
var logFilename = logFormat.Replace("%(date)s", DateTime.Now.ToString("yyyy-MM-dd"))
.Replace("%(time)s", DateTime.Now.ToString("hh-mm-ss"));
var fullPath = Path.Combine(logPath, logFilename);
if (!Path.IsPathRooted(fullPath))
{
logPath = PathHelpers.ExecutableRelativeFile(fullPath);
}
logHandler = new FileLogHandler(logPath);
}
fileLogHandler = new FileLogHandler(logPath);
_log.RootSawmill.Level = _config.GetCVar<LogLevel>("log.level");
_log.RootSawmill.AddHandler(fileLogHandler);
if (logEnabled && logHandler != null)
{
_logHandler = logHandler;
_log.RootSawmill.AddHandler(_logHandler!);
}
// Has to be done early because this guy's in charge of the main thread Synchronization Context.
_taskManager.Initialize();
@@ -241,6 +263,11 @@ namespace Robust.Server
// TODO: solve this properly.
_serializer.Initialize();
//IoCManager.Resolve<IMapLoader>().LoadedMapData +=
// IoCManager.Resolve<IRobustMappedStringSerializer>().AddStrings;
IoCManager.Resolve<IPrototypeManager>().LoadedData +=
IoCManager.Resolve<IRobustMappedStringSerializer>().AddStrings;
// Initialize Tier 2 services
IoCManager.Resolve<IGameTiming>().InSimulation = true;
@@ -395,7 +422,9 @@ namespace Robust.Server
// Wrtie down exception log
var logPath = _config.GetCVar<string>("log.path");
var pathToWrite = Path.Combine(PathHelpers.ExecutableRelativeFile(logPath),
var relPath = PathHelpers.ExecutableRelativeFile(logPath);
Directory.CreateDirectory(relPath);
var pathToWrite = Path.Combine(relPath,
"Runtime-" + DateTime.Now.ToString("yyyy-MM-dd-THH-mm-ss") + ".txt");
File.WriteAllText(pathToWrite, runtimeLog.Display(), EncodingHelpers.UTF8);

View File

@@ -1,5 +1,6 @@
using System;
using Robust.Server.Player;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Timing;
namespace Robust.Server.Interfaces
@@ -23,7 +24,7 @@ namespace Robust.Server.Interfaces
/// Sets up the server, loads the game, gets ready for client connections.
/// </summary>
/// <returns></returns>
bool Start();
bool Start(Func<ILogHandler>? logHandler = null);
/// <summary>
/// Hard restarts the server, shutting it down, kicking all players, and starting the server again.

View File

@@ -1,4 +1,6 @@
using Robust.Shared.Map;
using System;
using Robust.Shared.Map;
using YamlDotNet.RepresentationModel;
namespace Robust.Server.Interfaces.Maps
{
@@ -9,5 +11,7 @@ namespace Robust.Server.Interfaces.Maps
void LoadMap(MapId mapId, string path);
void SaveMap(MapId mapId, string yamlPath);
event Action<YamlStream, string> LoadedMapData;
}
}

View File

@@ -39,6 +39,8 @@ namespace Robust.Server.Maps
[Dependency] private readonly IComponentManager _componentManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public event Action<YamlStream, string>? LoadedMapData;
/// <inheritdoc />
public void SaveBlueprint(GridId gridId, string yamlPath)
{
@@ -99,6 +101,8 @@ namespace Robust.Server.Maps
var data = new MapData(reader);
LoadedMapData?.Invoke(data.Stream, resPath.ToString());
if (data.GridCount != 1)
{
throw new InvalidDataException("Cannot instance map with multiple grids as blueprint.");
@@ -182,6 +186,8 @@ namespace Robust.Server.Maps
var data = new MapData(reader);
LoadedMapData?.Invoke(data.Stream, resPath.ToString());
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _componentManager, _prototypeManager, (YamlMappingNode)data.RootNode, mapId);
context.Deserialize();
@@ -849,7 +855,9 @@ namespace Robust.Server.Maps
/// </summary>
private class MapData
{
public YamlNode RootNode { get; }
public YamlStream Stream { get; }
public YamlNode RootNode => Stream.Documents[0].RootNode;
public int GridCount { get; }
public MapData(TextReader reader)
@@ -869,9 +877,8 @@ namespace Robust.Server.Maps
throw new InvalidDataException("Stream too many YAML documents. Map files store exactly one.");
}
RootNode = stream.Documents[0].RootNode;
Stream = stream;
GridCount = ((YamlSequenceNode)RootNode["grids"]).Children.Count;
RobustSerializer.MappedStringSerializer.AddStrings(stream, "anonymous map YAML stream");
}
}
}

View File

@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using JetBrains.Annotations;
using NetSerializer;
using Newtonsoft.Json.Linq;
using Robust.Shared.Interfaces.Network;
using YamlDotNet.RepresentationModel;
namespace Robust.Shared.Serialization
{
[PublicAPI]
public interface IRobustMappedStringSerializer
{
/// <summary>
/// Starts the handshake from the server end of the given channel,
/// sending a <see cref="MsgRobustMappedStringsSerializerServerHandshake"/>.
/// </summary>
/// <param name="channel">The network channel to perform the handshake over.</param>
/// <remarks>
/// Locks the string mapping if this is the first time the server is
/// performing the handshake.
/// </remarks>
/// <seealso cref="MsgRobustMappedStringsSerializerClientHandshake"/>
/// <seealso cref="MsgRobustMappedStringsSerializerStrings"/>
Task Handshake(INetChannel channel);
/// <summary>
/// Performs the setup so that the serializer can perform the string-
/// exchange protocol.
/// </summary>
/// <remarks>
/// The string-exchange protocol is started by the server when the
/// client first connects. The server sends the client a hash of the
/// string mapping; the client checks that hash against any local
/// caches; and if necessary, the client requests a new copy of the
/// mapping from the server.
///
/// Uncached flow: <code>
/// Client | Server
/// | &lt;-------------- Hash |
/// | Need Strings ------&gt; |
/// | &lt;----------- Strings |
/// | Dont Need Strings -&gt; |
/// </code>
///
/// Cached flow: <code>
/// Client | Server
/// | &lt;-------------- Hash |
/// | Dont Need Strings -&gt; |
/// </code>
///
/// Verification failure flow: <code>
/// Client | Server
/// | &lt;-------------- Hash |
/// | Need Strings ------&gt; |
/// | &lt;----------- Strings |
/// + Hash Failed |
/// | Need Strings ------&gt; |
/// | &lt;----------- Strings |
/// | Dont Need Strings -&gt; |
/// </code>
///
/// NOTE: Verification failure flow is currently not implemented.
/// </remarks>
/// <param name="net">
/// The <see cref="INetManager"/> to perform the protocol steps over.
/// </param>
/// <seealso cref="MsgRobustMappedStringsSerializerServerHandshake"/>
/// <seealso cref="MsgRobustMappedStringsSerializerClientHandshake"/>
/// <seealso cref="MsgRobustMappedStringsSerializerStrings"/>
/// <seealso cref="RobustMappedStringSerializer.HandleServerHandshake"/>
/// <seealso cref="RobustMappedStringSerializer.HandleClientHandshake"/>
/// <seealso cref="RobustMappedStringSerializer.HandleStringsMessage"/>
/// <seealso cref="RobustMappedStringSerializer.OnClientCompleteHandshake"/>
void NetworkInitialize(INetManager net);
/// <summary>
/// Writes a strings package to a stream.
/// </summary>
/// <param name="stream">A writable stream.</param>
/// <exception cref="NotImplementedException">Overly long string in strings package.</exception>
void WriteStringPackage(Stream stream);
/// <summary>
/// Converts a URL-safe Base64 string into a byte array.
/// </summary>
/// <param name="s">A base64url formed string.</param>
/// <returns>The represented byte array.</returns>
byte[] ConvertFromBase64Url(string s);
IReadOnlyList<String> MappedStrings { get; }
/// <summary>
/// Whether the string mapping is decided, and cannot be changed.
/// </summary>
/// <value>
/// <para>
/// While <c>false</c>, strings can be added to the mapping, but
/// it cannot be saved to a cache.
/// </para>
/// <para>
/// While <c>true</c>, the mapping cannot be modified, but can be
/// shared between the server and client and saved to a cache.
/// </para>
/// </value>
bool LockMappedStrings { get; set; }
/// <value>
/// The hash of the string mapping.
/// </value>
/// <exception cref="InvalidOperationException">
/// Thrown if the mapping is not locked.
/// </exception>
byte[] MappedStringsHash { get; }
/// <summary>
/// Add a string to the constant mapping.
/// </summary>
/// <remarks>
/// If the string has multiple detectable subcomponents, such as a
/// filepath, it may result in more than one string being added to
/// the mapping. As string parts are commonly sent as subsets or
/// scoped names, this increases the likelyhood of a successful
/// string mapping.
/// </remarks>
/// <returns>
/// <c>true</c> if the string was added to the mapping for the first
/// time, <c>false</c> otherwise.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if the string is not normalized (<see cref="String.IsNormalized()"/>).
/// </exception>
bool AddString(string str);
/// <summary>
/// Add the constant strings from an <see cref="Assembly"/> to the
/// mapping.
/// </summary>
/// <param name="asm">The assembly from which to collect constant strings.</param>
void AddStrings(Assembly asm);
/// <summary>
/// Add strings from the given <see cref="YamlStream"/> to the mapping.
/// </summary>
/// <remarks>
/// Strings are taken from YAML anchors, tags, and leaf nodes.
/// </remarks>
/// <param name="yaml">The YAML to collect strings from.</param>
/// <param name="name">The stream name. Only used for logging.</param>
void AddStrings(YamlStream yaml, string name);
/// <summary>
/// Add strings from the given <see cref="JObject"/> to the mapping.
/// </summary>
/// <remarks>
/// Strings are taken from JSON property names and string nodes.
/// </remarks>
/// <param name="obj">The JSON to collect strings from.</param>
/// <param name="name">The stream name. Only used for logging.</param>
void AddStrings(JObject obj, string name);
/// <summary>
/// Remove all strings from the mapping, completely resetting it.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown if the mapping is locked.
/// </exception>
void ClearStrings();
/// <summary>
/// Add strings from the given enumeration to the mapping.
/// </summary>
/// <param name="strings">The strings to add.</param>
/// <param name="providerName">The source provider of the strings to be logged.</param>
void AddStrings(IEnumerable<string> strings, string providerName);
/// <summary>
/// Implements <see cref="ITypeSerializer.Handles"/>.
/// Specifies that this implementation handles strings.
/// </summary>
bool Handles(Type type);
/// <summary>
/// Implements <see cref="ITypeSerializer.GetSubtypes"/>.
/// </summary>
IEnumerable<Type> GetSubtypes(Type type);
/// <summary>
/// Implements <see cref="IStaticTypeSerializer.GetStaticWriter"/>.
/// </summary>
/// <seealso cref="RobustMappedStringSerializer.WriteMappedString"/>
MethodInfo GetStaticWriter(Type type);
/// <summary>
/// Implements <see cref="IStaticTypeSerializer.GetStaticReader"/>.
/// </summary>
/// <seealso cref="RobustMappedStringSerializer.ReadMappedString"/>
MethodInfo GetStaticReader(Type type);
/// <summary>
/// Write the encoding of the given string to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="value"> The (possibly null) string to write.</param>
void WriteMappedString(Stream stream, string? value);
/// <summary>
/// Try to read a string from the given stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="value"> The (possibly null) string read.</param>
/// <exception cref="InvalidOperationException">
/// Thrown if the mapping is not locked.
/// </exception>
void ReadMappedString(Stream stream, out string? value);
/// <summary>
/// See <see cref="RobustMappedStringSerializer.OnClientCompleteHandshake"/>.
/// </summary>
event Action? ClientHandshakeComplete;
}
}

View File

@@ -164,8 +164,8 @@ namespace Robust.Shared.Localization
{
_readEntry(entry, catalog);
}
RobustSerializer.MappedStringSerializer.AddStrings(yamlStream, filePath.ToString());
IoCManager.Resolve<IRobustMappedStringSerializer>()
.AddStrings(yamlStream, filePath.ToString());
}
private static void _readEntry(YamlMappingNode entry, Catalog catalog)

View File

@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using JetBrains.Annotations;
using Robust.Shared.Asynchronous;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.IoC;
@@ -70,6 +71,9 @@ namespace Robust.Shared.Prototypes
/// Registers a specific prototype name to be ignored.
/// </summary>
void RegisterIgnore(string name);
event Action<YamlStream, string>? LoadedData;
}
/// <summary>
@@ -214,7 +218,7 @@ namespace Robust.Shared.Prototypes
var result = ((YamlStream? yamlStream, ResourcePath?))(yamlStream, filePath);
RobustSerializer.MappedStringSerializer.AddStrings(yamlStream, filePath.ToString());
LoadedData?.Invoke(yamlStream, filePath.ToString());
return result;
}
@@ -261,8 +265,7 @@ namespace Robust.Shared.Prototypes
}
}
RobustSerializer.MappedStringSerializer.AddStrings(yaml, "anonymous prototypes YAML stream");
LoadedData?.Invoke(yaml, "anonymous prototypes YAML stream");
}
#endregion IPrototypeManager members
@@ -355,6 +358,9 @@ namespace Robust.Shared.Prototypes
{
IgnoredPrototypeTypes.Add(name);
}
public event Action<YamlStream, string>? LoadedData;
}
[Serializable]

View File

@@ -35,18 +35,6 @@
</ItemGroup>
<ItemGroup>
<Compile Remove="Input\CommandBindMapping.cs" />
<Compile Update="Serialization\RobustSerializer.MappedStringSerializer.MsgClientHandshake.cs">
<DependentUpon>RobustSerializer.MappedStringSerializer.cs</DependentUpon>
</Compile>
<Compile Update="Serialization\RobustSerializer.MappedStringSerializer.MsgServerHandshake.cs">
<DependentUpon>RobustSerializer.MappedStringSerializer.cs</DependentUpon>
</Compile>
<Compile Update="Serialization\RobustSerializer.MappedStringSerializer.MsgStrings.cs">
<DependentUpon>RobustSerializer.MappedStringSerializer.cs</DependentUpon>
</Compile>
<Compile Update="Serialization\RobustSerializer.MappedStringSerializer.cs">
<DependentUpon>RobustSerializer.cs</DependentUpon>
</Compile>
<Compile Update="Serialization\RobustSerializer.Handshake.cs">
<DependentUpon>RobustSerializer.cs</DependentUpon>
</Compile>

View File

@@ -0,0 +1,45 @@
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
/// <summary>
/// The client part of the string-exchange handshake, sent after the
/// client receives the mapping hash and after the client receives a
/// strings package. Tells the server if the client needs an updated
/// copy of the mapping.
/// </summary>
/// <remarks>
/// Also sent by the client after a new copy of the string mapping
/// has been received. If successfully loaded, the value of
/// <see cref="NeedsStrings"/> is <c>false</c>, otherwise it will be
/// <c>true</c>.
/// </remarks>
/// <seealso cref="RobustMappedStringSerializer.NetworkInitialize"/>
[UsedImplicitly]
internal class MsgRobustMappedStringsSerializerClientHandshake : NetMessage
{
public MsgRobustMappedStringsSerializerClientHandshake(INetChannel ch)
: base(nameof(MsgRobustMappedStringsSerializerClientHandshake), MsgGroups.Core)
{
}
/// <value>
/// <c>true</c> if the client needs a new copy of the mapping,
/// <c>false</c> otherwise.
/// </value>
public bool NeedsStrings { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
=> NeedsStrings = buffer.ReadBoolean();
public override void WriteToBuffer(NetOutgoingMessage buffer)
=> buffer.Write(NeedsStrings);
}
}

View File

@@ -0,0 +1,54 @@
using System;
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
/// <summary>
/// The server part of the string-exchange handshake. Sent as the
/// first message in the handshake. Tells the client the hash of
/// the current string mapping, so the client can check if it has
/// a local copy.
/// </summary>
/// <seealso cref="RobustMappedStringSerializer.NetworkInitialize"/>
[UsedImplicitly]
internal class MsgRobustMappedStringsSerializerServerHandshake : NetMessage
{
public MsgRobustMappedStringsSerializerServerHandshake(INetChannel ch)
: base(nameof(MsgRobustMappedStringsSerializerServerHandshake), MsgGroups.Core)
{
}
/// <value>
/// The hash of the current string mapping held by the server.
/// </value>
public byte[]? Hash { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var len = buffer.ReadVariableInt32();
if (len > 64)
{
throw new InvalidOperationException("Hash too long.");
}
Hash = buffer.ReadBytes(len);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
if (Hash == null)
{
throw new InvalidOperationException("Package has not been specified.");
}
buffer.WriteVariableInt32(Hash.Length);
buffer.Write(Hash);
}
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.IO;
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
/// <summary>
/// The meat of the string-exchange handshake sandwich. Sent by the
/// server after the client requests an updated copy of the mapping.
/// Contains the updated string mapping.
/// </summary>
/// <seealso cref="RobustMappedStringSerializer.NetworkInitialize"/>
[UsedImplicitly]
internal class MsgRobustMappedStringsSerializerStrings : NetMessage
{
public MsgRobustMappedStringsSerializerStrings(INetChannel ch)
: base(nameof(MsgRobustMappedStringsSerializerStrings), MsgGroups.Core)
{
}
/// <value>
/// The raw bytes of the string mapping held by the server.
/// </value>
public byte[]? Package { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var l = buffer.ReadVariableInt32();
var success = buffer.ReadBytes(l, out var buf);
if (!success)
{
throw new InvalidDataException("Not all of the bytes were available in the message.");
}
Package = buf;
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
if (Package == null)
{
throw new InvalidOperationException("Package has not been specified.");
}
buffer.WriteVariableInt32(Package.Length);
var start = buffer.LengthBytes;
buffer.Write(Package);
var added = buffer.LengthBytes - start;
if (added != Package.Length)
{
throw new InvalidOperationException("Not all of the bytes were written to the message.");
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ namespace Robust.Shared.Serialization
/// <param name="channel"></param>
/// <returns></returns>
public Task Handshake(INetChannel channel)
=> MappedStringSerializer.Handshake(channel);
=> _mappedStringSerializer.Handshake(channel);
/// <summary>
/// An event that occurs once all handshake extensions have
@@ -26,8 +26,8 @@ namespace Robust.Shared.Serialization
/// </summary>
public event Action ClientHandshakeComplete
{
add => MappedStringSerializer.ClientHandshakeComplete += value;
remove => MappedStringSerializer.ClientHandshakeComplete -= value;
add => _mappedStringSerializer.ClientHandshakeComplete += value;
remove => _mappedStringSerializer.ClientHandshakeComplete -= value;
}
}

View File

@@ -1,54 +0,0 @@
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
public partial class RobustSerializer
{
public partial class MappedStringSerializer
{
/// <summary>
/// The client part of the string-exchange handshake, sent after the
/// client receives the mapping hash and after the client receives a
/// strings package. Tells the server if the client needs an updated
/// copy of the mapping.
/// </summary>
/// <remarks>
/// Also sent by the client after a new copy of the string mapping
/// has been received. If successfully loaded, the value of
/// <see cref="NeedsStrings"/> is <c>false</c>, otherwise it will be
/// <c>true</c>.
/// </remarks>
/// <seealso cref="NetworkInitialize"/>
[UsedImplicitly]
private class MsgClientHandshake : NetMessage
{
public MsgClientHandshake(INetChannel ch)
: base($"{nameof(RobustSerializer)}.{nameof(MappedStringSerializer)}.{nameof(MsgClientHandshake)}", MsgGroups.Core)
{
}
/// <value>
/// <c>true</c> if the client needs a new copy of the mapping,
/// <c>false</c> otherwise.
/// </value>
public bool NeedsStrings { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
=> NeedsStrings = buffer.ReadBoolean();
public override void WriteToBuffer(NetOutgoingMessage buffer)
=> buffer.Write(NeedsStrings);
}
}
}
}

View File

@@ -1,65 +0,0 @@
using System;
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
public partial class RobustSerializer
{
public partial class MappedStringSerializer
{
/// <summary>
/// The server part of the string-exchange handshake. Sent as the
/// first message in the handshake. Tells the client the hash of
/// the current string mapping, so the client can check if it has
/// a local copy.
/// </summary>
/// <seealso cref="NetworkInitialize"/>
[UsedImplicitly]
private class MsgServerHandshake : NetMessage
{
public MsgServerHandshake(INetChannel ch)
: base($"{nameof(RobustSerializer)}.{nameof(MappedStringSerializer)}.{nameof(MsgServerHandshake)}", MsgGroups.Core)
{
}
/// <value>
/// The hash of the current string mapping held by the server.
/// </value>
public byte[]? Hash { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var len = buffer.ReadVariableInt32();
if (len > 64)
{
throw new InvalidOperationException("Hash too long.");
}
Hash = buffer.ReadBytes(len);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
if (Hash == null)
{
throw new InvalidOperationException("Package has not been specified.");
}
buffer.WriteVariableInt32(Hash.Length);
buffer.Write(Hash);
}
}
}
}
}

View File

@@ -1,72 +0,0 @@
using System;
using System.IO;
using JetBrains.Annotations;
using Lidgren.Network;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
public partial class RobustSerializer
{
public partial class MappedStringSerializer
{
/// <summary>
/// The meat of the string-exchange handshake sandwich. Sent by the
/// server after the client requests an updated copy of the mapping.
/// Contains the updated string mapping.
/// </summary>
/// <seealso cref="NetworkInitialize"/>
[UsedImplicitly]
private class MsgStrings : NetMessage
{
public MsgStrings(INetChannel ch)
: base($"{nameof(RobustSerializer)}.{nameof(MappedStringSerializer)}.{nameof(MsgStrings)}", MsgGroups.Core)
{
}
/// <value>
/// The raw bytes of the string mapping held by the server.
/// </value>
public byte[]? Package { get; set; }
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var l = buffer.ReadVariableInt32();
var success = buffer.ReadBytes(l, out var buf);
if (!success)
{
throw new InvalidDataException("Not all of the bytes were available in the message.");
}
Package = buf;
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
if (Package == null)
{
throw new InvalidOperationException("Package has not been specified.");
}
buffer.WriteVariableInt32(Package.Length);
var start = buffer.LengthBytes;
buffer.Write(Package);
var added = buffer.LengthBytes - start;
if (added != Package.Length)
{
throw new InvalidOperationException("Not all of the bytes were written to the message.");
}
}
}
}
}
}

View File

@@ -23,6 +23,8 @@ namespace Robust.Shared.Serialization
private HashSet<Type> _serializableTypes = default!;
private readonly RobustMappedStringSerializer _mappedStringSerializer = new RobustMappedStringSerializer();
#region Statistics
public static long LargestObjectSerializedBytes { get; private set; }
@@ -45,7 +47,8 @@ namespace Robust.Shared.Serialization
public void Initialize()
{
var mappedStringSerializer = new MappedStringSerializer();
IoCManager.RegisterInstance<IRobustMappedStringSerializer>(_mappedStringSerializer);
var types = _reflectionManager.FindTypesWithAttribute<NetSerializableAttribute>().ToList();
#if !FULL_RELEASE
// confirm only shared types are marked for serialization, no client & server only types
@@ -65,14 +68,14 @@ namespace Robust.Shared.Serialization
var settings = new Settings
{
CustomTypeSerializers = new ITypeSerializer[] {mappedStringSerializer}
CustomTypeSerializers = new ITypeSerializer[] {_mappedStringSerializer}
};
_serializer = new Serializer(types, settings);
_serializableTypes = new HashSet<Type>(_serializer.GetTypeMap().Keys);
if (_netManager.IsClient)
{
MappedStringSerializer.LockMappedStrings = true;
_mappedStringSerializer.LockMappedStrings = true;
}
else
{
@@ -80,7 +83,7 @@ namespace Robust.Shared.Serialization
var gameAssemblies = _reflectionManager.Assemblies;
var robustShared = defaultAssemblies
.First(a => a.GetName().Name == "Robust.Shared");
MappedStringSerializer.AddStrings(robustShared);
_mappedStringSerializer.AddStrings(robustShared);
// TODO: Need to add a GetSharedAssemblies method to the reflection manager
@@ -88,7 +91,7 @@ namespace Robust.Shared.Serialization
.FirstOrDefault(a => a.GetName().Name == "Content.Shared");
if (contentShared != null)
{
MappedStringSerializer.AddStrings(contentShared);
_mappedStringSerializer.AddStrings(contentShared);
}
// TODO: Need to add a GetServerAssemblies method to the reflection manager
@@ -97,11 +100,11 @@ namespace Robust.Shared.Serialization
.FirstOrDefault(a => a.GetName().Name == "Content.Server");
if (contentServer != null)
{
MappedStringSerializer.AddStrings(contentServer);
_mappedStringSerializer.AddStrings(contentServer);
}
}
MappedStringSerializer.NetworkInitialize(_netManager);
_mappedStringSerializer.NetworkInitialize(_netManager);
}
public void Serialize(Stream stream, object toSerialize)

View File

@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Text;
using Robust.Shared.IoC;
using Robust.Shared.IoC.Exceptions;
using Robust.Shared.Serialization;
using YamlDotNet.RepresentationModel;
@@ -28,8 +30,6 @@ namespace Robust.Shared.Utility
var document = yamlStream.Documents[0];
_abbreviations = ParseAbbreviations((YamlSequenceNode) document.RootNode);
RobustSerializer.MappedStringSerializer.AddStrings(yamlStream, "(embedded) Robust.Shared.Utility.TypeAbbreviations.yaml");
}
/// <summary>

View File

@@ -1,6 +1,8 @@
using Robust.Client;
using System;
using Robust.Client;
using Robust.Client.Input;
using Robust.Client.Interfaces;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Timing;
namespace Robust.UnitTesting
@@ -19,7 +21,7 @@ namespace Robust.UnitTesting
public bool LoadConfigAndUserData { get; set; } = true;
public bool Startup()
public bool Startup(Func<ILogHandler>? logHandlerFactory = null)
{
return true;
}

View File

@@ -197,8 +197,8 @@ namespace Robust.UnitTesting
_unhandledException = shutDownMessage.UnhandledException;
if (throwOnUnhandled && _unhandledException != null)
{
throw new Exception("Waiting instance shut down with unhandled exception",
_unhandledException);
ExceptionDispatchInfo.Capture(_unhandledException).Throw();
return;
}
break;
@@ -327,7 +327,7 @@ namespace Robust.UnitTesting
IoCManager.RegisterInstance<IStatusHost>(new Mock<IStatusHost>().Object, true);
_options?.InitIoC?.Invoke();
IoCManager.BuildGraph();
ServerProgram.SetupLogging();
//ServerProgram.SetupLogging();
ServerProgram.InitReflectionManager();
var server = DependencyCollection.Resolve<IBaseServerInternal>();
@@ -349,7 +349,7 @@ namespace Robust.UnitTesting
.OverrideConVars(_options.CVarOverrides.Select(p => (p.Key, p.Value)));
}
if (server.Start())
if (server.Start(() => new TestLogHandler("SERVER")))
{
throw new Exception("Server failed to start.");
}
@@ -436,7 +436,7 @@ namespace Robust.UnitTesting
.OverrideConVars(_options.CVarOverrides.Select(p => (p.Key, p.Value)));
};
client.Startup();
client.Startup(() => new TestLogHandler("CLIENT"));
var gameLoop = new IntegrationGameLoop(DependencyCollection.Resolve<IGameTiming>(),
_fromInstanceWriter, _toInstanceReader);

View File

@@ -0,0 +1,43 @@
using Robust.Shared.Interfaces.Log;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using NUnit.Framework;
using Robust.Shared.Log;
using Robust.Shared.Utility;
namespace Robust.UnitTesting
{
public sealed class TestLogHandler : ILogHandler, IDisposable
{
private readonly string _prefix;
private readonly TextWriter _writer;
private readonly Stopwatch _sw = Stopwatch.StartNew();
public TestLogHandler(string prefix)
{
_prefix = prefix;
_writer = TestContext.Out;
_writer.WriteLine($"{_prefix}: Started {DateTime.Now:o}");
}
public void Dispose()
{
_writer.Dispose();
}
public void Log(in LogMessage message)
{
var name = message.LogLevelToName();
var seconds = _sw.ElapsedMilliseconds/1000d;
_writer.WriteLine($"{_prefix}: {seconds:F3}s [{name}] {message.SawmillName}: {message.Message}");
}
}
}