diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index 874de92fc..2dc46b3d7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -1052,6 +1052,7 @@ namespace Barotrauma SoundManager?.Update(); LuaCs.EventService.PublishEvent(sub => sub.OnUpdate(Timing.Step)); + LuaCs.Logger.ProcessLogs(); Timing.Accumulator -= Timing.Step; diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs index 66d8ab350..b438e9af6 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs @@ -365,6 +365,8 @@ namespace Barotrauma CoroutineManager.Update(paused: false, (float)Timing.Step); LuaCs.EventService.PublishEvent(sub => sub.OnUpdate(Timing.Step)); + LuaCs.Logger.ProcessLogs(); + performanceCounterTimer.Stop(); if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LoggerService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LoggerService.cs index 0a590aac6..cbd773c6d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LoggerService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LoggerService.cs @@ -1,9 +1,11 @@ -using System; -using System.Linq; -using Barotrauma.Networking; +using Barotrauma.Networking; using FluentResults; +using HarmonyLib; using Microsoft.Xna.Framework; using MoonSharp.Interpreter; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; namespace Barotrauma.LuaCs; @@ -11,18 +13,118 @@ public partial class LoggerService : ILoggerService { public bool HideUserNames = true; -#if SERVER - private const string LogPrefix = "SV"; - private const int NetMaxLength = 1024; // character limit of vanilla Barotrauma's chat system. - private const int NetMaxMessages = 60; + private List logSubscribers = []; + private ConcurrentQueue logQueue = []; - // This is used so it's possible to call logging functions inside the serverLog - // hook without creating an infinite loop - private bool _lockLog = false; +#if SERVER + private const string TargetPrefix = "[SV]"; + private const int NetMaxLength = 1024; // character limit of vanilla Barotrauma's chat system. + private const int NetMaxMessages = 60; + + // This is used so it's possible to call logging functions inside the serverLog + // hook without creating an infinite loop + private bool _isInsideLogCall = false; #else - private const string LogPrefix = "CL"; + private const string TargetPrefix = "[CL]"; #endif - + + public LoggerService() { } + + public void Subscribe(ILoggerSubscriber subscriber) + { + logSubscribers.Add(subscriber); + } + + public void Unsubscribe(ILoggerSubscriber subscriber) + { + logSubscribers.Remove(subscriber); + } + + public void ProcessLogs() + { + while (logQueue.TryDequeue(out PendingLog log)) + { + logSubscribers.ForEach(s => s.OnLog(log)); + + DebugConsole.NewMessage(log.Message, log.Color); + +#if SERVER + if (GameMain.Server != null) + { + if (GameMain.Server.ServerSettings.SaveServerLogs) + { + string logMessage = "[LuaCs] " + log.Message; + GameMain.Server.ServerSettings.ServerLog.WriteLine(logMessage, log.MessageType, false); + + if (!_isInsideLogCall) + { + _isInsideLogCall = true; + GameMain.LuaCs?.Hook?.Call("serverLog", logMessage, log.MessageType); + _isInsideLogCall = false; + } + } + + for (int i = 0; i < log.Message.Length; i += NetMaxLength) + { + string subStr = log.Message.Substring(i, Math.Min(1024, log.Message.Length - i)); + BroadcastMessage(subStr); + } + } + + void BroadcastMessage(string m) + { + foreach (var client in GameMain.Server.ConnectedClients) + { + ChatMessage consoleMessage = ChatMessage.Create("", m, ChatMessageType.Console, null, textColor: log.Color); + GameMain.Server.SendDirectChatMessage(consoleMessage, client); + + if (!GameMain.Server.ServerSettings.SaveServerLogs || !client.HasPermission(ClientPermissions.ServerLog)) + { + continue; + } + + ChatMessage logMessage = ChatMessage.Create(log.MessageType.ToString(), "[LuaCs] " + m, ChatMessageType.ServerLog, null); + GameMain.Server.SendDirectChatMessage(logMessage, client); + } + } +#endif + } + } + + public void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage) + { + if (HideUserNames && !Environment.UserName.IsNullOrEmpty()) + { + message = message.Replace(Environment.UserName, "USERNAME"); + } + + message = $"{TargetPrefix} {message}"; + + logQueue.Enqueue(new PendingLog(message, color, messageType)); + } + + public void LogError(string message) + { + Log($"{message}", Color.Red, ServerLog.MessageType.Error); + } + + public void LogWarning(string message) + { + Log($"{message}", Color.Yellow, ServerLog.MessageType.ServerMessage); + } + + public void LogMessage(string message, Color? serverColor = null, Color? clientColor = null) + { + serverColor ??= Color.MediumPurple; + clientColor ??= Color.Purple; + +#if SERVER + Log(message, serverColor); +#else + Log(message, clientColor); +#endif + } + public void HandleException(Exception exception, string prefix = null) { string errorString = ""; @@ -54,120 +156,10 @@ public partial class LoggerService : ILoggerService errorString = $"{prefix ?? ""}{s}"; break; } - + LogError(prefix + Environment.UserName + " " + errorString); } - public void LogError(string message) - { - Log($"{message}", Color.Red, ServerLog.MessageType.Error); - } - - public void LogWarning(string message) - { - Log($"{message}", Color.Yellow, ServerLog.MessageType.ServerMessage); - } - - public void LogMessage(string message, Color? serverColor = null, Color? clientColor = null) - { - serverColor ??= Color.MediumPurple; - clientColor ??= Color.Purple; - -#if SERVER - Log(message, serverColor); -#else - Log(message, clientColor); -#endif - } - - public void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage) - { - // TODO: Make this thread Async compatible. - - if (HideUserNames && !Environment.UserName.IsNullOrEmpty()) - { - message = message.Replace(Environment.UserName, "USERNAME"); - } - - DebugConsole.NewMessage(message, color); - -#if SERVER - void BroadcastMessage(string m) - { - foreach (var client in GameMain.Server.ConnectedClients) - { - ChatMessage consoleMessage = ChatMessage.Create("", m, ChatMessageType.Console, null, textColor: color); - GameMain.Server.SendDirectChatMessage(consoleMessage, client); - - if (!GameMain.Server.ServerSettings.SaveServerLogs || !client.HasPermission(ClientPermissions.ServerLog)) - { - continue; - } - - ChatMessage logMessage = ChatMessage.Create(messageType.ToString(), "[LuaCs] " + m, ChatMessageType.ServerLog, null); - GameMain.Server.SendDirectChatMessage(logMessage, client); - } - } - - if (GameMain.Server != null) - { - if (GameMain.Server.ServerSettings.SaveServerLogs) - { - string logMessage = "[LuaCs] " + message; - GameMain.Server.ServerSettings.ServerLog.WriteLine(logMessage, messageType, false); - - if (!_lockLog) - { - _lockLog = true; - GameMain.LuaCs?.Hook?.Call("serverLog", logMessage, messageType); - _lockLog = false; - } - } - - for (int i = 0; i < message.Length; i += NetMaxLength) - { - string subStr = message.Substring(i, Math.Min(1024, message.Length - i)); - BroadcastMessage(subStr); - } - } -#endif - } - - public void HandleException(Exception ex) - { - string errorString = ""; - switch (ex) - { - case NetRuntimeException netRuntimeException: - if (netRuntimeException.DecoratedMessage == null) - { - errorString = netRuntimeException.ToString(); - } - else - { - // FIXME: netRuntimeException.ToString() doesn't print the InnerException's stack trace... - errorString = $"{netRuntimeException.DecoratedMessage}: {netRuntimeException}"; - } - break; - case InterpreterException interpreterException: - if (interpreterException.DecoratedMessage == null) - { - errorString = interpreterException.ToString(); - } - else - { - errorString = interpreterException.DecoratedMessage; - } - break; - default: - errorString = ex.StackTrace != null - ? ex.ToString() - : $"{ex}\n{Environment.StackTrace}"; - break; - } - - LogError(errorString); - } public void LogResults(FluentResults.Result result) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/ILoggerService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/ILoggerService.cs index c4c110379..c5ac48768 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/ILoggerService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/ILoggerService.cs @@ -5,11 +5,21 @@ using Microsoft.Xna.Framework; namespace Barotrauma.LuaCs; +public readonly record struct PendingLog(string Message, Color? Color, ServerLog.MessageType MessageType); + +public interface ILoggerSubscriber +{ + void OnLog(PendingLog pendingLog); +} + /// /// Provides console and debug logging services /// public interface ILoggerService : IReusableService { + void Subscribe(ILoggerSubscriber subscriber); + void Unsubscribe(ILoggerSubscriber subscriber); + void ProcessLogs(); void HandleException(Exception exception, string prefix = null); void LogError(string message); void LogWarning(string message);