diff --git a/OpenCS2hock.sln.DotSettings.user b/OpenCS2hock.sln.DotSettings.user index 26eddfc..2aaabe6 100644 --- a/OpenCS2hock.sln.DotSettings.user +++ b/OpenCS2hock.sln.DotSettings.user @@ -1,3 +1,3 @@  - True + True \ No newline at end of file diff --git a/OpenCS2hock/CS2MessageHandler.cs b/OpenCS2hock/CS2MessageHandler.cs deleted file mode 100644 index 08f8421..0000000 --- a/OpenCS2hock/CS2MessageHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace OpenCS2hock; - -internal class CS2MessageHandler -{ - internal delegate void CS2EventHandler(); - internal event CS2EventHandler? OnKill; - internal event CS2EventHandler? OnDeath; - internal event CS2EventHandler? OnRoundStart; - internal event CS2EventHandler? OnRoundEnd; - internal event CS2EventHandler? OnRoundWin; - internal event CS2EventHandler? OnRoundLoss; - - internal void HandleCS2Message(string message, string mySteamId) - { - JObject messageJson = JObject.Parse(message); - string? previousSteamId = messageJson.SelectToken("previously.player.steamid", false)?.Value(); - string? currentSteamId = messageJson.SelectToken("player.steamid", false)?.Value(); - - RoundState currentRoundState = ParseRoundStateFromString(messageJson.SelectToken("round.phase", false)?.Value()); - RoundState previousRoundState = ParseRoundStateFromString(messageJson.SelectToken("previously.round.phase", false)?.Value()); - if(previousRoundState == RoundState.Over && currentRoundState == RoundState.Live) - OnRoundStart?.Invoke(); - if(previousRoundState == RoundState.Live && currentRoundState == RoundState.FreezeTime) - OnRoundEnd?.Invoke(); - if(previousRoundState == RoundState.Live && currentRoundState == RoundState.Over) - OnRoundEnd?.Invoke(); - - Team playerTeam = ParseTeamFromString(messageJson.SelectToken("player.team", false)?.Value()); - Team winnerTeam = ParseTeamFromString(messageJson.SelectToken("round.win_team", false)?.Value()); - if(winnerTeam != Team.None && playerTeam != Team.None && playerTeam == winnerTeam) - OnRoundWin?.Invoke(); - else if(winnerTeam != Team.None && playerTeam != Team.None && playerTeam != winnerTeam) - OnRoundLoss?.Invoke(); - - int? previousDeaths = messageJson.SelectToken("previously.player.match_stats.deaths", false)?.Value(); - int? currentDeaths = messageJson.SelectToken("player.match_stats.deaths", false)?.Value(); - if(previousSteamId is null && currentSteamId == mySteamId && currentDeaths > previousDeaths) - OnDeath?.Invoke(); - - int? previousKills = messageJson.SelectToken("previously.player.match_stats.kills", false)?.Value(); - int? currentKills = messageJson.SelectToken("player.match_stats.kills", false)?.Value(); - if(previousSteamId is null && currentSteamId == mySteamId && currentKills > previousKills) - OnKill?.Invoke(); - } - - private RoundState ParseRoundStateFromString(string? str) - { - return str switch - { - "live" => RoundState.Live, - "freezetime" => RoundState.FreezeTime, - "over" => RoundState.Over, - _ => RoundState.Unknown - }; - } - - private Team ParseTeamFromString(string? str) - { - return str switch - { - "T" => Team.T, - "CT" => Team.CT, - _ => Team.None - }; - } - - private enum RoundState {FreezeTime, Live, Over, Unknown} - - private enum Team {T, CT, None} -} \ No newline at end of file diff --git a/OpenCS2hock/GSIServer.cs b/OpenCS2hock/GSIServer.cs deleted file mode 100644 index 11f30d1..0000000 --- a/OpenCS2hock/GSIServer.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Net; -using System.Text; - -namespace OpenCS2hock; - -internal class GSIServer -{ - private HttpListener HttpListener { get; init; } - internal delegate void OnMessageEventHandler(string content); - internal event OnMessageEventHandler? OnMessage; - - private bool _keepRunning = true; - internal bool IsRunning { get; private set; } - - internal GSIServer(int port) - { - HttpListener = new HttpListener(); - HttpListener.Prefixes.Add($"http://127.0.0.1:{port}/"); - HttpListener.Start(); - - Thread connectionListener = new (HandleConnection); - connectionListener.Start(); - - IsRunning = true; - } - - private async void HandleConnection() - { - while (_keepRunning) - { - HttpListenerContext context = await HttpListener.GetContextAsync(); - HttpListenerRequest request = context.Request; - - Console.WriteLine($"[{request.HttpMethod}] {request.Url} - {request.UserAgent}"); - - HttpResponseMessage responseMessage = new (HttpStatusCode.Accepted); - context.Response.OutputStream.Write(Encoding.UTF8.GetBytes(responseMessage.ToString())); - - StreamReader reader = new (request.InputStream, request.ContentEncoding); - string content = await reader.ReadToEndAsync(); - OnMessage?.Invoke(content); - } - HttpListener.Close(); - IsRunning = false; - } - - internal void Dispose() - { - _keepRunning = false; - } -} \ No newline at end of file diff --git a/OpenCS2hock/Installer.cs b/OpenCS2hock/Installer.cs index 0757ffa..c272ee4 100644 --- a/OpenCS2hock/Installer.cs +++ b/OpenCS2hock/Installer.cs @@ -14,46 +14,14 @@ public static class Installer return JsonConvert.DeserializeObject(File.ReadAllText(settingsFilePath)); } - internal static List GetShockers(Settings settings) + internal static List GetShockers(Settings settings, Logger? logger = null) { List shockers = new(); shockers.Add(new OpenShock(settings.OpenShockSettings.Endpoint, settings.OpenShockSettings.ApiKey, settings.OpenShockSettings.Shockers, new ConfiguredInteger(settings.IntensityRange.Min, settings.IntensityRange.Max), - new ConfiguredInteger(settings.DurationRange.Min, settings.DurationRange.Max))); + new ConfiguredInteger(settings.DurationRange.Min, settings.DurationRange.Max), + logger)); return shockers; } - - internal static void InstallGsi() - { - string installLocation = Path.Combine(GetInstallDirectory(), "game\\csgo\\cfg\\gamestate_integration_opencs2hock.cfg"); - File.WriteAllText(installLocation, Resources.GSI_CFG_Content); - } - - private static string GetInstallDirectory(int appId = 730) - { - string steamInstallation = -#pragma warning disable CA1416 //Registry only available on Windows - (string)(Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\Valve\Steam", "SteamPath", null) ?? -#pragma warning restore CA1416 - throw new DirectoryNotFoundException("No Steam Installation found.")); - string libraryFolderFilepath = Path.Combine(steamInstallation, "steamapps\\libraryfolders.vdf"); - string? libraryPath = null; - string? appManifestFolderPath = null; - foreach (string line in File.ReadAllLines(libraryFolderFilepath)) - if (line.Contains("path")) - libraryPath = line.Split("\"").Last(split => split.Length > 0); - else if (line.Contains($"\"{appId}\"")) - appManifestFolderPath = Path.Combine(libraryPath!, $"steamapps\\appmanifest_{appId}.acf"); - - string installationPath = ""; - if (appManifestFolderPath is null) - throw new DirectoryNotFoundException($"No {appId} Installation found."); - foreach(string line in File.ReadAllLines(appManifestFolderPath)) - if (line.Contains("installdir")) - installationPath = Path.Combine(libraryPath!, "steamapps\\common", line.Split("\"").Last(split => split.Length > 0)); - - return installationPath; - } - } \ No newline at end of file diff --git a/OpenCS2hock/Logger.cs b/OpenCS2hock/Logger.cs new file mode 100644 index 0000000..8a8c0da --- /dev/null +++ b/OpenCS2hock/Logger.cs @@ -0,0 +1,65 @@ +using Microsoft.Extensions.Logging; + +namespace OpenCS2hock; + +public class Logger : ILogger +{ + private LogLevel _enabledLoglevel; + private readonly ConsoleColor _defaultForegroundColor = Console.ForegroundColor; + private readonly ConsoleColor _defaultBackgroundColor = Console.BackgroundColor; + + public Logger(LogLevel logLevel = LogLevel.Trace) + { + _enabledLoglevel = logLevel; + } + + public void UpdateLogLevel(LogLevel logLevel) + { + this._enabledLoglevel = logLevel; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) + return; + Console.ForegroundColor = ForegroundColorForLogLevel(logLevel); + Console.BackgroundColor = BackgroundColorForLogLevel(logLevel); + Console.Write(logLevel.ToString()[..3].ToUpper()); + Console.ResetColor(); + // ReSharper disable once LocalizableElement + Console.Write($" [{DateTime.UtcNow:HH:mm:ss.fff}] "); + Console.WriteLine(formatter.Invoke(state, exception)); + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel >= _enabledLoglevel; + } + + public IDisposable? BeginScope(TState state) where TState : notnull + { + return null; + } + + private ConsoleColor ForegroundColorForLogLevel(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Error or LogLevel.Critical => ConsoleColor.Black, + LogLevel.Debug => ConsoleColor.Black, + LogLevel.Information => ConsoleColor.White, + _ => _defaultForegroundColor + }; + } + + private ConsoleColor BackgroundColorForLogLevel(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Error or LogLevel.Critical => ConsoleColor.Red, + LogLevel.Debug => ConsoleColor.Yellow, + LogLevel.Information => ConsoleColor.Black, + _ => _defaultBackgroundColor + }; + } +} \ No newline at end of file diff --git a/OpenCS2hock/OpenCS2hock.cs b/OpenCS2hock/OpenCS2hock.cs index 924f9d9..e29b1ee 100644 --- a/OpenCS2hock/OpenCS2hock.cs +++ b/OpenCS2hock/OpenCS2hock.cs @@ -1,35 +1,34 @@ -namespace OpenCS2hock; +using Microsoft.Extensions.Logging; + +namespace OpenCS2hock; public class OpenCS2hock { - private GSIServer GSIServer { get; init; } - private readonly CS2MessageHandler _cs2MessageHandler; + private readonly CS2GSI.CS2GSI _cs2GSI; private readonly List _shockers; private readonly Settings _settings; + private readonly Logger? _logger; - public OpenCS2hock(string? settingsPath = null) + public OpenCS2hock(string? settingsPath = null, Logger? logger = null) { + this._logger = logger; + this._logger?.Log(LogLevel.Information, "Starting OpenCS2hock..."); + this._logger?.Log(LogLevel.Information, "Loading Settings..."); _settings = Installer.GetSettings(settingsPath); - this._shockers = Installer.GetShockers(_settings); - Console.WriteLine(_settings); - Installer.InstallGsi(); - - this._cs2MessageHandler = new CS2MessageHandler(); + this._logger?.Log(LogLevel.Information, $"Loglevel set to {_settings.LogLevel}"); + this._logger?.UpdateLogLevel(_settings.LogLevel); + this._logger?.Log(LogLevel.Information, _settings.ToString()); + this._logger?.Log(LogLevel.Information, "Setting up Shockers..."); + this._shockers = Installer.GetShockers(_settings, logger); + this._cs2GSI = new CS2GSI.CS2GSI(_logger); this.SetupEventHandlers(); - - this.GSIServer = new GSIServer(3000); - this.GSIServer.OnMessage += OnGSIMessage; - - Thread runningThread = new(() => - { - while (GSIServer.IsRunning) - Thread.Sleep(10); - }); - runningThread.Start(); + while(this._cs2GSI.IsRunning) + Thread.Sleep(10); } private void SetupEventHandlers() { + this._logger?.Log(LogLevel.Information, "Setting up EventHandlers..."); foreach (Shocker shocker in this._shockers) { foreach (KeyValuePair kv in _settings.Actions) @@ -37,33 +36,25 @@ public class OpenCS2hock switch (kv.Key) { case "OnKill": - this._cs2MessageHandler.OnKill += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnKill += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; case "OnDeath": - this._cs2MessageHandler.OnDeath += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnDeath += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; case "OnRoundStart": - this._cs2MessageHandler.OnRoundStart += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnRoundStart += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; case "OnRoundEnd": - this._cs2MessageHandler.OnRoundEnd += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnRoundOver += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; case "OnRoundLoss": - this._cs2MessageHandler.OnRoundLoss += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnRoundLoss += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; case "OnRoundWin": - this._cs2MessageHandler.OnRoundWin += () => shocker.Control(Settings.StringToAction(kv.Value)); + this._cs2GSI.OnRoundWin += (cs2EventArgs) => shocker.Control(Settings.StringToAction(kv.Value)); break; } } } } - - private void OnGSIMessage(string content) - { - Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, "CS2Events")); - string fileName = Path.Combine(Environment.CurrentDirectory, "CS2Events" ,$"{DateTime.Now.ToLongTimeString().Replace(':','.')}.json"); - File.WriteAllText(fileName, content); - _cs2MessageHandler.HandleCS2Message(content, _settings.SteamId); - } } \ No newline at end of file diff --git a/OpenCS2hock/OpenCS2hock.csproj b/OpenCS2hock/OpenCS2hock.csproj index 885795a..1f1adf2 100644 --- a/OpenCS2hock/OpenCS2hock.csproj +++ b/OpenCS2hock/OpenCS2hock.csproj @@ -9,21 +9,7 @@ - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - True - True - Resources.resx - - - - + diff --git a/OpenCS2hock/OpenShock.cs b/OpenCS2hock/OpenShock.cs index dd2dee2..7ef0945 100644 --- a/OpenCS2hock/OpenShock.cs +++ b/OpenCS2hock/OpenShock.cs @@ -1,5 +1,6 @@ using System.Net.Http.Headers; using System.Text; +using Microsoft.Extensions.Logging; namespace OpenCS2hock; @@ -23,7 +24,7 @@ internal class OpenShock : Shocker }; request.Headers.Add("OpenShockToken", ApiKey); HttpResponseMessage response = this.HttpClient.Send(request); - Console.WriteLine($"{request.RequestUri} response: {response.StatusCode}"); + this.Logger?.Log(LogLevel.Debug, $"{request.RequestUri} response: {response.StatusCode}"); } private byte ControlActionToByte(ControlAction action) @@ -37,7 +38,7 @@ internal class OpenShock : Shocker }; } - internal OpenShock(string endpoint, string apiKey, string[] shockerIds, ConfiguredInteger intensity, ConfiguredInteger duration) : base(endpoint, apiKey, shockerIds, intensity, duration) + internal OpenShock(string endpoint, string apiKey, string[] shockerIds, ConfiguredInteger intensity, ConfiguredInteger duration, Logger? logger = null) : base(endpoint, apiKey, shockerIds, intensity, duration, logger) { } diff --git a/OpenCS2hock/Program.cs b/OpenCS2hock/Program.cs index 5ab0d37..0bbee14 100644 --- a/OpenCS2hock/Program.cs +++ b/OpenCS2hock/Program.cs @@ -1,9 +1,11 @@ -namespace OpenCS2hock; +using Microsoft.Extensions.Logging; + +namespace OpenCS2hock; public class Program { public static void Main(string[] args) { - OpenCS2hock openCS2Hock = new OpenCS2hock(); + OpenCS2hock openCS2Hock = new OpenCS2hock(logger: new Logger(LogLevel.Information)); } } \ No newline at end of file diff --git a/OpenCS2hock/Resources.Designer.cs b/OpenCS2hock/Resources.Designer.cs deleted file mode 100644 index fe02fff..0000000 --- a/OpenCS2hock/Resources.Designer.cs +++ /dev/null @@ -1,88 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace OpenCS2hock { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenCS2Hock.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to "OpenCS2hock" - ///{ - ///"uri" "http://127.0.0.1:3000" - ///"timeout" "5.0" - ///"buffer" "0.1" - ///"throttle" "0.5" - ///"heartbeat" "60.0" - ///"output" - /// { - /// "precision_time" "3" - /// "precision_position" "1" - /// "precision_vector" "3" - /// } - ///"data" - /// { - /// "provider" "1" // general info about client being listened to: game name, appid, client steamid, etc. - /// "map" "1" // map, gamemode, and current match phase ('warmup', 'intermission', 'gameover', 'live') and current score - /// [rest of string was truncated]";. - /// - internal static string GSI_CFG_Content { - get { - return ResourceManager.GetString("GSI_CFG_Content", resourceCulture); - } - } - } -} diff --git a/OpenCS2hock/Resources.resx b/OpenCS2hock/Resources.resx deleted file mode 100644 index d80b24a..0000000 --- a/OpenCS2hock/Resources.resx +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - gamestate_integration_opencs2hock.cfg;System.String, mscorlib, Version=4.0.0.0, Culture=neutral - - \ No newline at end of file diff --git a/OpenCS2hock/Settings.cs b/OpenCS2hock/Settings.cs index 2d63653..26f57bd 100644 --- a/OpenCS2hock/Settings.cs +++ b/OpenCS2hock/Settings.cs @@ -1,10 +1,11 @@ -using Newtonsoft.Json; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace OpenCS2hock; public struct Settings { - public string SteamId = ""; + public LogLevel LogLevel = LogLevel.Information; public OpenShockSettings OpenShockSettings = new() { Endpoint = "https://api.shocklink.net", diff --git a/OpenCS2hock/Shocker.cs b/OpenCS2hock/Shocker.cs index ef09f15..a999ba4 100644 --- a/OpenCS2hock/Shocker.cs +++ b/OpenCS2hock/Shocker.cs @@ -1,4 +1,6 @@ -namespace OpenCS2hock; +using Microsoft.Extensions.Logging; + +namespace OpenCS2hock; internal abstract class Shocker { @@ -6,6 +8,7 @@ internal abstract class Shocker protected readonly string ApiKey,Endpoint; private readonly string[] _shockerIds; private readonly ConfiguredInteger _intensity, _duration; + protected readonly Logger? Logger; internal enum ControlAction { Beep, Vibrate, Shock, Nothing } @@ -13,7 +16,7 @@ internal abstract class Shocker { int intensity = _intensity.GetValue(); int duration = _duration.GetValue(); - Console.WriteLine($"{action} {intensity} {duration}"); + this.Logger?.Log(LogLevel.Information, $"{action} {intensity} {duration}"); if (action is ControlAction.Nothing) return; if(shockerId is null) @@ -25,7 +28,7 @@ internal abstract class Shocker protected abstract void ControlInternal(ControlAction action, string shockerId, int intensity, int duration); - protected Shocker(string endpoint, string apiKey, string[] shockerIds, ConfiguredInteger intensity, ConfiguredInteger duration) + protected Shocker(string endpoint, string apiKey, string[] shockerIds, ConfiguredInteger intensity, ConfiguredInteger duration, Logger? logger = null) { this.Endpoint = endpoint; this.ApiKey = apiKey; @@ -33,5 +36,6 @@ internal abstract class Shocker this._shockerIds = shockerIds; this._intensity = intensity; this._duration = duration; + this.Logger = logger; } } \ No newline at end of file diff --git a/OpenCS2hock/gamestate_integration_opencs2hock.cfg b/OpenCS2hock/gamestate_integration_opencs2hock.cfg deleted file mode 100644 index c131ebe..0000000 --- a/OpenCS2hock/gamestate_integration_opencs2hock.cfg +++ /dev/null @@ -1,23 +0,0 @@ -"OpenCS2hock" -{ -"uri" "http://127.0.0.1:3000" -"timeout" "2.0" -"buffer" "0.0" -"throttle" "0.1" -"heartbeat" "60.0" -"output" - { - "precision_time" "3" - "precision_position" "1" - "precision_vector" "3" - } -"data" - { - "provider" "1" // general info about client being listened to: game name, appid, client steamid, etc. - "map" "1" // map, gamemode, and current match phase ('warmup', 'intermission', 'gameover', 'live') and current score - "round" "1" // round phase ('freezetime', 'over', 'live'), bomb state ('planted', 'exploded', 'defused'), and round winner (if any) - "player_id" "1" // player name, clan tag, observer slot (ie key to press to observe this player) and team - "player_state" "1" // player state for this current round such as health, armor, kills this round, etc. - "player_match_stats" "1" // player stats this match such as kill, assists, score, deaths and MVPs - } -} \ No newline at end of file