diff --git a/CS2WebSocketTelemetryPlugin/CS2WebSocketTelemetryPlugin.cs b/CS2WebSocketTelemetryPlugin/CS2WebSocketTelemetryPlugin.cs index b5dd042..dc961cc 100644 --- a/CS2WebSocketTelemetryPlugin/CS2WebSocketTelemetryPlugin.cs +++ b/CS2WebSocketTelemetryPlugin/CS2WebSocketTelemetryPlugin.cs @@ -1,14 +1,12 @@ // CS2WebSocketTelemetryPlugin.cs using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; -using System.Net.Sockets; using System.Net.Security; +using System.Net.Sockets; using System.Reflection; using System.Security.Authentication; using System.Security.Cryptography; @@ -22,7 +20,6 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Events; using Microsoft.Extensions.Logging; namespace WsTelemetry; @@ -31,9 +28,9 @@ namespace WsTelemetry; public class WebSocketTelemetryPlugin : BasePlugin { public override string ModuleName => "WS Telemetry"; - public override string ModuleVersion => "1.5.2"; + public override string ModuleVersion => "1.6.0"; public override string ModuleAuthor => "you + ChatGPT"; - public override string ModuleDescription => "WS(S)-Server: Spielerpos/aim + Granaten (Tick-Trace/Bounce/Detonate/Path) + Map + Smoke/Fire-Status"; + public override string ModuleDescription => "WS(S)-Server: Spielerpositionen + Blickrichtung + Map"; // --- Konfiguration --- private volatile bool _enabled = false; @@ -68,7 +65,6 @@ public class WebSocketTelemetryPlugin : BasePlugin public int? SendHz { get; set; } } - private void LoadAndApplyConfig(bool generateIfMissing = true) { try @@ -135,10 +131,6 @@ public class WebSocketTelemetryPlugin : BasePlugin // Sendefrequenz if (cfg.SendHz is int hz && hz >= 1 && hz <= 128) _sendHz = hz; - // Durations bleiben hart verdrahtet - _smokeDurationSec = SMOKE_DURATION_SEC; - _fireDurationSec = FIRE_DURATION_SEC; - Logger.LogInformation($"[WS] Konfiguration geladen ({_bindHost}:{_bindPort}{_bindPath}, tls={_useTls}, hz={_sendHz})"); } catch (Exception ex) @@ -153,10 +145,8 @@ public class WebSocketTelemetryPlugin : BasePlugin { var path = Path.Combine(ModuleDirectory, ConfigFileName); - // URL aus aktuellen Feldern zusammensetzen var url = $"{(_useTls ? "wss" : "ws")}://{_bindHost}:{_bindPort}{_bindPath}"; - // CertPath möglichst relativ speichern var cp = _certPath; try { @@ -206,102 +196,6 @@ public class WebSocketTelemetryPlugin : BasePlugin private const double MaxFrameDt = 0.25; private DateTime _lastTick = DateTime.UtcNow; - // --- Nade Runtime-Status --- - private int _seq = 0; // globale Sequenz (für Nade/Trace IDs) - - // Fest verdrahtete Zeiten (nicht änderbar) - private const double SMOKE_DURATION_SEC = 20.0; - private const double FIRE_DURATION_SEC = 7.0; - - // interner Zugriff bleibt über Felder, die auf die Konstanten gesetzt werden - private double _smokeDurationSec = SMOKE_DURATION_SEC; - private double _fireDurationSec = FIRE_DURATION_SEC; - - private class AoeState - { - public required DateTimeOffset EndAt; - public required float X; - public required float Y; - public required float Z; - } - - private readonly ConcurrentDictionary _activeSmokes = new(); - private readonly ConcurrentDictionary _activeFires = new(); - - // --- Trajektorien-Tracking --- - private class NadeTrack - { - public int Id; - public ulong SteamId; - public string Nade = ""; - public DateTimeOffset Start; - public DateTimeOffset Last; - public bool Finalized; - - public object? BoundEntity; - public string? BoundName; - public float LastX, LastY, LastZ; - - public int Sent = 0; // bereits gesendete Punkte - public int CurrentSeg = 0; // aktuelle Segment-ID (0-basiert) - - public readonly List Points = new(); - public class Point { public float X; public float Y; public float Z; public long T; public int S; } - } - - // Alle Tracks by id - private readonly ConcurrentDictionary _tracks = new(); - // Letzter aktiver Track je Spieler (für grenade_bounce ohne Typ) - private readonly ConcurrentDictionary _lastTrackByPlayer = new(); - // Aktiver Track je Spieler+Nade (für eindeutiges Finalisieren) - private readonly ConcurrentDictionary _activeTrackByPlayerNade = new(); - - private static string PNKey(ulong steamId, string nade) => $"{steamId}:{nade}"; - - // --- Entity-Suche via Reflection --- - private static MethodInfo? _miFindByDesigner; - private static MethodInfo? _miFindByClass; - - private static IEnumerable FindEntitiesByName(string name) - { - _miFindByDesigner ??= typeof(Utilities).GetMethod("FindAllEntitiesByDesignerName", BindingFlags.Public | BindingFlags.Static); - _miFindByClass ??= typeof(Utilities).GetMethod("FindAllEntitiesByClassname", BindingFlags.Public | BindingFlags.Static); - - IEnumerable? ie = null; - try - { - if (_miFindByDesigner != null) - ie = _miFindByDesigner.Invoke(null, new object[] { name }) as IEnumerable; - if (ie == null && _miFindByClass != null) - ie = _miFindByClass.Invoke(null, new object[] { name }) as IEnumerable; - } - catch { } - - if (ie == null) yield break; - foreach (var e in ie) if (e != null) yield return e; - } - - private static bool TryGetPos(object ent, out float x, out float y, out float z) - { - try - { - dynamic d = ent; - var pos = d.AbsOrigin; - x = (float)pos.X; y = (float)pos.Y; z = (float)pos.Z; - return true; - } - catch - { - x = y = z = 0; - return false; - } - } - private static bool IsEntValid(object ent) - { - try { dynamic d = ent; return d != null && d.IsValid; } - catch { return ent != null; } - } - // --- Stabiler Aim --- private readonly ConcurrentDictionary _lastAimByPlayer = new(); @@ -321,64 +215,120 @@ public class WebSocketTelemetryPlugin : BasePlugin return pitch; } - // -- Camera-gesteuerte Blickrichtung -- + // ========================= + // Blickrichtung (Client-Kamera) – EyeAngles bevorzugt + // ========================= + + private static bool IsAlive(dynamic pawn) + { + try { int ls = (int)pawn.LifeState; return ls == 0; } catch { } + try { return (bool)pawn.IsAlive; } catch { } + try { int hp = (int)pawn.Health; return hp > 0; } catch { } + return true; + } + private static bool TryReadAngles(dynamic src, out float pitch, out float yaw, out float roll) { - // Fallback-Init, damit out immer gesetzt ist pitch = 0f; yaw = 0f; roll = 0f; - try { var a = src.EyeAngles; pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } + // Kamera-/Aim-Winkel + try { var a = src.EyeAngles; pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } + // Modell-/Knotenorientierung try { var a = src.AbsRotation; pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } try { var a = src.ViewAngles; pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } try { pitch = (float)src.Pitch; yaw = (float)src.Yaw; roll = 0f; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } - // bleibt false; out sind bereits gesetzt return false; } - private static bool TryGetCameraAnglesFromRoot(dynamic root, out float pitch, out float yaw, out float roll) + /// Winkel vorzugsweise vom SceneNode; Fallback: NodeToWorld.Angles, root.AbsRotation. + private static bool TryGetAnglesFromSceneNode(dynamic root, out float pitch, out float yaw, out float roll) { - // out-Parameter VOR jeglicher Logik initialisieren (fix für CS0177) pitch = 0f; yaw = 0f; roll = 0f; - // 1) Aktive Kamera-Entity über CameraServices try { - dynamic cs = root.CameraServices; - dynamic h = (cs != null) ? cs.m_hActiveCamera : null; - dynamic cam = (h != null ? h.Value : null) ?? cs?.ActiveCamera ?? root.Camera; - if (cam != null && TryReadAngles(cam, out pitch, out yaw, out roll)) - return true; + dynamic node = root?.GameSceneNode; + if (node != null) + { + try { var r = node.AbsRotation; pitch = (float)r.X; yaw = (float)r.Y; roll = (float)r.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } + try { var tf = node.NodeToWorld; var a = tf.Angles; pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } + } + try { var r = root.AbsRotation; pitch = (float)r.X; yaw = (float)r.Y; roll = (float)r.Z; if (IsFinite(pitch) && IsFinite(yaw)) return true; } catch { } } catch { } - // 2) Winkel direkt aus CameraServices - try - { - dynamic cs = root.CameraServices; - if (cs != null && TryReadAngles(cs, out pitch, out yaw, out roll)) - return true; - } - catch { } - - // 3) Letzter Versuch: direkt am Root - return TryReadAngles(root, out pitch, out yaw, out roll); - } - - private static bool TryGetCameraAngles(CCSPlayerController p, dynamic pawn, out float pitch, out float yaw, out float roll) - { - // out-Defaults - pitch = 0f; yaw = 0f; roll = 0f; - - if (pawn != null && TryGetCameraAnglesFromRoot(pawn, out pitch, out yaw, out roll)) - return true; - - if (TryGetCameraAnglesFromRoot(p, out pitch, out yaw, out roll)) - return true; - return false; } + /// Observer-Target via Pawn (nicht Controller!), robust über verschiedene Felder. + private static dynamic? TryGetObserverTargetFromPawn(dynamic pawn) + { + // typische Felder am Observer-Pawn / Observer-Komponente + try { var os = pawn.ObserverServices; var h = os?.m_hObserverTarget; var v = (h != null ? (h.Value ?? h) : null); if (v != null) return v; } catch { } + try { var h = pawn.m_hObserverTarget; var v = (h != null ? (h.Value ?? h) : null); if (v != null) return v; } catch { } + try { var h = pawn.m_hLastObserverTarget; var v = (h != null ? (h.Value ?? h) : null); if (v != null) return v; } catch { } + try { var v = pawn.ObserverTarget; if (v != null) return (v.Value ?? v); } catch { } + return null; + } + + /// Irgendeine Entität in einen Pawn auflösen (Controller→Pawn, Entity→Pawn, bereits Pawn). + private static dynamic? AsPawn(dynamic entity) + { + if (entity == null) return null; + try { var p = entity.PlayerPawn; if (p != null && p.IsValid) return p.Value ?? p; } catch { } + try { var p = entity.Pawn; if (p != null && p.IsValid) return p.Value ?? p; } catch { } + try { var _ = entity.EyeAngles; return entity; } catch { } + try { var _ = entity.AbsOrigin; return entity; } catch { } + return null; + } + + /// Pawn, dessen Kamera wir darstellen: lebend → eigener Pawn; sonst → Observer-Target (vom Pawn aus). + private static bool TryGetClientCameraPawn(CCSPlayerController ctrl, dynamic pawn, out dynamic camPawn) + { + camPawn = null; + try { if (pawn != null && IsAlive(pawn)) { camPawn = pawn; return true; } } catch { } + var tgtEnt = TryGetObserverTargetFromPawn(pawn); + var tgtPawn = AsPawn(tgtEnt); + if (tgtPawn != null) { camPawn = tgtPawn; return true; } + return false; + } + + /// Endgültige View-Winkel (EyeAngles bevorzugt, sonst SceneNode/AbsRotation, dann Fallbacks). + private static bool TryGetViewAngles(CCSPlayerController ctrl, dynamic pawn, out float pitch, out float yaw, out float roll) + { + pitch = 0f; yaw = 0f; roll = 0f; + + dynamic camPawn; + if (TryGetClientCameraPawn(ctrl, pawn, out camPawn)) + { + // 1) Echte Kamera-/Aim-Winkel + try + { + var a = camPawn.EyeAngles; + pitch = (float)a.X; yaw = (float)a.Y; roll = (float)a.Z; + if (IsFinite(pitch) && IsFinite(yaw)) return true; + } + catch { } + + // 2) Modellorientierung als Fallback + if (TryGetAnglesFromSceneNode(camPawn, out pitch, out yaw, out roll)) + return true; + + try + { + var r = camPawn.AbsRotation; + pitch = (float)r.X; yaw = (float)r.Y; roll = (float)r.Z; + if (IsFinite(pitch) && IsFinite(yaw)) return true; + } + catch { } + } + + // 3) letzte Fallbacks + if (TryReadAngles(pawn, out pitch, out yaw, out roll)) return true; + return TryReadAngles(ctrl, out pitch, out yaw, out roll); + } + private (float pitch, float yaw) StoreAim(CCSPlayerController p, float pitch, float yaw) { var sid = p.AuthorizedSteamID?.SteamId64 ?? 0UL; @@ -386,38 +336,66 @@ public class WebSocketTelemetryPlugin : BasePlugin return (pitch, yaw); } + private static bool IsTiny(float v) => MathF.Abs(v) <= 1e-5f; + private static bool IsTinyPair(float a, float b) => IsTiny(a) && IsTiny(b); + private (float pitch, float yaw) GetStableAim(CCSPlayerController p, dynamic pawn) { - // 0) Kamera-Winkel bevorzugen - float cp, cy, cr; - if (TryGetCameraAngles(p, pawn, out cp, out cy, out cr)) - return StoreAim(p, ClampPitch(cp), NormalizeYaw(cy)); + float vp=0, vy=0, vr=0; - // 1) Fallback: Pawn EyeAngles + // 1) Bevorzugt: echte Kamera-/View-Winkel + if (TryGetViewAngles(p, pawn, out vp, out vy, out vr)) + { + vp = ClampPitch(vp); vy = NormalizeYaw(vy); + if (!IsTinyPair(vp, vy)) // 0/0 ist verdächtig → nicht akzeptieren + return StoreAim(p, vp, vy); + } + + // 2) Fallback: Pawn EyeAngles (falls vorhanden) try { dynamic a = pawn?.EyeAngles; float px = (float)a.X, py = (float)a.Y; if (IsFinite(px) && IsFinite(py)) - return StoreAim(p, ClampPitch(px), NormalizeYaw(py)); - } - catch { } - - // 2) Fallback: AbsRotation (Yaw), Pitch aus letztem Wert - try - { - dynamic r = pawn?.AbsRotation; - float yaw = (float)r.Y; - if (IsFinite(yaw)) { - var sid = p.AuthorizedSteamID?.SteamId64 ?? 0UL; - float pitch = _lastAimByPlayer.TryGetValue(sid, out var last) ? last.pitch : 0f; - return StoreAim(p, pitch, NormalizeYaw(yaw)); + px = ClampPitch(px); py = NormalizeYaw(py); + if (!IsTinyPair(px, py)) + return StoreAim(p, px, py); } } catch { } - // 3) Letzter gültiger Aim oder neutral + // 3) Fallback: Modell-/Feet-Yaw aus AbsRotation + try + { + dynamic r = pawn?.AbsRotation; + float ryaw = (float)r.Y; + if (IsFinite(ryaw) && !IsTiny(ryaw)) + { + var sid = p.AuthorizedSteamID?.SteamId64 ?? 0UL; + float pitch = _lastAimByPlayer.TryGetValue(sid, out var last) ? last.pitch : 0f; + return StoreAim(p, pitch, NormalizeYaw(ryaw)); + } + } + catch { } + + // 4) Fallback: Bewegungsrichtung (nur wenn sich der Spieler bewegt) + try + { + var vel = pawn?.AbsVelocity; + float vx = (float)vel.X, vy2 = (float)vel.Y; + float sp = MathF.Sqrt(vx * vx + vy2 * vy2); + if (sp > 1f) + { + float vyaw = NormalizeYaw(MathF.Atan2(vy2, vx) * 180f / MathF.PI); + var sid = p.AuthorizedSteamID?.SteamId64 ?? 0UL; + float pitch = _lastAimByPlayer.TryGetValue(sid, out var last) ? last.pitch : 0f; + return StoreAim(p, pitch, vyaw); + } + } + catch { } + + // 5) Letzter gültiger Aim oder neutral { var sid = p.AuthorizedSteamID?.SteamId64 ?? 0UL; if (_lastAimByPlayer.TryGetValue(sid, out var last)) return last; @@ -425,19 +403,35 @@ public class WebSocketTelemetryPlugin : BasePlugin } } + private static (float pitch, float yaw, float roll) ReadEyeAngles(dynamic pawn) + { + try + { + var ea = pawn?.EyeAngles; + float pitch = (float)ea.X; + float yaw = (float)ea.Y; + float roll = (float)ea.Z; + + // wie üblich begrenzen/normalisieren + if (IsFinite(pitch) && IsFinite(yaw)) + return (ClampPitch(pitch), NormalizeYaw(yaw), roll); + } + catch { } + return (0f, 0f, 0f); + } + + public override void Load(bool hotReload) { - Logger.LogInformation("[WS] Plugin geladen. Kommandos: css_ws_enable, css_ws_reloadcfg, css_ws_url, css_ws_rate, css_ws_cert, css_ws_certpwd, css_ws_sendmap"); + Logger.LogInformation("[WS] Plugin geladen. Kommandos: css_ws_enable, css_ws_restart, css_ws_reloadcfg, css_ws_url, css_ws_rate, css_ws_cert, css_ws_certpwd, css_ws_sendmap"); RegisterListener(OnTick); _mapName = Server.MapName ?? ""; RegisterListener(OnMapStart); - // Konfiguration einlesen (ohne enabled) … LoadAndApplyConfig(); - // … und automatisch starten (abschaltbar per css_ws_enable 0) _enabled = true; StartWebSocket(); } @@ -503,6 +497,36 @@ public class WebSocketTelemetryPlugin : BasePlugin } } + [ConsoleCommand("css_ws_restart", "Lädt config.json neu und startet den WS(S)-Server neu.")] + [CommandHelper(minArgs: 0, usage: "")] + public void CmdRestart(CCSPlayerController? caller, CommandInfo cmd) + { + try + { + // Config immer neu laden (ohne Beispieldatei zu erzeugen) + LoadAndApplyConfig(generateIfMissing: false); + + // internen Zustand zurücksetzen + _lastTick = DateTime.UtcNow; + _accumulator = 0; + while (_outbox.TryDequeue(out _)) { } + + // Server neu starten (nur wenn derzeit/enabled) + var wasEnabled = _enabled; + StopWebSocket(); + if (wasEnabled) + StartWebSocket(); + + cmd.ReplyToCommand("[WS] Config neu geladen und neu gestartet."); + if (!wasEnabled) + cmd.ReplyToCommand("[WS] Hinweis: Server ist deaktiviert (css_ws_enable 1), Neustart war nur intern."); + } + catch (Exception ex) + { + cmd.ReplyToCommand($"[WS] Restart-Fehler: {ex.Message}"); + } + } + [ConsoleCommand("css_ws_sendmap", "Sendet die aktuelle Karte an alle verbundenen Clients")] public void CmdSendMap(CCSPlayerController? caller, CommandInfo cmd) { @@ -595,18 +619,8 @@ public class WebSocketTelemetryPlugin : BasePlugin if (_accumulator < targetDt) return; _accumulator = 0; - // verwaiste Tracks aufräumen (Failsafe, 15s) - foreach (var kv in _tracks) - { - var tr = kv.Value; - if (!tr.Finalized && (DateTimeOffset.UtcNow - tr.Last).TotalSeconds > 15) - { - TryFinalizeTrackById(tr.Id, sendEvenIfEmpty: true, nadeOverride: tr.Nade); - } - } - - // --- Spieler sammeln --- var playersList = new List(); + foreach (var p in Utilities.GetPlayers()) { try @@ -619,704 +633,71 @@ public class WebSocketTelemetryPlugin : BasePlugin var pawn = pawnHandle.Value; if (pawn == null) continue; - var pos = pawn.AbsOrigin; - - // stabile Blickrichtung - var (aimPitch, aimYaw) = GetStableAim(p, pawn); - - // Bewegungsrichtung aus Velocity (2D) - float dirYaw = 0f, speed2D = 0f; - float vx = 0f, vy = 0f, vz = 0f; + // --- Position (SceneNode.AbsOrigin bevorzugt) + float posX, posY, posZ; try { - var vel = pawn.AbsVelocity; - vx = vel.X; vy = vel.Y; vz = vel.Z; - speed2D = MathF.Sqrt(vx * vx + vy * vy); - if (speed2D > 1f) - dirYaw = NormalizeYaw((float)(Math.Atan2(vy, vx) * 180.0 / Math.PI)); + var node = pawn?.CBodyComponent?.SceneNode; + var org = node != null ? node.AbsOrigin : pawn.AbsOrigin; + posX = (float)org.X; + posY = (float)org.Y; + posZ = (float)org.Z; + } + catch + { + var org = pawn.AbsOrigin; + posX = (float)org.X; + posY = (float)org.Y; + posZ = (float)org.Z; + } + + // --- Blickrichtung (Yaw aus EyeAngles, bereits normalisiert/geclamped) + var (_, eyeYaw, _) = ReadEyeAngles(pawn); + float yawDeg = eyeYaw; + + // --- viewAngle wie im Beispiel: AbsRotation (Pitch/Yaw/Roll) + float angX = 0f, angY = 0f, angZ = 0f; + try + { + var ang = pawn.AbsRotation; + angX = (float)ang.X; // pitch + angY = (float)ang.Y; // yaw + angZ = (float)ang.Z; // roll } catch { } - // rohe Pawn-Angles (Debug/Kompat) - float rawPitch = 0f, rawYaw = 0f, rawRoll = 0f; - try - { - var ar = pawn.EyeAngles; - rawPitch = ar.X; rawYaw = ar.Y; rawRoll = ar.Z; - } - catch { } + // --- Alive-Status + bool isAlive = true; + try { int ls = (int)pawn.LifeState; isAlive = (ls == 0); } catch { } + if (!isAlive) { try { isAlive = ((int)pawn.Health) > 0; } catch { } } + // --- Minimales Player-Objekt + viewAngle playersList.Add(new { - steamId = p.AuthorizedSteamID?.SteamId64 ?? 0UL, - name = p.PlayerName, - team = p.TeamNum, - pos = new { x = pos.X, y = pos.Y, z = pos.Z }, - - // stabilisierte Blickrichtung (nutzen!) - view = new { pitch = aimPitch, yaw = aimYaw, roll = 0f }, - aim = new { pitch = aimPitch, yaw = aimYaw }, - - // optional: rohe Werte - viewRaw = new { pitch = rawPitch, yaw = rawYaw, roll = rawRoll }, - - move = new - { - dirYaw = speed2D > 1f ? dirYaw : (float?)null, - speed = speed2D, - vel = new { x = vx, y = vy, z = vz } - } + steamId = p.AuthorizedSteamID?.SteamId64 ?? 0UL, + name = p.PlayerName, + team = p.TeamNum, + pos = new { x = posX, y = posY, z = posZ }, + viewAngle = new { pitch = angX, yaw = angY, roll = angZ }, + alive = isAlive }); } - catch { } + catch { /* Spieler überspringen bei Fehlern */ } } - // --- Nade-Trace pro Tick sammeln --- - var traceList = new List(); - - // Sammeln/Updaten der aktiven Projektile (füllt nur Track-Punkte) - SampleActiveNadeTracks(); - - // --- Nade-Path Realtime-Streaming pro Tick --- - foreach (var tr in _tracks.Values) - { - if (tr.Finalized) continue; - - int from, to; - List delta = null!; - lock (tr.Points) - { - from = tr.Sent; - to = tr.Points.Count; - if (to > from) - { - delta = new List(to - from); - for (int i = from; i < to; i++) - { - var p = tr.Points[i]; - delta.Add(new { x = p.X, y = p.Y, z = p.Z, t = p.T, s = p.S }); - } - tr.Sent = to; - } - } - - if (delta != null && delta.Count > 0) - { - Enqueue(new - { - type = "nade", - sub = "path", - id = tr.Id, - nade = tr.Nade, - steamId = tr.SteamId, - t = NowMs(), - points = delta, - final = false - }); - } - } - - // aktive Nades (letzte bekannte Position) - var activeList = new List(); - foreach (var tr in _tracks.Values) - { - if (tr.Finalized) continue; - activeList.Add(new - { - id = tr.Id, - nade = tr.Nade, - steamId = tr.SteamId, - pos = new { x = tr.LastX, y = tr.LastY, z = tr.LastZ } - }); - } - - if (playersList.Count == 0 && traceList.Count == 0 && activeList.Count == 0) return; + if (playersList.Count == 0) return; var payload = new { type = "tick", t = NowMs(), - players = playersList, - nades = (activeList.Count > 0 || traceList.Count > 0) - ? new { trace = traceList, active = activeList } - : null + players = playersList }; Broadcast(JsonSerializer.Serialize(payload)); } - // ========================= - // Sampling der aktiven Projektile - // ========================= - private static readonly Dictionary _kindToNames = new() - { - ["he"] = new[] { "hegrenade_projectile" }, - ["flash"] = new[] { "flashbang_projectile" }, - ["smoke"] = new[] { "smokegrenade_projectile" }, - ["decoy"] = new[] { "decoy_projectile" }, - ["molotov"] = new[] { "molotov_projectile", "incgrenade_projectile" }, - ["other"] = new[] { "grenade_projectile" } // Fallback - }; - - private IEnumerable<(object ent, string name, float x, float y, float z)> EnumerateProjectilesFor(string kind) - { - if (!_kindToNames.TryGetValue(kind, out var names)) names = _kindToNames["other"]; - - foreach (var nm in names) - { - foreach (var e in FindEntitiesByName(nm)) - { - if (e == null || !IsEntValid(e)) continue; - if (TryGetPos(e, out var x, out var y, out var z)) - yield return (e, nm, x, y, z); - } - } - } - - private void SampleActiveNadeTracks() - { - var neededKinds = _tracks.Values.Where(t => !t.Finalized).Select(t => t.Nade).Distinct().ToList(); - var poolByKind = new Dictionary>(); - foreach (var k in neededKinds) - poolByKind[k] = EnumerateProjectilesFor(k).ToList(); - - var usedEnts = new HashSet(ReferenceEqualityComparer.Instance); - - foreach (var kv in _tracks) - { - var tr = kv.Value; - if (tr.Finalized) continue; - - if (tr.BoundEntity == null) - { - if (!poolByKind.TryGetValue(tr.Nade, out var list) || list.Count == 0) continue; - - var best = list - .Where(tpl => !usedEnts.Contains(tpl.ent)) - .Select(tpl => new { tpl.ent, tpl.name, tpl.x, tpl.y, tpl.z, dist2 = Dist2(tr.LastX, tr.LastY, tr.LastZ, tpl.x, tpl.y, tpl.z) }) - .OrderBy(a => a.dist2) - .FirstOrDefault(); - - // NEU: altersabhängiger Fangradius + sanfter Fallback - var ageSec = (float)(DateTimeOffset.UtcNow - tr.Start).TotalSeconds; - - // wächst schnell an, deckelt bei 4500u (ca. halbe Mapbreite) - var maxDist = MathF.Min(4500f, 300f + ageSec * 2500f); - var maxDist2 = maxDist * maxDist; - - if (best != null && best.dist2 <= maxDist2) - { - tr.BoundEntity = best.ent; - tr.BoundName = best.name; - tr.LastX = best.x; tr.LastY = best.y; tr.LastZ = best.z; - tr.Last = DateTimeOffset.UtcNow; - - var tms = NowMs(); - lock (tr.Points) - tr.Points.Add(new NadeTrack.Point { X = best.x, Y = best.y, Z = best.z, T = tms, S = tr.CurrentSeg }); - - usedEnts.Add(best.ent); - } - else if (best != null && tr.Points.Count <= 1) - { - // Fallback: ganz am Anfang binden wir notfalls trotzdem den nächsten Kandidaten, - // damit sofort Bewegungsdaten fließen (vermeidet "nur Wurf+Detonate"). - tr.BoundEntity = best.ent; - tr.BoundName = best.name; - tr.LastX = best.x; tr.LastY = best.y; tr.LastZ = best.z; - tr.Last = DateTimeOffset.UtcNow; - - var tms = NowMs(); - lock (tr.Points) - tr.Points.Add(new NadeTrack.Point { X = best.x, Y = best.y, Z = best.z, T = tms, S = tr.CurrentSeg }); - - usedEnts.Add(best.ent); - } - } - else - { - var ent = tr.BoundEntity; - if (ent == null || !IsEntValid(ent)) continue; - - if (TryGetPos(ent, out var ex, out var ey, out var ez)) - { - if (Dist2(tr.LastX, tr.LastY, tr.LastZ, ex, ey, ez) > 0.1f * 0.1f) - { - var tms = NowMs(); - lock (tr.Points) - tr.Points.Add(new NadeTrack.Point { X = ex, Y = ey, Z = ez, T = tms, S = tr.CurrentSeg }); - - tr.LastX = ex; tr.LastY = ey; tr.LastZ = ez; - tr.Last = DateTimeOffset.UtcNow; - } - } - } - } - } - - private static float Dist2(float ax, float ay, float az, float bx, float by, float bz) - { - float dx = ax - bx, dy = ay - by, dz = az - bz; - return dx * dx + dy * dy + dz * dz; - } - - private static bool LooksAirburst(NadeTrack tr) - { - if (tr.Points.Count < 2) return false; - var a = tr.Points[^1]; - var b = tr.Points[^2]; - var dtMs = Math.Max(1, a.T - b.T); - var dist = MathF.Sqrt(Dist2(a.X, a.Y, a.Z, b.X, b.Y, b.Z)); - var speed3D = 1000f * dist / dtMs; // units/s - var vz = 1000f * Math.Abs(a.Z - b.Z) / dtMs; // vertikale speed - return speed3D > 250f || vz > 120f; - } - - // ========================= - // Game-Events: Granaten - // ========================= - - private static string WeaponToKind(string weapon) - { - var w = (weapon ?? "").ToLowerInvariant(); - return w switch - { - "hegrenade" or "weapon_hegrenade" => "he", - "flashbang" or "weapon_flashbang" => "flash", - "smokegrenade" or "weapon_smokegrenade" => "smoke", - "decoy" or "weapon_decoy" => "decoy", - "molotov" or "weapon_molotov" or "incgrenade" or "weapon_incgrenade" => "molotov", - _ => "other" - }; - } - - private int StartTrack(ulong steamId, string nade, float x, float y, float z) - { - var id = Interlocked.Increment(ref _seq); - var tr = new NadeTrack - { - Id = id, - SteamId = steamId, - Nade = nade, - Start = DateTimeOffset.UtcNow, - Last = DateTimeOffset.UtcNow, - LastX = x, LastY = y, LastZ = z, - Sent = 0, - CurrentSeg = 0 - }; - tr.Points.Add(new NadeTrack.Point { X = x, Y = y, Z = z, T = NowMs(), S = tr.CurrentSeg }); - _tracks[id] = tr; - _lastTrackByPlayer[steamId] = id; - _activeTrackByPlayerNade[PNKey(steamId, nade)] = id; - return id; - } - - private void AppendPointByPlayer(ulong steamId, float x, float y, float z) - { - if (_lastTrackByPlayer.TryGetValue(steamId, out var id) && - _tracks.TryGetValue(id, out var tr) && !tr.Finalized) - { - lock (tr.Points) - { - var tms = NowMs(); - tr.Points.Add(new NadeTrack.Point { X = x, Y = y, Z = z, T = tms, S = tr.CurrentSeg }); - tr.LastX = x; tr.LastY = y; tr.LastZ = z; - tr.Last = DateTimeOffset.UtcNow; - } - } - } - - private void AppendPointByPlayerBounce(ulong steamId, float x, float y, float z) - { - if (_lastTrackByPlayer.TryGetValue(steamId, out var id) && - _tracks.TryGetValue(id, out var tr) && !tr.Finalized) - { - lock (tr.Points) - { - tr.CurrentSeg++; // neues Liniensegment beginnen - var tms = NowMs(); - tr.Points.Add(new NadeTrack.Point { X = x, Y = y, Z = z, T = tms, S = tr.CurrentSeg }); - tr.LastX = x; tr.LastY = y; tr.LastZ = z; - tr.Last = DateTimeOffset.UtcNow; - } - } - } - - private void TryFinalizeTrack(ulong steamId, string nade, float? x, float? y, float? z) - { - var key = PNKey(steamId, nade); - if (!_activeTrackByPlayerNade.TryRemove(key, out var id)) return; - TryFinalizeTrackById(id, true, nade, x, y, z); - _lastTrackByPlayer.TryGetValue(steamId, out var lastId); - if (lastId == id) _lastTrackByPlayer.TryRemove(steamId, out _); - } - - private void TryFinalizeTrackById(int id, bool sendEvenIfEmpty, string? nadeOverride = null, float? x = null, float? y = null, float? z = null) - { - if (!_tracks.TryGetValue(id, out var tr) || tr.Finalized) return; - - if (x.HasValue && y.HasValue && z.HasValue) - { - lock (tr.Points) - { - tr.Points.Add(new NadeTrack.Point { X = x.Value, Y = y.Value, Z = z.Value, T = NowMs(), S = tr.CurrentSeg }); - } - } - - // Rest-Delta senden (falls noch nicht raus) - List? delta = null; - lock (tr.Points) - { - if (tr.Points.Count > tr.Sent) - { - delta = new List(tr.Points.Count - tr.Sent); - for (int i = tr.Sent; i < tr.Points.Count; i++) - { - var p = tr.Points[i]; - delta.Add(new { x = p.X, y = p.Y, z = p.Z, t = p.T, s = p.S }); - } - tr.Sent = tr.Points.Count; - } - } - - tr.Finalized = true; - - // Finale Nachricht - Enqueue(new - { - type = "nade", - sub = "path", - id = tr.Id, - nade = nadeOverride ?? tr.Nade, - steamId = tr.SteamId, - t = NowMs(), - points = (delta != null && delta.Count > 0) ? delta : null, - final = true - }); - - _tracks.TryRemove(id, out _); - } - - [GameEventHandler] - public HookResult OnGrenadeThrown(EventGrenadeThrown ev, GameEventInfo info) - { - try - { - var p = ev.Userid; - var pawn = p?.PlayerPawn?.Value; - var pos = pawn?.AbsOrigin; - var (gaPitch, gaYaw) = GetStableAim(p, pawn); - - string weapon = ev.Weapon ?? ""; - string kind = WeaponToKind(weapon); - - float tx = pos?.X ?? 0f, ty = pos?.Y ?? 0f, tz = pos?.Z ?? 0f; - var id = StartTrack(p?.AuthorizedSteamID?.SteamId64 ?? 0UL, kind, tx, ty, tz); - - try { SampleActiveNadeTracks(); } catch { } - - Enqueue(new - { - type = "nade", - sub = "thrown", - id, - nade = kind, - t = NowMs(), - steamId = p?.AuthorizedSteamID?.SteamId64 ?? 0UL, - name = p?.PlayerName ?? "", - weapon = weapon, - throwPos = new { x = tx, y = ty, z = tz }, - throwAim = new { pitch = gaPitch, yaw = NormalizeYaw(gaYaw) } - }); - } - catch { } - return HookResult.Continue; - } - - // Bounces -> Trajektorie weiter aufzeichnen (Punkt erscheint im nächsten Tick) - [GameEventHandler] - public HookResult OnGrenadeBounce(EventGrenadeBounce ev, GameEventInfo info) - { - try - { - dynamic d = ev; - var p = d.Userid as CCSPlayerController; - AppendPointByPlayerBounce(p?.AuthorizedSteamID?.SteamId64 ?? 0UL, (float)d.X, (float)d.Y, (float)d.Z); - } - catch { } - return HookResult.Continue; - } - - - [GameEventHandler] public HookResult OnHeDetonate (EventHegrenadeDetonate ev, GameEventInfo info) - { TryFinalizeDet(ev, "he"); return EnqueueNadeDet(ev, "he"); } - - [GameEventHandler] public HookResult OnFlashDetonate (EventFlashbangDetonate ev, GameEventInfo info) - { TryFinalizeDet(ev, "flash"); return EnqueueNadeDet(ev, "flash"); } - - [GameEventHandler] public HookResult OnDecoyDetonate (EventDecoyDetonate ev, GameEventInfo info) - { TryFinalizeDet(ev, "decoy"); return EnqueueNadeDet(ev, "decoy"); } - - private void TryFinalizeDet(GameEvent ev, string kind) - { - try - { - dynamic d = ev; - var p = d.Userid as CCSPlayerController; - TryFinalizeTrack(p?.AuthorizedSteamID?.SteamId64 ?? 0UL, kind, (float)d.X, (float)d.Y, (float)d.Z); - } - catch { } - } - - // Smoke: Detonate + start/end mit ETA (+ Pfadabschluss) - [GameEventHandler] - public HookResult OnSmokeDetonate(EventSmokegrenadeDetonate ev, GameEventInfo info) - { - try - { - dynamic d = ev; - var p = d.Userid as CCSPlayerController; - - TryFinalizeTrack(p?.AuthorizedSteamID?.SteamId64 ?? 0UL, "smoke", (float)d.X, (float)d.Y, (float)d.Z); - - Enqueue(new - { - type = "nade", - sub = "detonate", - nade = "smoke", - t = NowMs(), - steamId = p?.AuthorizedSteamID?.SteamId64 ?? 0UL, - name = p?.PlayerName ?? "", - pos = new { x = (float)d.X, y = (float)d.Y, z = (float)d.Z } - }); - - var id = Interlocked.Increment(ref _seq); - var endAt = DateTimeOffset.UtcNow.AddSeconds(_smokeDurationSec); - var state = new AoeState { EndAt = endAt, X = (float)d.X, Y = (float)d.Y, Z = (float)d.Z }; - _activeSmokes[id] = state; - - Enqueue(new - { - type = "smoke", - state = "start", - id, - t = NowMs(), - endAt = endAt.ToUnixTimeMilliseconds(), - pos = new { x = state.X, y = state.Y, z = state.Z } - }); - - var token = _serverCts?.Token ?? CancellationToken.None; - _ = Task.Run(async () => - { - try - { - await Task.Delay(TimeSpan.FromSeconds(_smokeDurationSec), token); - if (_activeSmokes.TryRemove(id, out _)) - { - Enqueue(new { type = "smoke", state = "end", id, t = NowMs() }); - } - } - catch { } - }, token); - } - catch { } - return HookResult.Continue; - } - - // Molotov: Detonate + start/end mit ETA (+ Pfadabschluss) – nur start, wenn kein Airburst - [GameEventHandler] - public HookResult OnMolotovDetonate(EventMolotovDetonate ev, GameEventInfo info) - { - try - { - dynamic d = ev; - var p = d.Userid as CCSPlayerController; - var sid = p?.AuthorizedSteamID?.SteamId64 ?? 0UL; - - bool airburst = false; - if (_activeTrackByPlayerNade.TryGetValue(PNKey(sid, "molotov"), out var tid) && - _tracks.TryGetValue(tid, out var trProbe)) - { - airburst = LooksAirburst(trProbe); - } - - TryFinalizeTrack(sid, "molotov", (float)d.X, (float)d.Y, (float)d.Z); - - Enqueue(new - { - type = "nade", - sub = "detonate", - nade = "molotov", - t = NowMs(), - steamId = sid, - name = p?.PlayerName ?? "", - pos = new { x = (float)d.X, y = (float)d.Y, z = (float)d.Z } - }); - - if (!airburst) - { - var id = Interlocked.Increment(ref _seq); - var endAt = DateTimeOffset.UtcNow.AddSeconds(_fireDurationSec); - var state = new AoeState { EndAt = endAt, X = (float)d.X, Y = (float)d.Y, Z = (float)d.Z }; - _activeFires[id] = state; - - Enqueue(new - { - type = "fire", - state = "start", - id, - t = NowMs(), - endAt = endAt.ToUnixTimeMilliseconds(), - pos = new { x = state.X, y = state.Y, z = state.Z } - }); - - var token = _serverCts?.Token ?? CancellationToken.None; - _ = Task.Run(async () => - { - try - { - await Task.Delay(TimeSpan.FromSeconds(_fireDurationSec), token); - if (_activeFires.TryRemove(id, out _)) - { - Enqueue(new { type = "fire", state = "end", id, t = NowMs() }); - } - } - catch { } - }, token); - } - } - catch { } - return HookResult.Continue; - } - - // Generischer Detonate-Helper für HE/Flash/Decoy (Einzel-Event bleibt) - private HookResult EnqueueNadeDet(GameEvent ev, string kind) - { - try - { - dynamic d = ev; - var p = d.Userid as CCSPlayerController; - - Enqueue(new - { - type = "nade", - sub = "detonate", - nade = kind, - t = NowMs(), - steamId = p?.AuthorizedSteamID?.SteamId64 ?? 0UL, - name = p?.PlayerName ?? "", - pos = new { x = (float)d.X, y = (float)d.Y, z = (float)d.Z } - }); - } - catch { } - return HookResult.Continue; - } - - private HookResult Enqueue(object obj) - { - if (!_enabled || !_serverRunning) return HookResult.Continue; - _outbox.Enqueue(JsonSerializer.Serialize(obj)); - _sendSignal.Set(); - return HookResult.Continue; - } - - private static bool LooksWrongPfxPassword(Exception ex) - { - try - { - var s = (ex?.Message ?? "").ToLowerInvariant(); - return s.Contains("password") || s.Contains("kennwort"); - } - catch { return false; } - } - - private bool TryLoadCertificate(out string usedPath) - { - usedPath = _certPath; - - try - { - string pluginDir = ModuleDirectory; - - // Pfad ermitteln - if (string.IsNullOrWhiteSpace(usedPath)) - { - var def = Path.Combine(pluginDir, "cert.pfx"); - if (File.Exists(def)) - { - usedPath = def; - } - else - { - var files = Directory.GetFiles(pluginDir, "*.pfx", SearchOption.TopDirectoryOnly); - if (files.Length > 0) usedPath = files[0]; - } - } - else if (!Path.IsPathRooted(usedPath)) - { - usedPath = Path.Combine(pluginDir, usedPath); - } - - if (string.IsNullOrWhiteSpace(usedPath) || !File.Exists(usedPath)) - { - Logger.LogWarning($"[WS] Kein PFX gefunden im Plugin-Ordner ({pluginDir}). Lege z.B. 'cert.pfx' dort ab oder setze mit css_ws_cert ."); - return false; - } - - try - { - _serverCert = new X509Certificate2( - usedPath, - string.IsNullOrEmpty(_certPassword) ? null : _certPassword, - X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable - ); - } - catch (CryptographicException cex) when (LooksWrongPfxPassword(cex)) - { - var pwdState = string.IsNullOrEmpty(_certPassword) ? "leer" : "gesetzt"; - Logger.LogError($"[WS] TLS-PFX konnte nicht geöffnet werden: vermutlich falsches Passwort. " + - $"Pfad: {usedPath}. CertPassword (aus config.json) ist {pwdState}. " + - $"Setze es mit 'css_ws_certpwd ' oder trage es in {ConfigFileName} unter 'CertPassword' ein."); - _serverCert = null; - return false; - } - catch (CryptographicException cex) - { - Logger.LogError($"[WS] TLS-PFX Fehler ({usedPath}): {cex.Message}"); - _serverCert = null; - return false; - } - - if (_serverCert == null) - return false; - - if (!_serverCert.HasPrivateKey) - { - Logger.LogError($"[WS] Zertifikat geladen, aber ohne Private Key: {Path.GetFileName(usedPath)}. " + - $"Bitte PFX mit privatem Schlüssel verwenden."); - _serverCert = null; - return false; - } - - try - { - Logger.LogInformation($"[WS] TLS-Zertifikat geladen: {Path.GetFileName(usedPath)} | Subject: {_serverCert.Subject} | Gültig bis: {_serverCert.NotAfter:u}"); - } - catch - { - Logger.LogInformation($"[WS] TLS-Zertifikat geladen: {Path.GetFileName(usedPath)}"); - } - - return true; - } - catch (Exception ex) - { - Logger.LogError($"[WS] Zertifikat konnte nicht geladen werden: {ex.Message}"); - _serverCert = null; - return false; - } - } // ========================= // WS(S)-Server / Broadcast @@ -1480,40 +861,6 @@ public class WebSocketTelemetryPlugin : BasePlugin { var nowMs = NowMs(); SendTextFrame(state, JsonSerializer.Serialize(new { type = "map", name = _mapName, t = nowMs })); - - foreach (var kv in _activeSmokes) - { - var aoe = kv.Value; - if (aoe.EndAt > DateTimeOffset.UtcNow) - { - SendTextFrame(state, JsonSerializer.Serialize(new - { - type = "smoke", - state = "start", - id = kv.Key, - t = nowMs, - endAt = aoe.EndAt.ToUnixTimeMilliseconds(), - pos = new { x = aoe.X, y = aoe.Y, z = aoe.Z } - })); - } - } - - foreach (var kv in _activeFires) - { - var aoe = kv.Value; - if (aoe.EndAt > DateTimeOffset.UtcNow) - { - SendTextFrame(state, JsonSerializer.Serialize(new - { - type = "fire", - state = "start", - id = kv.Key, - t = nowMs, - endAt = aoe.EndAt.ToUnixTimeMilliseconds(), - pos = new { x = aoe.X, y = aoe.Y, z = aoe.Z } - })); - } - } } catch { } @@ -1569,7 +916,7 @@ public class WebSocketTelemetryPlugin : BasePlugin return false; var firstLineEnd = header.IndexOf("\r\n", StringComparison.Ordinal); - var firstLine = firstLineEnd > 0 ? header[..firstLineEnd] : header; + var firstLine = firstLineEnd > 0 ? header[..firstLineEnd] : header; var parts = firstLine.Split(' '); if (parts.Length < 2) return false; @@ -1772,11 +1119,88 @@ public class WebSocketTelemetryPlugin : BasePlugin return Task.CompletedTask; } - // --- Hilfsklasse für Referenz-HashSet --- - private sealed class ReferenceEqualityComparer : IEqualityComparer + // --- TLS --- + private bool TryLoadCertificate(out string usedPath) { - public static readonly ReferenceEqualityComparer Instance = new(); - public new bool Equals(object x, object y) => ReferenceEquals(x, y); - public int GetHashCode(object obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); + usedPath = _certPath; + + try + { + string pluginDir = ModuleDirectory; + + if (string.IsNullOrWhiteSpace(usedPath)) + { + var def = Path.Combine(pluginDir, "cert.pfx"); + if (File.Exists(def)) + { + usedPath = def; + } + else + { + var files = Directory.GetFiles(pluginDir, "*.pfx", SearchOption.TopDirectoryOnly); + if (files.Length > 0) usedPath = files[0]; + } + } + else if (!Path.IsPathRooted(usedPath)) + { + usedPath = Path.Combine(pluginDir, usedPath); + } + + if (string.IsNullOrWhiteSpace(usedPath) || !File.Exists(usedPath)) + { + Logger.LogWarning($"[WS] Kein PFX gefunden im Plugin-Ordner ({pluginDir}). Lege z.B. 'cert.pfx' dort ab oder setze mit css_ws_cert ."); + return false; + } + + try + { + _serverCert = new X509Certificate2( + usedPath, + string.IsNullOrEmpty(_certPassword) ? null : _certPassword, + X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable + ); + } + catch (CryptographicException cex) when ((cex.Message ?? "").IndexOf("password", StringComparison.OrdinalIgnoreCase) >= 0 || + (cex.Message ?? "").IndexOf("kennwort", StringComparison.OrdinalIgnoreCase) >= 0) + { + var pwdState = string.IsNullOrEmpty(_certPassword) ? "leer" : "gesetzt"; + Logger.LogError($"[WS] TLS-PFX konnte nicht geöffnet werden: vermutlich falsches Passwort. Pfad: {usedPath}. CertPassword ist {pwdState}."); + _serverCert = null; + return false; + } + catch (CryptographicException cex) + { + Logger.LogError($"[WS] TLS-PFX Fehler ({usedPath}): {cex.Message}"); + _serverCert = null; + return false; + } + + if (_serverCert == null) + return false; + + if (!_serverCert.HasPrivateKey) + { + Logger.LogError($"[WS] Zertifikat geladen, aber ohne Private Key: {Path.GetFileName(usedPath)}. Bitte PFX mit privatem Schlüssel verwenden."); + _serverCert = null; + return false; + } + + try + { + Logger.LogInformation($"[WS] TLS-Zertifikat geladen: {Path.GetFileName(usedPath)} | Subject: {_serverCert.Subject} | Gültig bis: {_serverCert.NotAfter:u}"); + } + catch + { + Logger.LogInformation($"[WS] TLS-Zertifikat geladen: {Path.GetFileName(usedPath)}"); + } + + return true; + } + catch (Exception ex) + { + Logger.LogError($"[WS] Zertifikat konnte nicht geladen werden: {ex.Message}"); + _serverCert = null; + return false; + } } } diff --git a/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.dll b/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.dll index d25d40a..69a1e2e 100644 Binary files a/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.dll and b/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.dll differ diff --git a/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb b/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb index a7ed5fa..e441688 100644 Binary files a/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb and b/CS2WebSocketTelemetryPlugin/bin/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb differ diff --git a/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.dgspec.json b/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.dgspec.json index 8cf85b6..7cd2a72 100644 --- a/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.dgspec.json +++ b/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.dgspec.json @@ -1,31 +1,29 @@ { "format": 1, "restore": { - "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj": {} + "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj": {} }, "projects": { - "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj": { + "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj": { "version": "1.0.0", "restore": { - "projectUniqueName": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", + "projectUniqueName": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", "projectName": "CS2WebSocketTelemetryPlugin", - "projectPath": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", - "packagesPath": "C:\\Users\\Rother\\.nuget\\packages\\", - "outputPath": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\obj\\", + "projectPath": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", + "packagesPath": "C:\\Users\\Chris\\.nuget\\packages\\", + "outputPath": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\obj\\", "projectStyle": "PackageReference", "fallbackFolders": [ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" ], "configFilePaths": [ - "C:\\Users\\Rother\\AppData\\Roaming\\NuGet\\NuGet.Config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + "C:\\Users\\Chris\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config" ], "originalTargetFrameworks": [ "net8.0" ], "sources": { - "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "https://api.nuget.org/v3/index.json": {} }, "frameworks": { @@ -43,8 +41,7 @@ "enableAudit": "true", "auditLevel": "low", "auditMode": "direct" - }, - "SdkAnalysisLevel": "9.0.300" + } }, "frameworks": { "net8.0": { @@ -71,7 +68,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.408/PortableRuntimeIdentifierGraph.json" } } } diff --git a/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.g.props b/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.g.props index 2b4790d..6fb2b23 100644 --- a/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.g.props +++ b/CS2WebSocketTelemetryPlugin/obj/CS2WebSocketTelemetryPlugin.csproj.nuget.g.props @@ -5,18 +5,18 @@ NuGet $(MSBuildThisFileDirectory)project.assets.json $(UserProfile)\.nuget\packages\ - C:\Users\Rother\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages + C:\Users\Chris\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.14.0 + 6.11.1 - + - C:\Users\Rother\.nuget\packages\microsoft.dotnet.apicompat.task\8.0.203 + C:\Users\Chris\.nuget\packages\microsoft.dotnet.apicompat.task\8.0.203 \ No newline at end of file diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfo.cs b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfo.cs index 0f606c7..1d2c16f 100644 --- a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfo.cs +++ b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("CS2WebSocketTelemetryPlugin")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+612f8d5be47e3677a339fa180b79358a0e275fa9")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+43a13a88f47d899ca8efcc8a2093f5bd4191cd48")] [assembly: System.Reflection.AssemblyProductAttribute("CS2WebSocketTelemetryPlugin")] [assembly: System.Reflection.AssemblyTitleAttribute("CS2WebSocketTelemetryPlugin")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfoInputs.cache b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfoInputs.cache index d932ffe..f1b150e 100644 --- a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfoInputs.cache +++ b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.AssemblyInfoInputs.cache @@ -1 +1 @@ -4806324ce3160c37004a41ae85c8c6e06de1aaed85bc7956669d94d6c6918040 +0c1151723728a5334c4514e839cde655d6f00feaafd51bea16e71c52c6659b99 diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.GeneratedMSBuildEditorConfig.editorconfig b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.GeneratedMSBuildEditorConfig.editorconfig index e69493c..93b1b8b 100644 --- a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.GeneratedMSBuildEditorConfig.editorconfig +++ b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.GeneratedMSBuildEditorConfig.editorconfig @@ -8,8 +8,6 @@ build_property.PlatformNeutralAssembly = build_property.EnforceExtendedAnalyzerRules = build_property._SupportedPlatformList = Linux,macOS,Windows build_property.RootNamespace = CS2WebSocketTelemetryPlugin -build_property.ProjectDir = C:\Users\Rother\fork\ironie-cs2-websocket-plugin\CS2WebSocketTelemetryPlugin\ +build_property.ProjectDir = C:\Users\Chris\fork\ironie-cs2-websocket-plugin\CS2WebSocketTelemetryPlugin\ build_property.EnableComHosting = build_property.EnableGeneratedComInterfaceComImportInterop = -build_property.EffectiveAnalysisLevelStyle = 8.0 -build_property.EnableCodeStyleSeverity = diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.assets.cache b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.assets.cache index 0899760..0f560c2 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.assets.cache and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.assets.cache differ diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.AssemblyReference.cache b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.AssemblyReference.cache index e0463c3..08abe09 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.AssemblyReference.cache and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.AssemblyReference.cache differ diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.CoreCompileInputs.cache b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.CoreCompileInputs.cache index b6e5578..c35142f 100644 --- a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.CoreCompileInputs.cache +++ b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -db8c6dfe795ff7d85d430dc00d7bc0af9be5c091309a903f5252e5c7475b2471 +29a097f847e9cc9046a216cfa5e6dfc719414ee5e42dc15f3ee740e9754a5ad0 diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.dll b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.dll index d25d40a..69a1e2e 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.dll and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.dll differ diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb index a7ed5fa..e441688 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/CS2WebSocketTelemetryPlugin.pdb differ diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/ref/CS2WebSocketTelemetryPlugin.dll b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/ref/CS2WebSocketTelemetryPlugin.dll index 6c5d888..748acab 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/ref/CS2WebSocketTelemetryPlugin.dll and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/ref/CS2WebSocketTelemetryPlugin.dll differ diff --git a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/refint/CS2WebSocketTelemetryPlugin.dll b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/refint/CS2WebSocketTelemetryPlugin.dll index 6c5d888..748acab 100644 Binary files a/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/refint/CS2WebSocketTelemetryPlugin.dll and b/CS2WebSocketTelemetryPlugin/obj/Release/net8.0/refint/CS2WebSocketTelemetryPlugin.dll differ diff --git a/CS2WebSocketTelemetryPlugin/obj/project.assets.json b/CS2WebSocketTelemetryPlugin/obj/project.assets.json index 97fb105..7b637c3 100644 --- a/CS2WebSocketTelemetryPlugin/obj/project.assets.json +++ b/CS2WebSocketTelemetryPlugin/obj/project.assets.json @@ -2555,31 +2555,29 @@ ] }, "packageFolders": { - "C:\\Users\\Rother\\.nuget\\packages\\": {}, + "C:\\Users\\Chris\\.nuget\\packages\\": {}, "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} }, "project": { "version": "1.0.0", "restore": { - "projectUniqueName": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", + "projectUniqueName": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", "projectName": "CS2WebSocketTelemetryPlugin", - "projectPath": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", - "packagesPath": "C:\\Users\\Rother\\.nuget\\packages\\", - "outputPath": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\obj\\", + "projectPath": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", + "packagesPath": "C:\\Users\\Chris\\.nuget\\packages\\", + "outputPath": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\obj\\", "projectStyle": "PackageReference", "fallbackFolders": [ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" ], "configFilePaths": [ - "C:\\Users\\Rother\\AppData\\Roaming\\NuGet\\NuGet.Config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + "C:\\Users\\Chris\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config" ], "originalTargetFrameworks": [ "net8.0" ], "sources": { - "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "https://api.nuget.org/v3/index.json": {} }, "frameworks": { @@ -2597,8 +2595,7 @@ "enableAudit": "true", "auditLevel": "low", "auditMode": "direct" - }, - "SdkAnalysisLevel": "9.0.300" + } }, "frameworks": { "net8.0": { @@ -2625,7 +2622,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.408/PortableRuntimeIdentifierGraph.json" } } } diff --git a/CS2WebSocketTelemetryPlugin/obj/project.nuget.cache b/CS2WebSocketTelemetryPlugin/obj/project.nuget.cache index 13cfac9..3e562ed 100644 --- a/CS2WebSocketTelemetryPlugin/obj/project.nuget.cache +++ b/CS2WebSocketTelemetryPlugin/obj/project.nuget.cache @@ -1,57 +1,57 @@ { "version": 2, - "dgSpecHash": "6FH32YrwWIs=", + "dgSpecHash": "ZqU4AlfU0L4=", "success": true, - "projectFilePath": "C:\\Users\\Rother\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", + "projectFilePath": "C:\\Users\\Chris\\fork\\ironie-cs2-websocket-plugin\\CS2WebSocketTelemetryPlugin\\CS2WebSocketTelemetryPlugin.csproj", "expectedPackageFiles": [ - "C:\\Users\\Rother\\.nuget\\packages\\counterstrikesharp.api\\1.0.336\\counterstrikesharp.api.1.0.336.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\mcmaster.netcore.plugins\\1.4.0\\mcmaster.netcore.plugins.1.4.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.csharp\\4.7.0\\microsoft.csharp.4.7.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.dotnet.apicompat.task\\8.0.203\\microsoft.dotnet.apicompat.task.8.0.203.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\3.1.6\\microsoft.dotnet.platformabstractions.3.1.6.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration\\8.0.0\\microsoft.extensions.configuration.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\8.0.0\\microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.binder\\8.0.0\\microsoft.extensions.configuration.binder.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.commandline\\8.0.0\\microsoft.extensions.configuration.commandline.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.environmentvariables\\8.0.0\\microsoft.extensions.configuration.environmentvariables.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.fileextensions\\8.0.0\\microsoft.extensions.configuration.fileextensions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.json\\8.0.0\\microsoft.extensions.configuration.json.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.configuration.usersecrets\\8.0.0\\microsoft.extensions.configuration.usersecrets.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\8.0.0\\microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\8.0.0\\microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.dependencymodel\\6.0.0\\microsoft.extensions.dependencymodel.6.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.diagnostics\\8.0.0\\microsoft.extensions.diagnostics.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.diagnostics.abstractions\\8.0.0\\microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.fileproviders.abstractions\\8.0.0\\microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.fileproviders.physical\\8.0.0\\microsoft.extensions.fileproviders.physical.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.filesystemglobbing\\8.0.0\\microsoft.extensions.filesystemglobbing.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.hosting\\8.0.0\\microsoft.extensions.hosting.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.hosting.abstractions\\8.0.0\\microsoft.extensions.hosting.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.localization.abstractions\\8.0.3\\microsoft.extensions.localization.abstractions.8.0.3.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging\\8.0.0\\microsoft.extensions.logging.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\8.0.0\\microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.configuration\\8.0.0\\microsoft.extensions.logging.configuration.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.console\\8.0.0\\microsoft.extensions.logging.console.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.debug\\8.0.0\\microsoft.extensions.logging.debug.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.eventlog\\8.0.0\\microsoft.extensions.logging.eventlog.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.logging.eventsource\\8.0.0\\microsoft.extensions.logging.eventsource.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.options\\8.0.0\\microsoft.extensions.options.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.options.configurationextensions\\8.0.0\\microsoft.extensions.options.configurationextensions.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\microsoft.extensions.primitives\\8.0.0\\microsoft.extensions.primitives.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\scrutor\\4.2.2\\scrutor.4.2.2.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\serilog\\3.1.1\\serilog.3.1.1.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\serilog.extensions.logging\\8.0.0\\serilog.extensions.logging.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\serilog.sinks.console\\5.0.0\\serilog.sinks.console.5.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\serilog.sinks.file\\5.0.0\\serilog.sinks.file.5.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.buffers\\4.5.1\\system.buffers.4.5.1.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.data.datasetextensions\\4.5.0\\system.data.datasetextensions.4.5.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.diagnostics.diagnosticsource\\8.0.0\\system.diagnostics.diagnosticsource.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.diagnostics.eventlog\\8.0.0\\system.diagnostics.eventlog.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.memory\\4.5.4\\system.memory.4.5.4.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.text.encodings.web\\8.0.0\\system.text.encodings.web.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\system.text.json\\8.0.0\\system.text.json.8.0.0.nupkg.sha512", - "C:\\Users\\Rother\\.nuget\\packages\\tomlyn\\0.19.0\\tomlyn.0.19.0.nupkg.sha512" + "C:\\Users\\Chris\\.nuget\\packages\\counterstrikesharp.api\\1.0.336\\counterstrikesharp.api.1.0.336.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\mcmaster.netcore.plugins\\1.4.0\\mcmaster.netcore.plugins.1.4.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.csharp\\4.7.0\\microsoft.csharp.4.7.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.dotnet.apicompat.task\\8.0.203\\microsoft.dotnet.apicompat.task.8.0.203.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\3.1.6\\microsoft.dotnet.platformabstractions.3.1.6.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration\\8.0.0\\microsoft.extensions.configuration.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\8.0.0\\microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.binder\\8.0.0\\microsoft.extensions.configuration.binder.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.commandline\\8.0.0\\microsoft.extensions.configuration.commandline.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.environmentvariables\\8.0.0\\microsoft.extensions.configuration.environmentvariables.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.fileextensions\\8.0.0\\microsoft.extensions.configuration.fileextensions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.json\\8.0.0\\microsoft.extensions.configuration.json.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.configuration.usersecrets\\8.0.0\\microsoft.extensions.configuration.usersecrets.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\8.0.0\\microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\8.0.0\\microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.dependencymodel\\6.0.0\\microsoft.extensions.dependencymodel.6.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.diagnostics\\8.0.0\\microsoft.extensions.diagnostics.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.diagnostics.abstractions\\8.0.0\\microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.fileproviders.abstractions\\8.0.0\\microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.fileproviders.physical\\8.0.0\\microsoft.extensions.fileproviders.physical.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.filesystemglobbing\\8.0.0\\microsoft.extensions.filesystemglobbing.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.hosting\\8.0.0\\microsoft.extensions.hosting.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.hosting.abstractions\\8.0.0\\microsoft.extensions.hosting.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.localization.abstractions\\8.0.3\\microsoft.extensions.localization.abstractions.8.0.3.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging\\8.0.0\\microsoft.extensions.logging.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\8.0.0\\microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.configuration\\8.0.0\\microsoft.extensions.logging.configuration.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.console\\8.0.0\\microsoft.extensions.logging.console.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.debug\\8.0.0\\microsoft.extensions.logging.debug.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.eventlog\\8.0.0\\microsoft.extensions.logging.eventlog.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.logging.eventsource\\8.0.0\\microsoft.extensions.logging.eventsource.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.options\\8.0.0\\microsoft.extensions.options.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.options.configurationextensions\\8.0.0\\microsoft.extensions.options.configurationextensions.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\microsoft.extensions.primitives\\8.0.0\\microsoft.extensions.primitives.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\scrutor\\4.2.2\\scrutor.4.2.2.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\serilog\\3.1.1\\serilog.3.1.1.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\serilog.extensions.logging\\8.0.0\\serilog.extensions.logging.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\serilog.sinks.console\\5.0.0\\serilog.sinks.console.5.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\serilog.sinks.file\\5.0.0\\serilog.sinks.file.5.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.buffers\\4.5.1\\system.buffers.4.5.1.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.data.datasetextensions\\4.5.0\\system.data.datasetextensions.4.5.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.diagnostics.diagnosticsource\\8.0.0\\system.diagnostics.diagnosticsource.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.diagnostics.eventlog\\8.0.0\\system.diagnostics.eventlog.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.memory\\4.5.4\\system.memory.4.5.4.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.text.encodings.web\\8.0.0\\system.text.encodings.web.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\system.text.json\\8.0.0\\system.text.json.8.0.0.nupkg.sha512", + "C:\\Users\\Chris\\.nuget\\packages\\tomlyn\\0.19.0\\tomlyn.0.19.0.nupkg.sha512" ], "logs": [] } \ No newline at end of file