fifa17-recon: sweep auto-advance + the three-state oracle, live-proven

The oracle is three-valued, and all three fingerprints are now confirmed live
against a running client rather than read out of Ghidra:

  NAMED        our sentinel rating 7 survives and a real name appears. The id is
               real, and teamid/nation/leagueId come back FILLED by the game
               because we send them as zero.
  placeholder  rating 7 survives but the name is 'Jamal Blackman', team 0. The
               players-table row exists and is an empty slot. This is the trap:
               169193 does this and it was in VERIFIED_ASSET_IDS.
  MISS         rating 0x32, teamid 0x78d, nation 0xe, position 2, name ' '. That
               is the binary's miss-fill, byte for byte, and it is exactly the
               blank card photographed in a pack today.

Scale: 5000 candidates per response ingests cleanly; 20000 was served and then
silently not ingested (the map did not change at all), so the ceiling is between
them and auto chunks default to 5000.

Auto-advance: the client PAGES the club, so one visit yields several fetches.
'auto:lo-hi:step' hands out the next chunk per fetch. Item ids derive from the
candidate's offset in the WHOLE range, not its index in the chunk, so chunks never
collide and results accumulate across fetches for a single probe at the end.

sweep_collect.py accumulates into data/players.json and rejects the placeholder
name as a matter of course. Yield in 20000-24999 was 19 real ids per 5000, which
is why auto-advance matters: the real roster clusters in 150000-240000.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
This commit is contained in:
funman300
2026-08-04 20:44:02 -07:00
parent 87e5cd2e53
commit b0bbc2a07f
21 changed files with 230413 additions and 35 deletions
+64 -13
View File
@@ -1297,23 +1297,74 @@ def sweep_window():
return _ID_SWEEP
def sweep_items():
"""Synthetic club contents for one id-sweep window."""
win = sweep_window()
# Auto-advance state. The client PAGES the club -- one visit produced seven
# GET /club?...start=50&count=11 fetches -- so an auto window can hand out a new
# chunk on every fetch and cover ~7 chunks per visit instead of one. Item ids are
# derived from the candidate's OFFSET IN THE WHOLE RANGE, not from its index in
# the chunk, so chunks never collide in the map and results ACCUMULATE across
# fetches; one probe at the end reads them all.
_SWEEP_SPEC = None
_SWEEP_POS = 0
def _parse_window(win):
"""'lo-hi' or 'auto:lo-hi:step' -> (lo, hi, step or None). None on garbage."""
auto = win.startswith("auto:")
step = None
if auto:
parts = win[5:].split(":")
rng = parts[0]
if len(parts) > 1:
step = int(parts[1], 0)
else:
rng = win
try:
lo, hi = (int(x, 0) for x in win.split("-", 1))
lo, hi = (int(x, 0) for x in rng.split("-", 1))
except Exception:
log(" SWEEP: bad window %r, want '<lo>-<hi>'" % win)
return []
return None
if hi < lo:
lo, hi = hi, lo
out = []
for i, pid in enumerate(range(lo, hi + 1)):
it = _item(SWEEP_ID_BASE + i, pid, SWEEP_SENTINEL_RATING,
"ST", 0, 0, 0, [1, 1, 1, 1, 1, 1])
out.append(it)
log(" SWEEP: serving %d candidate playerid(s) %d..%d as the club "
"[synthetic, nothing saved]" % (len(out), lo, hi))
if auto and not step:
step = 5000
return lo, hi, step
def sweep_items():
"""Synthetic club contents for the current sweep window.
5000 candidates per response is live-proven. 20000 was served fine and then
silently NOT ingested -- the map did not change at all -- so there is a
ceiling between the two. Auto chunks therefore default to 5000, and a chunk
that is not ingested costs one fetch, not the sweep.
"""
global _SWEEP_SPEC, _SWEEP_POS
win = sweep_window()
parsed = _parse_window(win)
if not parsed:
log(" SWEEP: bad window %r, want '<lo>-<hi>' or 'auto:<lo>-<hi>:<step>'" % win)
return []
lo, hi, step = parsed
if win != _SWEEP_SPEC: # re-aimed: restart the walk
_SWEEP_SPEC, _SWEEP_POS = win, 0
if step:
start = lo + _SWEEP_POS
if start > hi:
log(" SWEEP: range %d..%d EXHAUSTED -- probe now, then re-aim" % (lo, hi))
return []
end = min(start + step - 1, hi)
_SWEEP_POS += step
else:
start, end = lo, hi
out = [_item(SWEEP_ID_BASE + (pid - lo), pid, SWEEP_SENTINEL_RATING,
"ST", 0, 0, 0, [1, 1, 1, 1, 1, 1])
for pid in range(start, end + 1)]
log(" SWEEP: serving %d candidate playerid(s) %d..%d%s "
"[synthetic, nothing saved]"
% (len(out), start, end,
(" (auto, %d..%d done)" % (lo, end)) if step else ""))
return out