feat(smash): projectiles you can see - #1044
Merged
Merged
Conversation
Twenty of the fifty-one abilities fire a projectile, and until now a client saw none of them. `projectile::fire` created a game-half entity with `Flight`, `Payload` and `FiredBy`, integrated it, detected the hit and dealt the damage -- all correct, all tested against the mock, and all invisible, because nothing ever told a client the thing existed. A Skeleton's Barrage sent five arrows' worth of damage and not one arrow. The flight stays where it is. It is authoritative for the hit, it is deterministic, and `tests/abilities.rs` and `smash-e2e` both rest on it; moving it would fork test from production. What was missing was only the picture, so only the picture is added. Each projectile now carries a `Visual(EntityKind)` -- a generated enum named directly, the choice #1035 made for particles, exact where a vanilla entity is the thing (an arrow is `Arrow`, a wither skull a `WitherSkull`, a thrown cub a `Wolf`) and the closest always-rendered projectile where it is not (a thrown coal is a `SmallFireball`, an ink pellet a `Snowball`), each `[APPROXIMATED]` marked at the call site. `crate::draw`, on the host, turns that into an entity a client draws: it decorates the existing projectile with the components hyperion's egress reads -- kind, position, per-tick velocity, facing -- and enqueues the spawn. The client dead-reckons the arc from the velocity the add_entity packet carries, which is how vanilla draws a thrown projectile between updates. It is the same entity, not a shadow, so when `fly` destructs it on the hit the drawn thing dies too and hyperion's despawn observer tells every client to drop it -- no second lifetime to keep in step. Two things it deliberately does not do, each with a reason that is a property of the engine rather than a preference. No `Owner`: that is what `update_projectile_positions` requires to integrate and collide an entity, and omitting it is what stops hyperion re-flying and re-hitting a projectile `Flight` already owns. Not `spawn::spawn`, which makes a new entity: decorating the existing one is the whole reason despawn is free. The drawn arc and the hit are computed by two integrators that do not share constants -- hyperion's client dead reckoning uses the vanilla gravity for the kind, `Flight` uses what the ability set. They agree exactly for a zero-gravity projectile and drift for a heavy one over its flight; the projectile vanishes at the hit either way, because the hit is what destructs it. Sharing one integrator is the larger change `docs/smash-design.md` flags; this is the visible-now half. Proof, both halves of the seam: `every_projectile_that_flies_can_be_seen` fires every ability and asserts that whatever projectiles appeared carry a `Visual` -- the input to drawing, for all twenty, checked in the game half against the mock. It sweeps the roster rather than a hand-list of projectile abilities, so a kit added tomorrow that forgets the visual fails here. `tools/smash-match.py` decodes `add_entity` and proves "a projectile was drawn" on the wire: over the sweep, firing the abilities produces add_entity packets, which before `draw` existed never left the server. During the sweep the only new entities are projectiles, because every player spawned before it began. Guard broken and watched to fail, then restored: `.set(visual)` removed from `fire` Skeleton / Barrage put 2 projectile(s) in the world with no Visual Spider / Needler put 6 projectile(s) in the world with no Visual Sky Squid / Ink Shotgun put 7 projectile(s) in the world with no Visual ... and every other projectile ability The wire half cannot be run to green from here: my `smash-e2e` attempts die on the load box's connection sweep, as before. The in-process Visual proof is the local evidence; the add_entity census is structural and runs on the gate.
Benchmark Results for generalComparing to d11ca7a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Twenty of the fifty-one abilities fire a projectile, and until now a client saw none of them.
projectile::firecreated a game-half entity withFlight,PayloadandFiredBy, integrated it, detected the hit and dealt the damage — all correct, all tested against the mock, and all invisible, because nothing ever told a client the thing existed. A Skeleton's Barrage sent five arrows' worth of damage and not one arrow.The flight stays where it is
It is authoritative for the hit, it is deterministic, and
tests/abilities.rsandsmash-e2eboth rest on it; moving it would fork test from production. What was missing was only the picture, so only the picture is added.Each projectile now carries a
Visual(EntityKind)— a generated enum named directly, the choice #1035 made for particles. Exact where a vanilla entity is the thing (an arrow isArrow, a wither skull aWitherSkull, a thrown cub aWolf) and the closest always-rendered projectile where it is not (a thrown coal is aSmallFireball, an ink pellet aSnowball), each[APPROXIMATED]marked at the call site.crate::draw, on the host, turns that into an entity a client draws: it decorates the existing projectile with the components hyperion's egress reads — kind, position, per-tick velocity, facing — and enqueues the spawn. The client dead-reckons the arc from the velocity theadd_entitypacket carries, which is how vanilla draws a thrown projectile between updates. It is the same entity, not a shadow, so whenflydestructs it on the hit the drawn thing dies too and hyperion's despawn observer tells every client to drop it — no second lifetime to keep in step.Two things it deliberately does not do
Each has a reason that is a property of the engine, not a preference.
No
Owner. That is whatupdate_projectile_positionsrequires to integrate and collide an entity; omitting it is what stops hyperion re-flying and re-hitting a projectileFlightalready owns.Not
spawn::spawn, which makes a new entity. Decorating the existing one is the whole reason despawn is free.The honest limitation
The drawn arc and the hit are computed by two integrators that do not share constants — hyperion's client dead-reckoning uses the vanilla gravity for the kind,
Flightuses what the ability set. They agree exactly for a zero-gravity projectile (a hook, a line of cows) and drift for a heavy one over its flight. The projectile vanishes at the hit either way, because the hit is what destructs it. Sharing one integrator is the larger changedocs/smash-design.mdflags; this is the visible-now half.Proof, both halves of the seam
every_projectile_that_flies_can_be_seenfires every ability and asserts that whatever projectiles appeared carry aVisual— the input to drawing, for all twenty, checked in the game half against the mock. It sweeps the roster rather than a hand-list of projectile abilities, so a kit added tomorrow that forgets the visual fails here.tools/smash-match.pydecodesadd_entityand proves "a projectile was drawn" on the wire: over the sweep, firing the abilities producesadd_entitypackets, which beforedrawexisted never left the server. During the sweep the only new entities are projectiles, because every player spawned before it began.Guard broken and watched to fail, then restored
.set(visual)removed fromfire:What this does not settle
The wire half cannot be run to green from here: my
smash-e2eattempts die on the load box's connection sweep, as they have all session. The in-processVisualproof is the local evidence; theadd_entitycensus is structural and runs on the gate.One call site in
tests/relationship_traits.rs(from #1041/#1042) also callsfire; its signature fix is included — three lines, no reformatting of that file.Checks
cargo test -p smash(21 binaries) andnix run --accept-flake-config .#lintboth green ond11ca7a, which includes the flecs trait pass.