ci / verify (push) Successful in 52s
Health survives a run now, so a character sitting on the select screen can be nearly dead -- and the screen was still printing only the ceiling. "level 11, 2000 hp" for a character on 160 is a lie told at the exact moment it matters most, because the next thing that happens is the player walks it into a dungeon under permadeath. The roster message carries current hit points alongside the maximum, and the screen shows a fraction only when it means something: a healthy character still reads "1300 hp" rather than "1300/1300". The field is inserted mid-record, which shifts every field after it, so the protocol version goes to 10 -- a mismatched client would read the active flag out of the middle of a health value. tools/screenshot.gd had no capture for this screen, which is how a UI change to it could have shipped unlooked-at. It now shows a made-up roster of three characters -- whole, badly hurt, nearly dead -- for the same reason the upgrade screen is fed a made-up offer: the point is the layout, not a dungeon run. Getting there needs the player walked off the quartermaster first, or the game scene re-opens the upgrade screen over the top of it every frame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
3.5 KiB
GDScript
75 lines
3.5 KiB
GDScript
class_name Protocol
|
|
extends RefCounted
|
|
## Wire constants. Bump [constant VERSION] whenever a codec layout changes; the
|
|
## server refuses mismatched clients at handshake rather than desyncing later.
|
|
|
|
## 2: added spawn-grace flag + cleared countdown to the snapshot, and the
|
|
## roster message.
|
|
## 3: dropped the post-hit invulnerability flag, added the respawn-lockout byte
|
|
## to each player record.
|
|
## 4: added SimEvent.Type.PLAYER_FIRED. It was inserted mid-enum, which shifts
|
|
## the wire value of every event after it -- a mismatched client would
|
|
## mis-decode every hit and death, so the handshake has to reject it.
|
|
## 5: handshake carries an auth ticket instead of a bare name; added character
|
|
## list/select/create messages, per-player max health and colour in the
|
|
## snapshot.
|
|
## 6: inventory and loot. The snapshot gained the observer's own inventory and
|
|
## the ground-loot list, the input frame gained a slot byte, and three item
|
|
## events were appended. Every one of those changes the byte layout of a
|
|
## message both ends parse positionally.
|
|
## 7: more than one dungeon. enter_instance carries a portal LIST and the id of
|
|
## the dungeon you are standing in, replacing the single portal position.
|
|
## 8: upgrades. A new server -> client upgrade-state message, a new
|
|
## client -> server choice message, and two more SelectResult values.
|
|
## 9: telegraphed boss attacks. A new TELEGRAPH event, appended to the enum.
|
|
## 10: health survives a run, so the character roster carries current hit
|
|
## points as well as the ceiling. Inserted mid-record, which shifts every
|
|
## field after it -- a mismatched client would read the active flag out of
|
|
## the middle of a health value.
|
|
const VERSION := 10
|
|
const DEFAULT_PORT := 27015
|
|
const MAX_CLIENTS := 32
|
|
|
|
## ENet channels. Separating them stops a burst of reliable bullet events from
|
|
## head-of-line blocking the unreliable snapshot stream.
|
|
const CH_CONTROL := 1 ## handshake, instance transitions -- reliable
|
|
const CH_SNAPSHOT := 2 ## world state -- unreliable, newest wins
|
|
const CH_EVENTS := 3 ## bullet spawns/despawns, hits -- reliable ordered
|
|
const CH_INPUT := 4 ## client -> server intent -- unreliable ordered
|
|
## Must be identical on both ends, and larger than the highest channel above.
|
|
const CHANNEL_COUNT := 8
|
|
|
|
enum InstanceKind { LOBBY, DUNGEON }
|
|
|
|
## Why a character selection failed. Sent rather than a bare "no", so the UI can
|
|
## say something useful instead of appearing broken.
|
|
enum SelectResult {
|
|
OK,
|
|
NO_SUCH_CHARACTER,
|
|
CHARACTER_IS_DEAD,
|
|
LIMIT_REACHED,
|
|
NOT_AUTHENTICATED,
|
|
## Swapping is hub-only. Allowing it inside a dungeon would be an instant,
|
|
## uninterruptible exit from danger -- strictly better than the one-second
|
|
## escape channel, and it would make that channel pointless.
|
|
NOT_IN_HUB,
|
|
## Upgrades are spent standing at the hub's NPC. Same rule as the portal:
|
|
## where you are is the one thing a modified client cannot fake.
|
|
NOT_AT_THE_NPC,
|
|
## No unspent level-up, or an option index that was not on the table.
|
|
NOTHING_TO_CHOOSE,
|
|
}
|
|
|
|
## Player flags packed into the snapshot's per-player byte.
|
|
const F_ALIVE := 1
|
|
const F_ESCAPING := 4
|
|
## Arrival protection: invulnerable and unable to shoot. The only invulnerable
|
|
## state there is -- see the i-frames note in SimConfig.
|
|
const F_SPAWN_GRACE := 8
|
|
## The peer behind this player has dropped and is being channelled out.
|
|
const F_LINKDEAD := 16
|
|
|
|
## Snapshot's cleared-countdown byte when the instance is not in the cleared
|
|
## state -- 30s fits in a byte, so 255 is free to mean "not applicable".
|
|
const COUNTDOWN_NONE := 255
|