PC3 Phase 2.1: server-authoritative sim + prediction/reconciliation with replay
Made the multiplayer core actually work (it had never run a real sim): - project.godot: physics pinned to 30 Hz -- the shared sim/snapshot/prediction rate. - server.gd: loads the world's level (real ground + collision), spawns a server Player per peer at the level's PlayerSpawn, broadcasts a full snapshot every physics frame with each peer's state AND last_seq (the last input seq applied for that peer). Accepts `--port/--world/--status-file` args (status file = player count for a future idle reaper); defaults keep `godot res://scenes/server.tscn` working standalone. - player_spawner.gd (server Player): rewrote -- drains its whole input queue each physics frame in seq order, integrates gravity/walk/jump with the shared constants, move_and_slide(), tracks last_applied_seq. No more no-op process_server_input(). - network_client.gd: rewrote reconciliation -- drops inputs the server has acked (last_seq), re-simulates the still-in-flight ones from the authoritative state, snaps only on real drift. Exposes get_remote_states() (peer_id -> latest snapshot state) for the game to render other players. predict_and_send() now also sends body yaw. - proxies trimmed to the RPC set the rewrite actually uses. - scripts/mp_test_client.gd + run_multiplayer_2p_test.sh: two real client processes vs one real server -- client A sees client B and its own movement round-trips. PASS. - run_checkpoint_tests.py: also run the 2p shell test. smoke_test_network_client 4/4, player 15/15, avatar_in_world 10/10, all green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Two real client processes against one real dedicated server: client A must
|
||||
# see client B and its own movement must round-trip through the server.
|
||||
#
|
||||
# Usage: scripts/run_multiplayer_2p_test.sh
|
||||
# Exit 0 iff A reports moved=true and remotes>=1.
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
GODOT_BIN="${GODOT_BIN:-godot}"
|
||||
command -v "$GODOT_BIN" >/dev/null 2>&1 || GODOT_BIN="$HOME/godot/godot"
|
||||
PORT=7787
|
||||
|
||||
cleanup() { kill "${SRV_PID:-}" "${B_PID:-}" 2>/dev/null; wait 2>/dev/null; }
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "=== starting dedicated server on $PORT ==="
|
||||
"$GODOT_BIN" --headless --path . res://scenes/server.tscn -- --port "$PORT" >/tmp/mp2p_server.log 2>&1 &
|
||||
SRV_PID=$!
|
||||
sleep 3
|
||||
|
||||
echo "=== starting client B (idle observer) ==="
|
||||
"$GODOT_BIN" --headless --path . --script res://scripts/mp_test_client.gd -- --role b --port "$PORT" >/tmp/mp2p_b.log 2>&1 &
|
||||
B_PID=$!
|
||||
# Wait until B is actually connected before starting A.
|
||||
for _ in $(seq 1 30); do grep -q 'RESULT b connected' /tmp/mp2p_b.log && break; sleep 1; done
|
||||
|
||||
echo "=== running client A (mover) ==="
|
||||
"$GODOT_BIN" --headless --path . --script res://scripts/mp_test_client.gd -- --role a --port "$PORT" 2>&1 | tee /tmp/mp2p_a.log
|
||||
A_LINE=$(grep '^RESULT a' /tmp/mp2p_a.log || true)
|
||||
|
||||
wait "$B_PID" 2>/dev/null
|
||||
B_LINE=$(grep '^RESULT b' /tmp/mp2p_b.log || true)
|
||||
|
||||
echo
|
||||
echo "server: $(grep -c 'joined' /tmp/mp2p_server.log) joins seen"
|
||||
echo "A: ${A_LINE:-<none>}"
|
||||
echo "B: ${B_LINE:-<none>}"
|
||||
|
||||
if echo "$A_LINE" | grep -q 'moved=true' && echo "$A_LINE" | grep -qE 'remotes=[1-9]'; then
|
||||
echo "=== SMOKE RESULT: PASS ==="
|
||||
exit 0
|
||||
fi
|
||||
echo "=== SMOKE RESULT: FAIL ==="
|
||||
exit 1
|
||||
Reference in New Issue
Block a user