feat: log the game's high-score report as a gameplay event
When the game builds its high-score table report for Stern's agent (/api/v1/game/high_score_events - the endpoint the Insider gate already sits on), the logger writes an HSTD line with all four players' scores. This is the report the game sends when a game has changed the table, so the line marks a high-score event with no new per-game RE. The entries themselves (initials, position) live deeper in the report body and stay uncaptured for now - that needs locating the report builder or the in-memory table. Tests: the endpoint trigger and the HSTD line lift into the host harness (the endpoint pointer is a 32-bit register, so the harness maps its string below 4 GiB and the tests run on the Linux leg like the port-gate ones). Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ why, and the game runs stock.
|
||||
| Stock modes (`gamelog: MODE START/END`): the game's own 22 modes with score delta | **deployed on the machine, verified in play** (`mode_score` is approximate) |
|
||||
| Award screens (`AWARD id=…`): every award the game shows, extra-ball tracking | **deployed on the machine, verified in play** — except the extra-ball award id is not yet confirmed (below) |
|
||||
| Switch/button log (`/dump/switch.log`, `SW` lines with generated names) | **deployed on the machine, verified in play** (flippers on both edges; sites located with `portswitch.switch_source` and added to the port) |
|
||||
| High-score reports (`HSTD`): the game's own high-score table report, with all players' scores | **built, deploy pending** — fires when the game sends `/api/v1/game/high_score_events` to Stern's agent; the entries (initials) need new per-game RE (below) |
|
||||
|
||||
The machine currently runs the v3 build (events, shots, modes, awards, switch
|
||||
log). The v3 work needed more than a rebuild: the port had no `switch_edge` /
|
||||
@@ -90,6 +91,7 @@ reboots:
|
||||
[pad] gamelog: 2026-10-12T20:45:12.123Z MODE END 5 (Island Scene Escape the Swamp) player=1 score=... mode_score=... dur=...ms
|
||||
[GAMELOG] 2026-10-12T20:45:12.123Z AWARD EXTRA BALL (id=13) player=1 <- next ball logs as "ball N (EXTRA BALL)"
|
||||
[GAMELOG] 2026-10-12T20:45:12.123Z AWARD id=<n> player=1 score=... <- every award screen
|
||||
[GAMELOG] 2026-10-12T20:45:12.123Z HSTD high-score report - P1=95,421,190 P2=0 P3=0 P4=0
|
||||
```
|
||||
|
||||
Bus event ids (King Kong 0.97): `game_start`=0x4d, `ball_start`=0x25,
|
||||
|
||||
@@ -8671,6 +8671,21 @@ static void gl_switch_log(unsigned id, unsigned level, int hit, int playing)
|
||||
if (fd >= 0) { write(fd, line, len); close(fd); }
|
||||
}
|
||||
|
||||
/* GAMELOG: the game's own high-score report (HSTD_REPORT — the /api/v1/game/high_score_events
|
||||
* the agent gate sees being built): the players' scores as the game reports its table. */
|
||||
static void gl_hstd_report(void)
|
||||
{
|
||||
char tbuf[40], buf[128], one[32];
|
||||
unsigned p, n = 0;
|
||||
gl_rfc3339(tbuf, sizeof tbuf);
|
||||
buf[0] = 0;
|
||||
for (p = 1; p <= 4; p++) {
|
||||
pm_commas(one, sizeof one, pm_score(p));
|
||||
n += (unsigned)pm_snprintf(buf + n, sizeof buf - n, "%sP%u=%s", n ? " " : "", p, one);
|
||||
}
|
||||
pm_log("[GAMELOG] %s HSTD high-score report - %s", tbuf, buf);
|
||||
}
|
||||
|
||||
static void on_switch_edge(unsigned *r)
|
||||
{
|
||||
static volatile int said;
|
||||
@@ -8998,6 +9013,8 @@ static void on_agent_header(unsigned *r)
|
||||
const char *s = (const char *)(unsigned long)r[1];
|
||||
if (s && s[0] == '/') str_copy(insider_endpoint, sizeof insider_endpoint, s, INSIDER_ENDPOINT_MAX - 1);
|
||||
else insider_endpoint[0] = 0;
|
||||
if (insider_blocked(insider_endpoint) == 2) /* the high-score table report */
|
||||
gl_hstd_report();
|
||||
}
|
||||
|
||||
static int on_agent_begin(unsigned *r)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/bin/sh
|
||||
# log_shipper - tail /dump/*.log and push them to Loki (see shipper/README.md).
|
||||
# Config comes from the environment; defaults in the binary match the machine.
|
||||
# The shipper's own complaints land in /dump/logshipper.log (a few lines a boot).
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start-stop-daemon -S -b -m -p /run/logshipper.pid -x /usr/local/bin/logshipper
|
||||
start-stop-daemon -S -b -m -p /run/logshipper.pid -x /bin/sh \
|
||||
-- -c 'exec /usr/local/bin/logshipper >>/dump/logshipper.log 2>&1'
|
||||
;;
|
||||
stop)
|
||||
start-stop-daemon -K -p /run/logshipper.pid
|
||||
|
||||
@@ -406,3 +406,85 @@ def test_gl_switch_log_writes_the_ports_buttons(tmp_path):
|
||||
'2026-10-13T12:00:00.000Z SW 62 "RIGHT FLIPPER BUTTON" level=1 hit=1 game=1',
|
||||
'2026-10-13T12:00:00.000Z SW 36 "START BUTTON" level=0 hit=0 game=0',
|
||||
], text
|
||||
|
||||
|
||||
# ---- the high-score report event ------------------------------------------------------
|
||||
|
||||
_HSTD_PRELUDE = r"""
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
static void event_line(const char *s) { (void)s; }
|
||||
static void lamp_line(const char *s) { (void)s; }
|
||||
static void rule_line(const char *s) { (void)s; }
|
||||
static void port_line(const char *s) { (void)s; }
|
||||
static void say(const char *fmt, ...) { (void)fmt; }
|
||||
static void on_agent_header(unsigned *r);
|
||||
static void gl_rfc3339(char *buf, unsigned cap) { snprintf(buf, cap, "2026-10-22T23:59:01.123Z"); }
|
||||
int pm_snprintf(char *out, unsigned long cap, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n;
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(out, cap, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
void pm_log(const char *fmt, ...)
|
||||
{
|
||||
char b[256];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(b, sizeof b, fmt, ap);
|
||||
va_end(ap);
|
||||
printf("PMLOG %s\n", b);
|
||||
}
|
||||
static void pm_commas(char *out, unsigned cap, unsigned long long v)
|
||||
{
|
||||
snprintf(out, cap, "%llu", (unsigned long long)v);
|
||||
}
|
||||
static unsigned long long pm_score(unsigned player) { return player == 1 ? 137615960 : 0; }
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static unsigned r[4];
|
||||
/* the runtime reads the endpoint pointer as a 32-bit register: the path must live below 4 GiB */
|
||||
char *path = mmap((void *)0x08000000, 4096, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
|
||||
if (path == (char *)-1) { printf("mmap failed\n"); return 1; }
|
||||
snprintf(path, 4096, "%s", argv[1]);
|
||||
r[1] = (unsigned)(unsigned long)path;
|
||||
on_agent_header(r);
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def _hstd_source():
|
||||
src = _src()
|
||||
helpers = "\n".join(_lift(src, s) for s in (
|
||||
"static int str_eq(", "static void str_copy(", "static int hexval(", "static uint64_t number("))
|
||||
insider = _between(src, "#define INSIDER_ENDPOINT_MAX", "static void on_agent_header(unsigned *r)")
|
||||
fn = "\n".join(_lift(src, s) for s in (
|
||||
"static void gl_hstd_report(void)", "static void on_agent_header(unsigned *r)"))
|
||||
return _HSTD_PRELUDE + helpers + "\n" + insider + "\n" + fn
|
||||
|
||||
|
||||
def test_a_high_score_report_logs_an_hstd_event_with_the_scores(tmp_path):
|
||||
if sys.platform == "darwin":
|
||||
pytest.skip("the runtime's pointers are 32-bit registers; darwin arm64 maps nothing "
|
||||
"below 4 GiB - the Linux leg runs these")
|
||||
out = _host_run(tmp_path, _hstd_source(),
|
||||
args=("/api/v1/game/high_score_events",))
|
||||
assert "HSTD high-score report" in out, out
|
||||
assert "P1=137615960 P2=0 P3=0 P4=0" in out, out
|
||||
|
||||
|
||||
def test_other_agent_reports_log_no_hstd_event(tmp_path):
|
||||
if sys.platform == "darwin":
|
||||
pytest.skip("32-bit pointer registers: see the first HSTD test")
|
||||
for path in ("/api/v3/game/session_start", "/ingest/v1/game/game_events", "junk", ""):
|
||||
out = _host_run(tmp_path, _hstd_source(), args=(path,))
|
||||
assert "HSTD" not in out, "%s logged an HSTD event" % path
|
||||
|
||||
Reference in New Issue
Block a user