1dc1952a3c
ci / verify (push) Successful in 45s
(1) Bullets appeared to trail the ship. Two independent causes, measured with
the new tools/diag_prediction.gd rather than guessed at:
- ServerRuntime ticked before ClientRuntime, so input sampled on frame N was
not consumed until frame N+1, leaving the drawn ship a constant one tick
(4.00px at 240 u/s) ahead of the authoritative one that bullets spawn from.
ClientRuntime now sets process_physics_priority = -10. Gap on a listen
server: 4.00px -> 0.10px mean, 0.30px worst.
- PLAYER_MUZZLE_OFFSET was PLAYER_RADIUS + 6 = 12px against a 13px drawn
ship, so bullets were born inside the sprite. Regression from the previous
commit's hitbox shrink; it now derives from PLAYER_VISUAL_RADIUS.
(2) No more timed respawn. A downed player stays down until they ask for the
hub (E), which is an ordinary input -- the server has no "revive me" message.
(3) Escape channel 3s -> 1s, and damage no longer cancels it. An interruptible
channel makes killing the process strictly better than using the button, so a
dropped connection now runs the same channel: the player stays in the world as
linkdead, still killable, and is only released once it completes. Instances
refuse to close while a linkdead body is resolving, or a solo drop would delete
it on the next tick and hand the exploit straight back.
(4) Escape opens an in-game menu: return to hub (routed through the same held-
escape channel, not a new message), disconnect, quit.
(5) Server pushes a roster so the hub shows who is online and which dungeon
they are in. Entering a dungeon grants 2s arrival protection -- invulnerable
AND weapons-cold, since invulnerability alone would make the spawn a free
firing position -- flagged in the snapshot and drawn on every protected ship.
(6) Cleared dungeons hold the party 30s (was 5s) with a visible countdown.
(7) The hub's grey circle was a 100k-HP target dummy that read as scenery. Now
drawn as a bullseye so its purpose is legible.
Protocol version 1 -> 2. 91 tests (was 78); smoke.sh gains a bot that is
SIGKILLed mid-dungeon to prove the disconnect path end to end. check.sh,
test.sh and smoke.sh all pass.
60 lines
2.0 KiB
GDScript
60 lines
2.0 KiB
GDScript
extends Node
|
|
## Measures the gap between where the client *draws* the local player and where
|
|
## the server actually has it -- the number that decides whether bullets appear
|
|
## to leave the ship's nose or trail out of its back.
|
|
##
|
|
## godot --headless --path . res://tools/diag_prediction.tscn
|
|
##
|
|
## Runs the real listen-server path (ServerRuntime + ClientRuntime + the actual
|
|
## Net loopback), not a mock, so node process order is exactly production's.
|
|
|
|
var _ticks: int = 0
|
|
var _worst: float = 0.0
|
|
var _sum: float = 0.0
|
|
var _samples: int = 0
|
|
|
|
|
|
func _ready() -> void:
|
|
GameOpts.bot_client = true
|
|
GameOpts.player_name = "diag"
|
|
if Net.host(27300) != OK:
|
|
push_error("could not host")
|
|
get_tree().quit(1)
|
|
return
|
|
Net.start_local_client()
|
|
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
_ticks += 1
|
|
var srv := Net.server
|
|
var cli := Net.client
|
|
if srv == null or cli == null:
|
|
return
|
|
var inst := srv.instance_of(Net.LOCAL_PEER)
|
|
if inst == null or not inst.world.players.has(Net.LOCAL_PEER):
|
|
return
|
|
var sp: SimPlayer = inst.world.players[Net.LOCAL_PEER]
|
|
var delta_px := sp.pos.distance_to(cli.predicted_pos)
|
|
|
|
# Skip the first few ticks while the handshake settles.
|
|
if _ticks > 20:
|
|
_worst = maxf(_worst, delta_px)
|
|
_sum += delta_px
|
|
_samples += 1
|
|
|
|
if _ticks % 20 == 0:
|
|
print("tick=%4d srv_tick=%4d srv_pos=%-22s pred=%-22s gap=%6.2fpx acked=%4d cli_tick=%4d queue=%d" % [
|
|
_ticks, inst.world.tick, str(sp.pos.round()), str(cli.predicted_pos.round()),
|
|
delta_px, sp.last_input_tick, cli.input_tick, sp.input_queue.size()])
|
|
|
|
if _ticks >= 300:
|
|
print("---")
|
|
print("GAP worst=%.2fpx mean=%.2fpx over %d samples" % [_worst, _sum / maxf(_samples, 1), _samples])
|
|
print("player speed is %.0f u/s = %.2f px per tick" % [
|
|
SimConfig.PLAYER_SPEED, SimConfig.PLAYER_SPEED * SimConfig.TICK_DELTA])
|
|
print("muzzle offset = %.1fpx, drawn ship radius = %.1fpx, hitbox = %.1fpx" % [
|
|
SimConfig.PLAYER_MUZZLE_OFFSET, SimConfig.PLAYER_VISUAL_RADIUS,
|
|
SimConfig.PLAYER_RADIUS])
|
|
Net.shutdown()
|
|
get_tree().quit(0)
|