Game award stats and awards in the feed #38
Labels
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Depends on
#49 JSON Schema for the game-state feed
mintopia/blur
#33 Test-race scenarios and race bot in scripts/
mintopia/blur
Reference
mintopia/blur#38
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The game builds awards from
MultiplayerAwardStat(~42 stats: PlayersHitWithBolt, TimeInFirstPlace, LongestDrift, PowerupsEvaded, ...) and award types (Hunter, Marksman, WoodenSpoon, ...); LuaGetRaceAwards,Multiplayer.GetRaceResults(); possibly synced viaMpDataGathering.Spike
Find the per-racer stat values and awarded titles at race end; which are available for remote racers on each instance.
Acceptance
award_stats(game values, game stat names in snake_case; absent where the game has none), live instandingsand final inrace_result.race_result.awards:[{award, racer, stat, value}], all awards the game gives.totalsstays separate.Rules
docs/decisions/0019-feed-message-contract.md). Repo data/tools: ADR 0020.BLUR_ENGINE=recomponly.docs/state-websocket.mdandexamples/live-timingfor any feed change.scripts/scenarios (windowed, low settings) once #33 exists.Research findings (static: Blur.exe strings + current src), 2026-10-20.
System (data-driven):
MultiplayerAward,AwardType,AwardStat,AwardThreshold,MultiplayerAwardStatSortingStrategy(HighestWins/LowestWins),MultiplayerAwardDistributionSpec.Lua:
GetRaceAwards,GetRaceAwardsInLobby,GetRaceHistoryAwardIconNames.Multiplayer.GetRaceResults()is already used inHud.post.lua. Likely cheapest route: call these from a results-screen.post.luaand publish viaHost.state.Stats (
MultiplayerAwardStat, strings near exe file offset 0xad9754): PlayersHitWithBolt/Shunt/Mine/Barge/IronFist, PowerupsShielded, BlockedPowerups, NitroOvertakes, LongestJump, TimeInFirstPlace, LongestTimeNotDamaged, PowerupsDropped, TimesProtectedByStableFrame, CarsBatteringRammed, PlayersHitwithDecoys, PlayersScrambled, PowerupsAbsorbedWithSafetyNet, TimeInvisibleWithSilentRunning, MinesFragmentedWithMastermine, TimesHit, TotalCarsWrecked, LongestTimeWithoutBeingWrecked, DamageDealt, TimesOffEdge, TimesInWater, LongestDrift, PickupsCollected, EnvironmentalCrashes, Car2CarVictimHits, Car2CarAggressiveHits, TopSpeed, PowerupsEvaded, LongestChain, Accuracy, DeathStreak, KillStreak, HitsByPowerups, PowerupHits, EliminationPoints, OnTrackPoints, LapsCompleted, DistanceTravelled, HealthRemaining, SlowestLap, FastestLap, RaceTime.Awards: Hunter, CrimsonCarapace, LitterBug, Defender, SafeHands, Booster, CloseEncounters, LongJumper, Dexterous, ButterFingers, Batterer, Environmentalist, Cliffhanger, DeepSeaDiver, PunchingBag, WriteOff, Relentless, Survivor, WreckingBall, CleanRacer, Brawler, Agile, BreakNeck, CrashTestDummy, Gladiator, Squirrel, TriggerHappy, Marksman, OldNews, FanTastic, Tortoise, WoodenSpoon.
Also ~90 fan scoring events
SCR_NEW_AMAX_SCORING_*(drift, air, near miss, revenge, killing spree...).Unknowns for the spike
MpDataGathering/DataGatheringRecordssuggest yes). The ADR 0016 victim counters+0x598/+0x5d8update only on the victim's own instance.BLUR_LUA_DUMPrun through a race-results screen first.Done (commits 99ea103, 24f4aab; ADR 0024).
Spike findings (verified on BLUR_ENGINE=recomp, 2-instance LAN):
Implemented: Lua hook (Commands.post.lua) publishes _awards/_award_stats on awards screen; Feed::set_awards republishes race_result with awards [{award,racer,stat?,value?,unit?,display}] and per-racer standings[].award_stats (stats of won awards). MP only. Docs (state-websocket.md), live-timing example, changelog updated. Verified in recomp LAN race: both instances got awards.
Not done (needs native reads of MpDataGathering, separate RE task): live award_stats in standings, values for stats of awards not won, full 42-stat set. Suggest a follow-up issue.
Done (ADR 0025, extends 0024). Found the game's per-racer stat records natively: float[48] (all MultiplayerAwardStat, enum order from the exe table at 0x10faed8) at +0x30 of a tracked heap block, signature 0xd0036 at +0x14, byte +0x1b = join ref. Verified on recomp LAN races (2 and 3 instances): values match awards-screen amounts (env crashes, accuracy, nitro overtakes, longest time not damaged) and agree across instances. Feed: per-racer
award_stats(all 48 snake_case stats, raw game values) instandingsonce tallied and inrace_result(republished when late records arrive); scraped per-award stats stay as fallback. Limit: the game only fills records at race end (zero mid-race), so nothing is live mid-race; documented in docs/state-websocket.md and ADR 0025. Live-timing example updated; unit tests added.Rebased onto develop (#35 totals) + review fixes. Commits 8172428, 8d6be48.
totals(nested maps) the last field per racer; award_stats splicing anchored on the first}and landed insidetotals.uses. Now inserts before,"totals":(npos-guarded). Tests tightened.Feed::set_statsmerges (freed records don't strip a published result), cleared at race start.award_statstime stats now seconds, matching native records (awards[].valuestays ms withunit:"ms")._msrule for raw game stats; amends 0024's "not done" line.Verified: build + ctest 23/23; recomp 2-instance LAN race (
scripts/race.py lan): valid JSON, award_stats beside totals, none on unfinished racers, final values agree across instances.Known limit (unchanged): the bot run quits before the awards screen / remote record arrival, so that run's race_result had native stats for 1 of 2 racers; late records republish race_result when they arrive.
Critic fix (ccb65d6): removed the 4 flagged comments (race_feed.cpp splice invariant, race_feed.h Racer::stats gloss, set_awards format note, set_stats note) plus two similar ones in race_scan.cpp and Commands.post.lua; renamed
Feed::set_statstomerge_stats,Racer::statstoaward_stats,racer_endtobefore_totals. Comment check PASS; ctest 23/23.Completed and merged by Harmonic (task 30).