diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj
index fcb5186..6bdba56 100644
--- a/MosswartMassacre/MosswartMassacre.csproj
+++ b/MosswartMassacre/MosswartMassacre.csproj
@@ -10,6 +10,7 @@
MosswartMassacre
MosswartMassacre
v4.8
+ 8.0
512
true
diff --git a/MosswartMassacre/Telemetry.cs b/MosswartMassacre/Telemetry.cs
index dc57ec3..7facf99 100644
--- a/MosswartMassacre/Telemetry.cs
+++ b/MosswartMassacre/Telemetry.cs
@@ -1,110 +1,107 @@
-using System;
+// Telemetry.cs ───────────────────────────────────────────────────────────────
+using System;
using System.Net.Http;
using System.Text;
-using System.Timers;
+using System.Threading;
+using System.Threading.Tasks;
using Decal.Adapter;
using Newtonsoft.Json;
namespace MosswartMassacre
{
- ///
- /// Periodically sends gameplay telemetry to your FastAPI collector.
- /// Toggle with: Telemetry.Start() / Telemetry.Stop()
- ///
public static class Telemetry
{
- /* ============ CONFIG ============ */
-
- private const string Endpoint = "https://mosswart.snakedesert.se/position";
- private const string SharedSecret = "your_shared_secret";
- private const int IntervalSec = 5; // send every 5 s
-
- /* ============ internals ========== */
+ /* ───────────── configuration ───────────── */
+ private const string Endpoint = "https://mosswart.snakedesert.se/position/"; // <- trailing slash!
+ private const string SharedSecret = "your_shared_secret"; // <- keep in sync
+ private const int IntervalSec = 5; // seconds between posts
+ /* ───────────── runtime state ───────────── */
private static readonly HttpClient _http = new HttpClient();
- private static Timer _timer;
- private static bool _enabled;
private static string _sessionId;
+ private static CancellationTokenSource _cts;
+ private static bool _enabled;
- /* ============ public API ========= */
-
+ /* ───────────── public API ───────────── */
public static void Start()
{
- if (_enabled) return; // already on
-
- _sessionId = $"{CoreManager.Current.CharacterFilter.Name}-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
- _timer = new Timer(IntervalSec * 1000);
- _timer.Elapsed += (_, __) => SendSnapshot();
- _timer.Start();
+ if (_enabled) return;
_enabled = true;
+ _sessionId = $"{CoreManager.Current.CharacterFilter.Name}-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
+ _cts = new CancellationTokenSource();
+
PluginCore.WriteToChat("[Telemetry] HTTP streaming ENABLED");
- PluginCore.WriteToChat("[Tel] timer every " + IntervalSec + " s");
+
+ _ = Task.Run(() => LoopAsync(_cts.Token)); // fire-and-forget
}
public static void Stop()
{
if (!_enabled) return;
-
+ _cts.Cancel();
_enabled = false;
- _timer?.Stop();
- _timer?.Dispose();
- _timer = null;
-
PluginCore.WriteToChat("[Telemetry] HTTP streaming DISABLED");
}
- /* ============ snapshot builder === */
-
- private static async void SendSnapshot()
+ /* ───────────── async loop ───────────── */
+ private static async Task LoopAsync(CancellationToken token)
{
- try
+ while (!token.IsCancellationRequested)
{
- var coords = Coordinates.Me;
-
- var payload = new
+ try
{
- character_name = CoreManager.Current.CharacterFilter.Name,
- char_tag = PluginCore.CharTag,
- session_id = _sessionId,
- timestamp = DateTime.UtcNow.ToString("o"),
-
- ew = coords.EW,
- ns = coords.NS,
- z = coords.Z,
-
- kills = PluginCore.totalKills,
- deaths = 0,
- rares_found = PluginCore.rareCount,
- prismatic_taper_count = 0,
- vt_state = "Unknown"
- };
-
- string json = JsonConvert.SerializeObject(payload);
-
- var req = new HttpRequestMessage(HttpMethod.Post, Endpoint)
- {
- Content = new StringContent(json, Encoding.UTF8, "application/json")
- };
- req.Headers.Add("X-Plugin-Secret", SharedSecret);
-
- /* ---------- NEW: wait for response & print result ---------- */
- var resp = await _http.SendAsync(req);
- if (resp.IsSuccessStatusCode)
- {
- PluginCore.WriteToChat($"[Tel] ✓ {resp.StatusCode}");
+ await SendSnapshotAsync(token);
}
- else
+ catch (Exception ex)
{
- PluginCore.WriteToChat($"[Tel] ✗ {resp.StatusCode} ({await resp.Content.ReadAsStringAsync()})");
+ PluginCore.WriteToChat($"[Telemetry] send failed: {ex.Message}");
}
- }
- catch (Exception ex)
- {
- var inner = ex.InnerException?.Message ?? "no inner msg";
- PluginCore.WriteToChat($"[Tel] FAILED — {ex.GetType().Name}: {ex.Message} ⇢ {inner}");
+
+ try
+ {
+ await Task.Delay(TimeSpan.FromSeconds(IntervalSec), token);
+ }
+ catch (TaskCanceledException) { } // expected on Stop()
}
}
+ /* ───────────── single POST ───────────── */
+ private static async Task SendSnapshotAsync(CancellationToken token)
+ {
+ var coords = Coordinates.Me;
+
+ var payload = new
+ {
+ character_name = CoreManager.Current.CharacterFilter.Name,
+ char_tag = PluginCore.CharTag,
+ session_id = _sessionId,
+ timestamp = DateTime.UtcNow.ToString("o"),
+
+ ew = coords.EW,
+ ns = coords.NS,
+ z = coords.Z,
+
+ kills = PluginCore.totalKills,
+ deaths = 0,
+ rares_found = PluginCore.rareCount,
+ prismatic_taper_count = 0,
+ vt_state = "Unknown"
+ };
+
+ string json = JsonConvert.SerializeObject(payload);
+ var req = new HttpRequestMessage(HttpMethod.Post, Endpoint)
+ {
+ Content = new StringContent(json, Encoding.UTF8, "application/json")
+ };
+ req.Headers.Add("X-Plugin-Secret", SharedSecret);
+
+ using var resp = await _http.SendAsync(req, token);
+
+ if (!resp.IsSuccessStatusCode) // stay quiet on success
+ {
+ PluginCore.WriteToChat($"[Telemetry] server replied {resp.StatusCode}");
+ }
+ }
}
}