Initial commit: Transcience MVP
Top-down twin-stick bullet-hell, Godot 4.7, server-authoritative dedicated server with client-side prediction. Clients send input only; the server resolves every hit for both players and enemies (no PvP). - SimWorld: whole simulation as plain RefCounted objects (no nodes, no physics server), ~0.24ms/tick at peak load -- runs headless for free and drives 78 tests in under a second - BulletPool: struct-of-arrays bullet storage, replicated as spawn/despawn events rather than per-tick state - Emitter framework (Ring/AimedSpread/WallGap/ArcSweep) shared by trash enemies and bosses -- a new boss is data in src/content/content.gd, no simulation changes - The Warden of the Fold: stationary 4-phase boss built entirely on that format - Lobby hub with a portal into on-demand dungeon instances; one process hosts the hub plus every concurrent dungeon - Emergency escape: 3s server-owned channel, cancelled by damage - tools/check.sh, test.sh (GUT), smoke.sh (real server + bot clients over ENet), bench.gd; git hooks wired to the same scripts - docs/ARCHITECTURE.md, NETCODE.md, WORKFLOW.md, ROADMAP.md
This commit is contained in:
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
# End-to-end integration test: boot a dedicated server, connect two scripted
|
||||
# bot clients over a real ENet socket, and assert that the whole loop happened --
|
||||
# handshake, lobby, portal into a dungeon, and the emergency escape back.
|
||||
#
|
||||
# This is the test that catches everything unit tests structurally cannot: RPC
|
||||
# wiring, codec round-trips over the wire, instance transfers, and the client's
|
||||
# reconciliation loop. Run it before calling any networking change done.
|
||||
#
|
||||
# tools/smoke.sh # ~35s
|
||||
# KEEP=1 tools/smoke.sh # keep the logs and print where they are
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
GODOT="${GODOT:-godot}"
|
||||
PORT="${PORT:-27099}"
|
||||
SERVER_TICKS="${SERVER_TICKS:-2000}"
|
||||
CLIENT_TICKS="${CLIENT_TICKS:-1700}"
|
||||
OUT="$(mktemp -d -t transcience-smoke-XXXXXX)"
|
||||
|
||||
cleanup() {
|
||||
kill %1 %2 %3 2>/dev/null
|
||||
wait 2>/dev/null
|
||||
if [[ "${KEEP:-0}" == "1" ]]; then
|
||||
echo "logs kept in $OUT"
|
||||
else
|
||||
rm -rf "$OUT"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "smoke: server on port $PORT, logs in $OUT"
|
||||
"$GODOT" --headless --path . -- --server --port "$PORT" --autoquit "$SERVER_TICKS" \
|
||||
> "$OUT/server.log" 2>&1 &
|
||||
|
||||
for _ in $(seq 1 60); do
|
||||
grep -q "SERVER_READY" "$OUT/server.log" 2>/dev/null && break
|
||||
sleep 0.25
|
||||
done
|
||||
if ! grep -q "SERVER_READY" "$OUT/server.log" 2>/dev/null; then
|
||||
echo "FAIL: server never became ready"; cat "$OUT/server.log"; exit 1
|
||||
fi
|
||||
|
||||
for n in 1 2; do
|
||||
"$GODOT" --headless --path . -- --bot --host 127.0.0.1 --port "$PORT" \
|
||||
--name "bot$n" --autoquit "$CLIENT_TICKS" > "$OUT/bot$n.log" 2>&1 &
|
||||
sleep 0.4
|
||||
done
|
||||
|
||||
wait %2 %3 2>/dev/null
|
||||
wait %1 2>/dev/null
|
||||
|
||||
fails=0
|
||||
check() { # check <label> <file> <pattern>
|
||||
if grep -qE "$3" "$2"; then
|
||||
echo " ok $1"
|
||||
else
|
||||
echo " FAIL $1 (no match for /$3/ in $(basename "$2"))"
|
||||
fails=$((fails + 1))
|
||||
fi
|
||||
}
|
||||
refute() { # refute <label> <file> <pattern>
|
||||
local hits
|
||||
hits=$(grep -cE "$3" "$2" || true)
|
||||
if [[ "$hits" == "0" ]]; then
|
||||
echo " ok $1"
|
||||
else
|
||||
echo " FAIL $1 ($hits line(s) matched /$3/ in $(basename "$2"))"
|
||||
grep -E "$3" "$2" | head -5 | sed 's/^/ /'
|
||||
fails=$((fails + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "assertions:"
|
||||
check "server accepted bot1" "$OUT/server.log" "joined as 'bot1'"
|
||||
check "server accepted bot2" "$OUT/server.log" "joined as 'bot2'"
|
||||
check "a dungeon instance opened" "$OUT/server.log" "opened dungeon instance"
|
||||
check "emergency escape completed" "$OUT/server.log" "escaped to lobby"
|
||||
check "bot1 reached a dungeon" "$OUT/bot1.log" "entered instance .*DUNGEON"
|
||||
check "bot1 returned to the lobby" "$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"
|
||||
|
||||
echo
|
||||
if [[ $fails -eq 0 ]]; then
|
||||
echo "SMOKE PASS"
|
||||
else
|
||||
echo "SMOKE FAIL ($fails)"
|
||||
echo "--- server.log (tail) ---"; tail -40 "$OUT/server.log"
|
||||
echo "--- bot1.log (tail) ---"; tail -40 "$OUT/bot1.log"
|
||||
fi
|
||||
exit $fails
|
||||
Reference in New Issue
Block a user