Close three holes an unmodified client could walk through
One account is now one session. Two peers signed in as the same account each got their own player built from the SAME character record, so both held a copy of one bag and whichever wrote last won: drop the single potion from both copies and two potions hit the floor. Reconciling two live mirrors of one record cannot be made to work, so the handshake refuses the second connection -- the newcomer, not the incumbent, or claiming an account would be a way to kick anyone off the server. A muzzle offset is a teleport and the bullet pool never sees it: it samples geometry once per tick. Forward that is 19px, but Doubleshot adds up to 45px sideways, wider than a 32px wall plus the 6px hitbox holding you off it. Six Doubleshots, stand flush against a wall, fire along it, and live shots came out the far side. Shots now start from the shooter when geometry stands in the way, which keeps the count honest. Bullet-spawn events carry the firing actor's exact position, and they were filtered by a flat 2900-unit radius against an 800-unit actor cull. A depth-1 map's diagonal is about 2521, so that radius culled nothing: every enemy that fired was pinpointed for every player, through fog, on terrain the client was deliberately not streamed. The test is now per bullet -- can THIS one reach ground you can see -- which keeps the guarantee the wide radius existed for and drops the radar. Found while fixing the above: a rejection never reached the client. The RPC is queued and only leaves on the frame's flush, so kicking in the same call threw the reason away, and _on_server_disconnected overwrote last_error anyway. Both were true of protocol mismatch too, which is the one message a player most needs. Smoke asserts the reason arrives. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b8332b697d
commit
ef28970654
9 files changed
+357
-21
No files matched your search
@@ -55,6 +55,22 @@ for n in 1 2; do
|
||||
sleep 0.4
|
||||
done
|
||||
|
||||
# A bot that claims an account another bot is already playing. One account is
|
||||
# one session: two peers on one account each get their own player built from the
|
||||
# SAME character record, so both hold a copy of one bag and whichever writes last
|
||||
# wins -- drop the single potion from both copies and two potions hit the floor.
|
||||
# The handshake has to refuse it, and it has to refuse the NEWCOMER, or claiming
|
||||
# someone's account becomes a way to kick them off the server.
|
||||
for _ in $(seq 1 60); do
|
||||
grep -q "authenticated as account 7001" "$OUT/server.log" 2>/dev/null && break
|
||||
sleep 0.25
|
||||
done
|
||||
"$GODOT" --headless --path . -- --bot --host 127.0.0.1 --port "$PORT" \
|
||||
--name "twinbot" --account 7001 --autoquit 300 \
|
||||
> "$OUT/twinbot.log" 2>&1 &
|
||||
PIDS+=($!)
|
||||
sleep 0.4
|
||||
|
||||
# A fourth bot that leaves *politely* -- the same Net.shutdown() the in-game
|
||||
# menu's "Disconnect" button calls. The escape channel is keyed on the socket
|
||||
# closing, not on how it closed, so a clean exit must be caught exactly like the
|
||||
@@ -118,6 +134,17 @@ no_engine_errors() { # no_engine_errors <label> <file>
|
||||
fi
|
||||
}
|
||||
|
||||
count() { # count <label> <file> <pattern> <expected>
|
||||
local hits
|
||||
hits=$(grep -cE "$3" "$2" || true)
|
||||
if [[ "$hits" == "$4" ]]; then
|
||||
echo " ok $1"
|
||||
else
|
||||
echo " FAIL $1 (/$3/ matched $hits time(s), expected $4, in $(basename "$2"))"
|
||||
fails=$((fails + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
refute() { # refute <label> <file> <pattern>
|
||||
local hits
|
||||
hits=$(grep -cE "$3" "$2" || true)
|
||||
@@ -166,6 +193,16 @@ check "a polite disconnect is also channelled" \
|
||||
# assert nothing at all. The upgrade-state message DOES cross the wire on every
|
||||
# login, so a malformed one shows up in the script-error refutations below --
|
||||
# and tools/diag_upgrades.tscn covers the loop itself.
|
||||
check "a duplicate account is refused" "$OUT/server.log" \
|
||||
"claimed account 7001, already held by peer"
|
||||
check "and the newcomer is the one told no" "$OUT/twinbot.log" \
|
||||
"that account is already playing"
|
||||
count "the account was only ever seated once" "$OUT/server.log" \
|
||||
"authenticated as account 7001" 1
|
||||
# The victim keeps playing: refusing the second connection must not disturb the
|
||||
# session that already held the account.
|
||||
check "the original session survives it" "$OUT/bot1.log" "entered instance .*LOBBY"
|
||||
|
||||
refute "no server script errors" "$OUT/server.log" "SCRIPT ERROR|Parse Error|USER ERROR"
|
||||
refute "no client script errors" "$OUT/bot1.log" "SCRIPT ERROR|Parse Error|USER ERROR"
|
||||
no_engine_errors "no engine errors on the server" "$OUT/server.log"
|
||||
|
||||
Reference in new issue
Block a user