#!/usr/bin/env python3 """Does `deployed` climb in the first seconds of flight? If the route table's times are FRAMES at 30 Hz, Stage 02's phase-1 entries at t = 90/120/170/210/240 are 3-8 seconds, so every arrival happens before the normal probe's first sample (~25 s in, after enumeration and calibration). This samples from the very first moment instead, with the expensive setup deferred: * no witness calibration up front -- it costs two 32 MB reads; * no per-record labelling -- names are not needed to count deployed records; * `deployed` = distinct roster records referenced by a live craft, which needs one heap scan (bytes.find, ~seconds) plus ~300 small preads. Frames reading -> deployed climbs during the first ~10 s, then is flat. Seconds reading -> deployed is flat early and climbs at 164 s+ of wall-clock. """ import os, sys, time, struct, collections sys.path.insert(0, __file__.rsplit('/', 1)[0]) import gmem, gworld, entities2 ROSTER_VT = struct.pack('>I', 0x820AF030) DELTA, WIN, LINK, HULL = 0x130, 0x400, 0x08, 0x154 def scan_vt(fd, size, vt): out = [] for a, b in gmem.extents(fd, size): pos = a while pos < b: m = min(1 << 24, b - pos) blob = os.pread(fd, m, pos) i = blob.find(vt) while i != -1: if (pos + i) % 4 == 0: out.append(pos + i) i = blob.find(vt, i + 1) pos += m return sorted(out) def deployed(fd, defs, want): lo, hi = gmem.va_to_off(entities2.ENT_VA_LO), gmem.va_to_off(entities2.ENT_VA_HI) per = collections.Counter(); tot = 0; pos = lo while pos < hi: m = min(1 << 24, hi - pos) blob = os.pread(fd, m, pos) for needle in defs: i = blob.find(needle) while i != -1: if (pos + i) % 4 == 0: base = pos + i - DELTA tot += 1 try: alive = struct.unpack('>f', os.pread(fd, 4, base + HULL))[0] > 0 except Exception: alive = False if alive: head = os.pread(fd, WIN, base) for j in range(0, len(head) - 3, 4): (p,) = struct.unpack_from('>I', head, j) if p in want: per[want[p]] += 1; break i = blob.find(needle, i + 1) pos += m return len(per), tot, per def main(): args = [a for a in sys.argv[1:] if not a.startswith('--')] fast_for = int(args[0]) if len(args) > 0 else 60 total = int(args[1]) if len(args) > 1 else 240 wait = '--wait' in sys.argv w = gworld.World(); fd = w.fd if wait: # Flight DETECTION lags the mission start, so even a sample at # flight+0.8 s may be several seconds of game time late -- and under the # frames reading the entire phase-1 schedule is 8 s long. Start before # the mission exists and poll for the roster to appear instead. print('waiting for a mission to load...', flush=True) while True: r = scan_vt(fd, w.size, ROSTER_VT) if len(r) >= 30: print('roster appeared: %d records' % len(r), flush=True); break time.sleep(0.5) t0 = time.time() defs = entities2.definitions(w) roster = scan_vt(fd, w.size, ROSTER_VT) want = {} for o in roster: va = gmem.primary_va(o) if va is not None: want[va + LINK] = o print('setup done at +%.1fs: %d roster records, %d definitions' % (time.time() - t0, len(roster), len(defs)), flush=True) if not roster or not defs: print('NOT IN A MISSION'); return 2 series = [] while time.time() - t0 < total: el = time.time() - t0 d, tot, per = deployed(fd, defs, want) series.append((round(el, 1), d, tot)) print(' +%6.1fs deployed=%3d craft=%3d strengths %s' % (el, d, tot, sorted(collections.Counter(per.values()).items())), flush=True) time.sleep(1 if time.time() - t0 < fast_for else 15) d0 = series[0][1]; dmax = max(s[1] for s in series) print('\nfirst deployed=%d max deployed=%d climb=%d' % (d0, dmax, dmax - d0)) print('verdict: %s' % ('DEPLOYED CLIMBED -- consistent with arrivals happening' if dmax > d0 else 'flat -- no arrival observed in this window')) return 0 if __name__ == '__main__': sys.exit(main())