feat(dashboard): client system info, as reported by the client

Account pages get a Client system info (as reported) card (client_sysinfo):
every description the account's client sent at login, newest first, with the
raw values, the memory text split into numbers and each field's caveat. The
address is only shown with logs_audit. The Client System Info page (Logs &
Health) shows the spread across players from each account's newest report:
Windows version, video card, memory buckets, processor count and client build,
marked as client-reported and approximate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 19:07:46 -05:00
parent 05a99cef82
commit c44af311ad
12 changed files with 540 additions and 2 deletions

View File

@@ -157,6 +157,18 @@
</div>
{% endif %}
{% if can.client_sysinfo %}
<div class="card mb-4" id="sysinfoCard" data-account="{{ account.id }}">
<div class="card-header"><h5 class="mb-0">Client system info (as reported) <span class="badge rounded-pill text-bg-secondary align-middle" id="sysinfoCount"></span></h5></div>
<div class="card-body">
<p class="small text-body-secondary mb-2">What this account's game client said about its system at each login, exactly as sent. The client asks old Windows
functions from a 32-bit program, so several values are compatibility defaults, not the player's real hardware; each field says how far it can be trusted.
A new entry starts when anything but the memory in use changes.{% if not can.logs_audit %} Addresses are only shown with logs_audit.{% endif %}</p>
<div id="sysinfoList"><p class="text-body-secondary mb-0">Loading&hellip;</p></div>
</div>
</div>
{% endif %}
{% if can.accounts_notes %}
<div class="card mb-4" id="historyCard">
<div class="card-header d-flex justify-content-between align-items-center"><h5 class="mb-0">Moderation history</h5><span class="small text-body-secondary" id="warningCount"></span></div>
@@ -701,6 +713,7 @@ function disconnectMailAccount() {
<script src="/js/related.js"></script>
<script src="/js/strikes.js"></script>
<script src="/js/account-links.js"></script>
<script src="/js/client-sysinfo.js"></script>
<script src="/js/api-keys.js"></script>
<script src="/js/grants.js"></script>
<script>

View File

@@ -0,0 +1,68 @@
{% extends "base.jinja2" %}
{% block title %}Client System Info - DarkflameServer{% endblock %}
{% block content %}
<div id="sysinfoPage">
<h2 class="mb-1">Client System Info</h2>
<div class="alert alert-warning small" role="note">
<strong>As reported by the client, approximate.</strong> The game client describes its system with old Windows functions called from a 32-bit program
with no compatibility manifest. Windows answers with compatibility values (8.1, 10 and 11 all say 6.2 build 9200; the processor type is always 586),
and under Wine or Proton the answers are whatever Wine reports. Treat this as what clients claim, not as players' real hardware.
</div>
<p class="text-body-secondary">From each account's newest report (<span id="sysinfoAccounts">&hellip;</span> accounts), kept for <code>log_client_sysinfo_days</code>.
Each account's own history is on its account page.</p>
<div class="row g-4">
<div class="col-lg-6">
<div class="card h-100" id="spread-os">
<div class="card-header"><h5 class="mb-0">Windows version reported</h5></div>
<div class="card-body">
<p class="small text-body-secondary" data-caveat></p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0"><tbody><tr><td class="text-body-secondary">Loading&hellip;</td></tr></tbody></table></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100" id="spread-video">
<div class="card-header"><h5 class="mb-0">Video card (Direct3D adapter)</h5></div>
<div class="card-body">
<p class="small text-body-secondary" data-caveat></p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0"><tbody><tr><td class="text-body-secondary">Loading&hellip;</td></tr></tbody></table></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100" id="spread-memory">
<div class="card-header"><h5 class="mb-0">Physical memory reported</h5></div>
<div class="card-body">
<p class="small text-body-secondary" data-caveat></p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0"><tbody><tr><td class="text-body-secondary">Loading&hellip;</td></tr></tbody></table></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100" id="spread-processors">
<div class="card-header"><h5 class="mb-0">Logical processors reported</h5></div>
<div class="card-body">
<p class="small text-body-secondary" data-caveat></p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0"><tbody><tr><td class="text-body-secondary">Loading&hellip;</td></tr></tbody></table></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100" id="spread-clientOs">
<div class="card-header"><h5 class="mb-0">Client build</h5></div>
<div class="card-body">
<p class="small text-body-secondary" data-caveat></p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0"><tbody><tr><td class="text-body-secondary">Loading&hellip;</td></tr></tbody></table></div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="/js/client-sysinfo.js"></script>
{% endblock %}

View File

@@ -105,8 +105,8 @@
</div>
{% endif %}
{% set logsPages = ["health", "instances", "diagnostics", "network", "activity_log", "command_log", "audit_log", "system_log"] %}
{% if can.health_view or can.logs_activity or can.logs_command or can.logs_audit or can.logs_system %}
{% set logsPages = ["health", "instances", "diagnostics", "network", "activity_log", "command_log", "audit_log", "system_log", "client_sysinfo"] %}
{% if can.health_view or can.logs_activity or can.logs_command or can.logs_audit or can.logs_system or can.client_sysinfo %}
<div class="nav-group">
<button class="nav-group-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#nav-logs" aria-expanded="{% if current_page in logsPages %}true{% else %}false{% endif %}" aria-controls="nav-logs">
Logs &amp; Health<span class="nav-group-dot" title="Something in here needs attention"></span>
@@ -117,6 +117,7 @@
{% if can.logs_activity %}<a href="/activity_log" class="list-group-item list-group-item-action{% if current_page == "activity_log" %} active{% endif %}">Activity Log</a>{% endif %}
{% if can.logs_command %}<a href="/command_log" class="list-group-item list-group-item-action{% if current_page == "command_log" %} active{% endif %}">Command Log</a>{% endif %}
{% if can.logs_audit %}<a href="/audit_log" class="list-group-item list-group-item-action{% if current_page == "audit_log" %} active{% endif %}">Audit Log</a>{% endif %}
{% if can.client_sysinfo %}<a href="/client_sysinfo" class="list-group-item list-group-item-action{% if current_page == "client_sysinfo" %} active{% endif %}">Client System Info</a>{% endif %}
{% if can.logs_system %}<a href="/system_log" class="list-group-item list-group-item-action{% if current_page == "system_log" %} active{% endif %}">System Log</a>{% endif %}
</div>
</div>