Audited rather than appended. What had drifted, now corrected: - ROADMAP's opening still described the project before this session: no word of the anti-cheat fixes, 468 tests, 23 smoke assertions, and a screenshot list half its current size. It now summarises the session, lists seven open items raised with the user and not yet decided (the boss-kill experience edge, the hub frame rate, aggro through corner cracks, the Warden-hall alcoves, one-tile arena doors, the Proving Grounds before release, hit feedback), and quotes current numbers. Its radius table still gave BULLET_INTEREST_RADIUS as the bullet cull at 2200; it is a 2900 ceiling over a per-bullet check. - NETCODE described the flat bullet radius as the design, claimed the respawn lockout gates connected players (they are unbound on death), and called actor scoping "next stage". All corrected, and a new section covers what the handshake and server now refuse: duplicate sessions, reasons that now arrive, paced control messages, coalesced store writes, bounds-checked hot decoders. - DECISIONS gains the audit's rules, the Proving Grounds' status, the tunnel and entrance rules, and the interface calls -- each flagged as the user's or as a judgement call. - ARCHITECTURE's file map lacked the actor classes, the dev auth provider, the menu backdrop and any tools section. - README told people to start a client with --name, which now only names bot characters, and its feature list predated fog, tunnels and carried health. CLAUDE.md gains three invariants (health on the record, one session per account, tools that host use scratch data); WORKFLOW gains the four traps this session fell into or nearly did. Every doc was checked for unintended deletions against the session's start, and every relative link resolves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
94ad6b3429
commit
08f58af1a8
7 files changed
+251
-26
No files matched your search
@@ -543,3 +543,95 @@ a different fight in a different room.
|
||||
**A CHASE boss has a dead band around its preferred distance.** The correction
|
||||
is signed, so without one the sign flips every tick at the standoff and the boss
|
||||
vibrates on the spot at the tick rate — a couple of pixels, and unmistakable.
|
||||
|
||||
---
|
||||
|
||||
## The anti-cheat audit, and the rules it left behind
|
||||
|
||||
*(User asked for an audit for design flaws and cheats, then "fix these".)* Each
|
||||
of these was found by reading the server's code paths and then proving the
|
||||
problem with a probe before fixing it.
|
||||
|
||||
**One account, one session — and the newcomer is refused, not the incumbent.**
|
||||
Two peers on one account each got a player built from the same character record,
|
||||
so both held a copy of one bag and whichever wrote last won: drop the one potion
|
||||
from both copies and two potions hit the floor. Refusing the second connection is
|
||||
the only fix that closes it. The *new* peer is the one turned away because, with
|
||||
an auth provider anyone can spoof, "kick whoever holds this account" would let
|
||||
anyone disconnect anyone. A dropped player's linkdead body still holds the
|
||||
account until its escape channel resolves, which is deliberate: reconnecting
|
||||
during that second would put the character in the world twice.
|
||||
|
||||
**A rejection must reach the client.** `Net.reject_and_kick` defers the kick,
|
||||
because an RPC only leaves on the frame's flush and kicking in the same call threw
|
||||
the reason away — a protocol mismatch used to arrive as "server closed the
|
||||
connection".
|
||||
|
||||
**Bullets are never born on the far side of geometry.** A muzzle offset is a
|
||||
teleport the pool never sees. The player's shot falls back to the shooter's
|
||||
centre when blocked (six Doubleshots used to put shots through a one-tile wall);
|
||||
an enemy's shot is dropped (boss curtains were spawning through the arenas' own
|
||||
pillars and barricades). A telegraphed burst is checked from its marker, not from
|
||||
the boss.
|
||||
|
||||
**Bullet spawns are culled per bullet, not by a flat radius.** Each spawn event
|
||||
carries the shooter's exact position, and the flat 2900-unit radius exceeded the
|
||||
whole depth-1 map, so every firing enemy was located for every player.
|
||||
`ServerRuntime._bullet_concerns` asks whether *this* bullet can reach ground the
|
||||
player can see.
|
||||
|
||||
**No packet buys unbounded work.** Selecting a character you already hold is a
|
||||
no-op; select and create are paced per peer (`CONTROL_COOLDOWN_TICKS`), and the
|
||||
pacing logs once per burst, not once per packet. Item moves and experience grants
|
||||
mark the store dirty and it is written once per tick at most.
|
||||
|
||||
**The hub has no escape channel.** Completing it there meant transferring into
|
||||
the instance you were already in — teleport, full heal, map re-streamed.
|
||||
|
||||
**A player's drop is refused when the floor is full.** The cap evicts the oldest
|
||||
item, which is right for a dungeon's own drops and was wrong for the hub, where it
|
||||
let a player destroy whatever someone else had left there longest.
|
||||
|
||||
## Health, experience and a debug dungeon
|
||||
|
||||
**The Proving Grounds is a debugging tool and does not ship.** *(User.)* It pays
|
||||
full experience for a boss with 8% of its health. Remove it from `Dungeons.ORDER`
|
||||
and its `P` marker before a release — see the roadmap.
|
||||
|
||||
## Tunnels and what is in them
|
||||
|
||||
**Tunnels are 2 to 4 tiles wide, rolled per leg.** *(User: "a varied width with a
|
||||
minimum of 2".)* A one-tile corridor in a bullet hell is a shooting gallery. The
|
||||
width is per leg of each L, not varying along a leg; that was the simpler reading
|
||||
and the user has not asked for the other. The arena is excluded from every carve —
|
||||
an L that bent the wrong way used to slit 45% of arenas' west walls open.
|
||||
|
||||
**Now and then a tunnel has a group in it.** *(User: "rarely".)* A 10% chance per
|
||||
leg of two or three of one kind; about one depth-1 run in three gets one. Never
|
||||
within aggro range of the entrance, checked per member.
|
||||
|
||||
**Nothing can be watching the entrance when you arrive.** Room 0 was always left
|
||||
empty for this, but only room 0 — and wider tunnels tripled how often a
|
||||
neighbouring room could see the entrance (25 to 75 runs in 200). Enemies that
|
||||
would be awake on arrival are skipped: about one in sixteen. Clearing everything
|
||||
within aggro range would have removed nearly a third, which is a balance change
|
||||
the user has not asked for.
|
||||
|
||||
## Interface
|
||||
|
||||
**Every key a prompt names is the one actually bound.** All actions are
|
||||
rebindable; prompts go through `Settings.short_label()`.
|
||||
|
||||
**The boss bar waits until the boss has been seen.** The server sends the boss
|
||||
through walls from 800 units, and the bar used to announce it to a player still in
|
||||
the entrance room. It appears once the fog has lit the boss and stays for the
|
||||
fight. A judgement call, flagged to the user as reversible.
|
||||
|
||||
**The controls hint fades six seconds into a dungeon** and stays in the hub. Also
|
||||
a judgement call; one constant (`Hud.HINT_HOLD`).
|
||||
|
||||
**The connect screen asks for no name.** The field it had only named the
|
||||
characters bots create; people name characters on the roster screen.
|
||||
|
||||
**Getting hit reddens the screen's edge, not the whole screen** — a full-screen
|
||||
tint dimmed the bullets at the moment you were among them.
|
||||
Reference in new issue
Block a user