mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Shared String Dictionary ctd. (#1126)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// | <-------------- Hash |
|
||||
/// | Need Strings ------> |
|
||||
/// | <----------- Strings |
|
||||
/// | Dont Need Strings -> |
|
||||
/// </code>
|
||||
///
|
||||
/// Cached flow: <code>
|
||||
/// Client | Server
|
||||
/// | <-------------- Hash |
|
||||
/// | Dont Need Strings -> |
|
||||
/// </code>
|
||||
///
|
||||
/// Verification failure flow: <code>
|
||||
/// Client | Server
|
||||
/// | <-------------- Hash |
|
||||
/// | Need Strings ------> |
|
||||
/// | <----------- Strings |
|
||||
/// + Hash Failed |
|
||||
/// | Need Strings ------> |
|
||||
/// | <----------- Strings |
|
||||
/// | Dont Need Strings -> |
|
||||
/// </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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1319
Robust.Shared/Serialization/RobustMappedStringSerializer.cs
Normal file
1319
Robust.Shared/Serialization/RobustMappedStringSerializer.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
43
Robust.UnitTesting/TestLogHandler.cs
Normal file
43
Robust.UnitTesting/TestLogHandler.cs
Normal 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}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user