diff --git a/Winch.sln b/Winch.sln index 5696d732..4ceb150f 100644 --- a/Winch.sln +++ b/Winch.sln @@ -11,6 +11,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisasterButton", "Winch.Exa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleItems", "Winch.Examples\ExampleItems\ExampleItems.csproj", "{C112288E-E993-44F3-B7B0-FB4BD37F18EA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinchConsole", "WinchConsole\WinchConsole.csproj", "{D5CFABA9-FA58-474F-A394-E197BDF38FDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinchCommon", "WinchCommon\WinchCommon.csproj", "{EF5F69E6-FD4D-4314-B187-A89D00BF9355}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +37,14 @@ Global {C112288E-E993-44F3-B7B0-FB4BD37F18EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {C112288E-E993-44F3-B7B0-FB4BD37F18EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {C112288E-E993-44F3-B7B0-FB4BD37F18EA}.Release|Any CPU.Build.0 = Release|Any CPU + {D5CFABA9-FA58-474F-A394-E197BDF38FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5CFABA9-FA58-474F-A394-E197BDF38FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5CFABA9-FA58-474F-A394-E197BDF38FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5CFABA9-FA58-474F-A394-E197BDF38FDD}.Release|Any CPU.Build.0 = Release|Any CPU + {EF5F69E6-FD4D-4314-B187-A89D00BF9355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF5F69E6-FD4D-4314-B187-A89D00BF9355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF5F69E6-FD4D-4314-B187-A89D00BF9355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF5F69E6-FD4D-4314-B187-A89D00BF9355}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Winch/Core/WinchCore.cs b/Winch/Core/WinchCore.cs index 6ba1d549..bd069c1c 100644 --- a/Winch/Core/WinchCore.cs +++ b/Winch/Core/WinchCore.cs @@ -2,17 +2,15 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection; -using System.Text.RegularExpressions; -using UnityEngine.Profiling.Memory.Experimental; -using Winch.Config; using Winch.Logging; using Winch.Util; namespace Winch.Core { - public class WinchCore + public class WinchCore { public static Logger Log = new Logger(); @@ -22,6 +20,18 @@ public class WinchCore public static void Main() { + Log.Info("Starting console"); + try + { + Process.Start(Path.Combine(WinchInstallLocation, "WinchConsole.exe")); + } + catch (Exception e) + { + Log.Error($"Could not start console : {e}"); + } + + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); + try { string metaPath = Path.Combine(WinchInstallLocation, "mod_meta.json"); @@ -67,5 +77,10 @@ public static void Main() Log.Debug("Harmony Patching complete."); } + + public static void OnProcessExit(object sender, EventArgs e) + { + Log.Debug("DREDGE application exited"); + } } } diff --git a/Winch/Logging/LogSocket.cs b/Winch/Logging/LogSocket.cs index 5cfdbc1c..33f68d53 100644 --- a/Winch/Logging/LogSocket.cs +++ b/Winch/Logging/LogSocket.cs @@ -12,7 +12,6 @@ public class LogSocket private Socket _socket; private readonly int _port; private readonly Logger _logger; - private const string IP = "127.0.0.1"; public LogSocket(Logger logger, int port) { @@ -30,14 +29,15 @@ public void WriteToSocket(LogMessage logMessage) if (!_socket.Connected) { - var endPoint = new IPEndPoint(IPAddress.Parse(IP), _port); try + var endPoint = new IPEndPoint(IPAddress.Parse(Constants.IP), _port); + try { _socket?.Connect(endPoint); } catch (Exception ex) { _socket = null; - _logger.Error($"Could not connect to console at {IP}:{_port} - {ex}"); + _logger.Error($"Could not connect to console at {Constants.IP}:{_port} - {ex}"); } } diff --git a/Winch/Logging/Logger.cs b/Winch/Logging/Logger.cs index 5d5ce6a4..30713d9d 100644 --- a/Winch/Logging/Logger.cs +++ b/Winch/Logging/Logger.cs @@ -7,12 +7,7 @@ namespace Winch.Logging { - enum LogLevel - { - UNITY = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4 - } - - public class Logger + public class Logger { private LogFile? _log; private LogFile? _latestLog; @@ -73,7 +68,7 @@ private void Log(LogLevel level, string message, string source) { _logSocket?.WriteToSocket(new LogMessage() { - Level = level.ToString(), + Level = level, Message = message, Source = source }); diff --git a/Winch/Winch.csproj b/Winch/Winch.csproj index f6c5d406..c9749d35 100644 --- a/Winch/Winch.csproj +++ b/Winch/Winch.csproj @@ -16,21 +16,6 @@ - - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - PreserveNewest @@ -49,4 +34,9 @@ + + + + + diff --git a/Winch/mod_meta.json b/Winch/mod_meta.json index 3366cd4e..7cd7f35c 100644 --- a/Winch/mod_meta.json +++ b/Winch/mod_meta.json @@ -2,5 +2,5 @@ "Name": "Winch", "Author": "Hacktix", "ModGUID": "hacktix.winch", - "Version": "0.3.0" + "Version": "0.4.0" } diff --git a/Winch/Config/DefaultConfig.json b/WinchCommon/Config/DefaultConfig.json similarity index 100% rename from Winch/Config/DefaultConfig.json rename to WinchCommon/Config/DefaultConfig.json diff --git a/Winch/Config/JSONConfig.cs b/WinchCommon/Config/JSONConfig.cs similarity index 100% rename from Winch/Config/JSONConfig.cs rename to WinchCommon/Config/JSONConfig.cs diff --git a/Winch/Config/ModConfig.cs b/WinchCommon/Config/ModConfig.cs similarity index 85% rename from Winch/Config/ModConfig.cs rename to WinchCommon/Config/ModConfig.cs index 3e28557f..5b05ef20 100644 --- a/Winch/Config/ModConfig.cs +++ b/WinchCommon/Config/ModConfig.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using Winch.Core; +using System.Reflection; namespace Winch.Config { - public class ModConfig : JSONConfig + public class ModConfig : JSONConfig { private static Dictionary DefaultConfigs = new Dictionary(); private static Dictionary Instances = new Dictionary(); @@ -18,7 +15,7 @@ private static string GetDefaultConfig(string modName) { if(!DefaultConfigs.ContainsKey(modName)) { - WinchCore.Log.Error($"No 'DefaultConfig' attribute found in mod_meta.json for {modName}!"); + //WinchCore.Log.Error($"No 'DefaultConfig' attribute found in mod_meta.json for {modName}!"); throw new KeyNotFoundException($"No 'DefaultConfig' attribute found in mod_meta.json for {modName}!"); } return DefaultConfigs[modName]; diff --git a/Winch/Config/WinchConfig.cs b/WinchCommon/Config/WinchConfig.cs similarity index 64% rename from Winch/Config/WinchConfig.cs rename to WinchCommon/Config/WinchConfig.cs index 912cdc9f..ef16ddc0 100644 --- a/Winch/Config/WinchConfig.cs +++ b/WinchCommon/Config/WinchConfig.cs @@ -7,7 +7,7 @@ public class WinchConfig : JSONConfig { private static readonly string WinchConfigPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "WinchConfig.json"); - private WinchConfig() : base(WinchConfigPath, Properties.Resources.DefaultConfig) { } + private WinchConfig() : base(WinchConfigPath, WinchCommon.Properties.Resources.DefaultConfig) { } private static WinchConfig? _instance; public static WinchConfig Instance @@ -22,7 +22,19 @@ public static WinchConfig Instance public static new T? GetProperty(string key, T defaultValue) { - return ((JSONConfig)Instance).GetProperty(key, defaultValue); + try + { + return ((JSONConfig)Instance).GetProperty(key, defaultValue); + } + catch + { + return defaultValue; + } } + + public static new void SetProperty(string key, T value) + { + ((JSONConfig)Instance).SetProperty(key, value); + } } } diff --git a/WinchCommon/Constants.cs b/WinchCommon/Constants.cs new file mode 100644 index 00000000..61f7263d --- /dev/null +++ b/WinchCommon/Constants.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Winch; + +public static class Constants +{ + public const string IP = "127.0.0.1"; +} diff --git a/WinchCommon/LogLevel.cs b/WinchCommon/LogLevel.cs new file mode 100644 index 00000000..ac20e5f8 --- /dev/null +++ b/WinchCommon/LogLevel.cs @@ -0,0 +1,6 @@ +namespace Winch; + +public enum LogLevel +{ + UNITY = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4 +} diff --git a/Winch/Logging/LogMessage.cs b/WinchCommon/Logging/LogMessage.cs similarity index 81% rename from Winch/Logging/LogMessage.cs rename to WinchCommon/Logging/LogMessage.cs index 4e3909ca..42098df3 100644 --- a/Winch/Logging/LogMessage.cs +++ b/WinchCommon/Logging/LogMessage.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using WinchCommon; namespace Winch.Logging { @@ -8,7 +9,7 @@ public class LogMessage public string Source { get; set; } [JsonProperty("level")] - public string Level { get; set; } + public LogLevel Level { get; set; } [JsonProperty("message")] public string Message { get; set; } diff --git a/Winch/Properties/Resources.Designer.cs b/WinchCommon/Properties/Resources.Designer.cs similarity index 92% rename from Winch/Properties/Resources.Designer.cs rename to WinchCommon/Properties/Resources.Designer.cs index 0349a657..e3f866b0 100644 --- a/Winch/Properties/Resources.Designer.cs +++ b/WinchCommon/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Winch.Properties { +namespace WinchCommon.Properties { using System; @@ -39,7 +39,7 @@ internal Resources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Winch.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinchCommon.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -66,7 +66,8 @@ internal Resources() { /// "LogLevel": "DEBUG", /// "LogsFolder": "Logs", /// "DetailedLogSources": false, - /// "EnableDeveloperConsole": true + /// "EnableDeveloperConsole": true, + /// "MaxLogFiles": 10 ///}. /// internal static string DefaultConfig { diff --git a/Winch/Properties/Resources.resx b/WinchCommon/Properties/Resources.resx similarity index 100% rename from Winch/Properties/Resources.resx rename to WinchCommon/Properties/Resources.resx diff --git a/WinchCommon/WinchCommon.csproj b/WinchCommon/WinchCommon.csproj new file mode 100644 index 00000000..0feb137a --- /dev/null +++ b/WinchCommon/WinchCommon.csproj @@ -0,0 +1,33 @@ + + + + net48 + enable + enable + 11 + + + + + + + + + + + + + Resources.resx + True + True + + + + + + Resources.Designer.cs + ResXFileCodeGenerator + + + + diff --git a/WinchConsole/LogSocketListener.cs b/WinchConsole/LogSocketListener.cs new file mode 100644 index 00000000..cd8495b9 --- /dev/null +++ b/WinchConsole/LogSocketListener.cs @@ -0,0 +1,154 @@ +using Newtonsoft.Json; +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using Winch.Config; +using Winch.Logging; + +namespace Winch +{ + public class LogSocketListener + { + private const string Separator = "\n--------------------------------"; + private const int BufferSize = 262144; + private static int _port; + private static TcpListener _server; + private bool _hasReceivedFatalMessage; + + public LogSocketListener() + { + + } + + public void Init() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + _port = ((IPEndPoint)listener.LocalEndpoint).Port; + WriteByType(LogLevel.INFO, $"Found available port {_port}"); + listener.Stop(); + + WinchConfig.SetProperty("LogPort", $"{_port}"); + + SetupSocketListener(); + } + + private void SetupSocketListener() + { + WriteByType(LogLevel.INFO, $"Setting up socket listener {_port}"); + try + { + ListenToSocket(); + } + catch (SocketException ex) + { + WriteByType(LogLevel.ERROR, $"Error in socket listener: {ex.SocketErrorCode} - {ex}"); + } + catch (Exception ex) + { + WriteByType(LogLevel.ERROR, $"Error while listening: {ex}"); + } + finally + { + _server?.Stop(); + } + } + + private void ListenToSocket() + { + var localAddress = IPAddress.Parse(Constants.IP); + + _server = new TcpListener(localAddress, _port); + _server.Start(); + WriteByType(LogLevel.INFO, $"Connected to local {localAddress}:{_port}"); + + var bytes = new byte[BufferSize]; + + while (true) + { + var client = _server.AcceptTcpClient(); + + WriteByType(LogLevel.INFO, "Console connected to socket!"); + + var stream = client.GetStream(); + + int i; + + while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) + { + ProcessMessage(bytes, i); + } + + WriteByType(LogLevel.INFO, "Closing client!"); + client.Close(); + } + } + + private void ProcessMessage(byte[] bytes, int count) + { + var message = Encoding.UTF8.GetString(bytes, 0, count); + + ProcessJson(message); + + // OWML would split messages into multiple jsons but that bugs out here + // when we get a message that has a json object in it or just new lines generally + // weird + + /* + var jsons = message.Split('\n'); + + foreach (var json in jsons) + { + if (string.IsNullOrWhiteSpace(json)) + { + return; + } + + ProcessJson(json); + } + */ + } + + private void ProcessJson(string json) + { + LogMessage data; + try + { + data = JsonConvert.DeserializeObject(json) ?? throw new NullReferenceException(); + } + catch (Exception ex) + { + WriteByType(LogLevel.WARN, $"Failed to process following message:{Separator}\n{json}{Separator}"); + WriteByType(LogLevel.WARN, $"Reason: {ex}"); + return; + } + + var nameTypePrefix = $"[{data.Source}] : "; + + var messageData = data.Message; + messageData = messageData.Replace("\n", $"\n{new string(' ', nameTypePrefix.Length)}"); + + WriteByType(data.Level, $"{nameTypePrefix}{messageData}"); + } + + public static void WriteByType(LogLevel type, string line) + { + if (string.IsNullOrEmpty(line)) + { + return; + } + + Console.ForegroundColor = type switch + { + LogLevel.ERROR => ConsoleColor.Red, + LogLevel.WARN => ConsoleColor.Yellow, + LogLevel.DEBUG => ConsoleColor.DarkGray, + _ => ConsoleColor.White + }; + + Console.WriteLine(line); + } + } +} diff --git a/WinchConsole/Program.cs b/WinchConsole/Program.cs new file mode 100644 index 00000000..24f4ca74 --- /dev/null +++ b/WinchConsole/Program.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics; +using System.Linq; +using Winch; + +namespace WinchConsole +{ + internal class Program + { + static void Main(string[] args) + { + Console.WriteLine("Loading Winch console!"); + + // Only allow one console to be open at a time + var currentProcess = Process.GetCurrentProcess(); + var duplicates = Process.GetProcessesByName(currentProcess.ProcessName); + + // Let the existing console handle logs + // For example, loading with the manager loads the game then it gets relaunched via Steam opening two consoles + // However only the first console shows text + if (duplicates.Length > 1) + { + currentProcess.Kill(); + } + else + { + new LogSocketListener().Init(); + } + } + } +} \ No newline at end of file diff --git a/WinchConsole/WinchConsole.csproj b/WinchConsole/WinchConsole.csproj new file mode 100644 index 00000000..6e9f9d82 --- /dev/null +++ b/WinchConsole/WinchConsole.csproj @@ -0,0 +1,24 @@ + + + + Exe + net48 + 11 + + + + + + + + + False + False + + + + + + + +