F1: the telemetry session id is new at every login. The original made one per load of its assembly and its loader reloaded it at every login; the backend counts new kills per (session id, character), so reusing one id across a relog under-counted kills until the reset counter passed the old total. F5/F6: .NET Framework never wrote a negative zero, and its fixed-point formats (F7, F2, F0) rounded at fifteen significant digits, half away from zero. Spawn and portal coordinates, kills_per_hour and the !report line now format that way; the round-trip converter maps -0 to 0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
84 lines
2.7 KiB
C#
84 lines
2.7 KiB
C#
using System.Globalization;
|
|
using OpenAC.MosswartMassacre.Backend;
|
|
using OpenAC.MosswartMassacre.Infrastructure;
|
|
|
|
namespace OpenAC.MosswartMassacre.Streams;
|
|
|
|
/// <summary>
|
|
/// The <c>telemetry</c> frame: sent right after the socket registers and then
|
|
/// every five seconds, as the original's socket loop did.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Several fields are strings on the wire (kill rate, deaths, taper count) and
|
|
/// <c>mem_mb</c> carries bytes; the backend reads them that way.
|
|
/// </remarks>
|
|
internal sealed class TelemetryStream : IMmFeature, IBackendConnectionListener
|
|
{
|
|
public static readonly TimeSpan Interval = TimeSpan.FromSeconds(5);
|
|
|
|
private readonly MmContext _context;
|
|
private readonly SessionStats _stats;
|
|
private readonly ProcessTelemetry _process = new();
|
|
private IDisposable? _timer;
|
|
|
|
public TelemetryStream(MmContext context, SessionStats stats)
|
|
{
|
|
_context = context;
|
|
_stats = stats;
|
|
}
|
|
|
|
public void OnBackendConnected()
|
|
{
|
|
if (!_context.IsLoggedIn)
|
|
return;
|
|
SendNow();
|
|
_timer?.Dispose();
|
|
_timer = _context.Scheduler.Every(Interval, SendNow);
|
|
}
|
|
|
|
public void OnLogin()
|
|
{
|
|
}
|
|
|
|
public void OnLogoff()
|
|
{
|
|
_timer?.Dispose();
|
|
_timer = null;
|
|
}
|
|
|
|
public void Dispose() => OnLogoff();
|
|
|
|
internal void SendNow()
|
|
{
|
|
if (!_context.IsLoggedIn)
|
|
return;
|
|
_context.Send(Build());
|
|
}
|
|
|
|
internal object Build()
|
|
{
|
|
MmCoordinates coords = MmCoordinates.From(_context.Automation.Navigation.Snapshot.Position);
|
|
var kills = _stats.Kills;
|
|
return new
|
|
{
|
|
type = "telemetry",
|
|
character_name = _context.CharacterName,
|
|
char_tag = _context.Settings.Current.CharTag ?? "",
|
|
session_id = _context.TelemetrySessionId,
|
|
timestamp = Wire.Timestamp(_context.Clock),
|
|
ew = coords.EW,
|
|
ns = coords.NS,
|
|
z = coords.Z,
|
|
kills = kills.TotalKills,
|
|
kills_per_hour = FrameworkNumberConverter.FrameworkFixed(kills.KillsPerHour, "F0", CultureInfo.CurrentCulture),
|
|
onlinetime = (_context.Clock.Now - kills.StatsStartTime).ToString(@"dd\.hh\:mm\:ss", CultureInfo.InvariantCulture),
|
|
deaths = kills.SessionDeaths.ToString(CultureInfo.CurrentCulture),
|
|
total_deaths = kills.TotalDeaths.ToString(CultureInfo.CurrentCulture),
|
|
prismatic_taper_count = _context.PrismaticTaperCount().ToString(CultureInfo.CurrentCulture),
|
|
vt_state = _context.MetaState() ?? string.Empty,
|
|
mem_mb = _process.MemoryBytes,
|
|
cpu_pct = _process.SampleCpuUsage(),
|
|
mem_handles = _process.HandleCount,
|
|
};
|
|
}
|
|
}
|