#!/usr/bin/env bash
# Layer 13: Real assets over the mesh (textures, models, audio) (HyperOverlay)
#
# Layer 12 moves synthetic noise between two peers. This one moves the game's own
# texture files, checks they still decode as images on arrival, and checks that a
# peer which fetched one can serve it to a third peer after the origin is gone.
#
# Assets are staged from the hyper-tps working tree. They are NOT copied into the
# repo: they are large, they already exist, and a test fixture that duplicates
# 8 MB of PNG into git is its own problem.
set -euo pipefail
cd "$(dirname "$0")"
GAME_DIR="${HYPER_TPS_DIR:-/storage/projects/code/godot/hyper-tps}"
SRC="/tmp/layer13/src"
echo "=== Layer 13: Real assets over the mesh (textures, models, audio) ==="
rm -f /tmp/layer13_status.txt
rm -rf /tmp/layer13
mkdir -p "$SRC"
# ---- Stage real textures ----
# A JPEG and three PNGs of different sizes: enough chunks to exercise the
# scheduler, small enough that the suite stays runnable.
# A MIXED set on purpose. The overlay is content-addressed and type-blind — it
# chunks bytes and verifies blake2b, and nothing in the path inspects a format —
# but "it should be type-agnostic" is a claim, not a result. A game needs to move
# models and audio as much as textures, so the suite moves real ones of each and
# validates them per format on arrival.
STAGED=0
for rel in \
assets/tumble.jpg \
bridge_textures/bridge_Image_0.png \
bridge_textures/bridge_Image_2.png \
fandisk.glb \
assets/models/portal/portal.glb \
assets/sounds/player/generic_robot_jump.wav \
assets/sounds/ambience/spawnpad_hum.wav \
assets/sounds/ambience/constant_natural_wind.mp3
do
if [ -f "$GAME_DIR/$rel" ]; then
cp "$GAME_DIR/$rel" "$SRC/$(basename "$rel")"
STAGED=$((STAGED + 1))
else
echo "[orchestrator] MISSING (skipped): $GAME_DIR/$rel"
fi
done
if [ "$STAGED" -eq 0 ]; then
echo "FAIL: no texture assets could be staged from $GAME_DIR"
echo " set HYPER_TPS_DIR to the hyper-tps working tree"
echo "=== Layer 13: FAILED ==="
exit 1
fi
echo "[orchestrator] staged $STAGED assets ($(du -sh "$SRC" | cut -f1)) from $GAME_DIR"
# ---- Start testnet ----
echo "[orchestrator] starting DHT testnet..."
TESTNET_PID=""
cleanup() {
[ -n "$TESTNET_PID" ] && kill "$TESTNET_PID" 2>/dev/null || true
rm -f /tmp/layer_bootstrap.txt /tmp/layer13_status.txt
}
trap cleanup EXIT
node layer2_testnet.js &
TESTNET_PID=$!
for i in $(seq 1 30); do
[ -f /tmp/layer_bootstrap.txt ] && break
sleep 0.5
done
if [ ! -f /tmp/layer_bootstrap.txt ]; then
echo "FAIL: testnet did not write bootstrap within 15s"
echo "=== Layer 13: FAILED ==="
exit 1
fi
echo "[orchestrator] testnet ready: $(cat /tmp/layer_bootstrap.txt)"
# ---- Run ----
cp layer13_textures.gd project/layer13_textures.gd 2>/dev/null || true
echo "[orchestrator] running in-process tests (single Godot process)..."
OUTPUT=$(timeout -k 10 300 godot4 --headless --path project --script res://layer13_textures.gd 2>&1 || true)
echo "$OUTPUT"
STATUS=$(cat /tmp/layer13_status.txt 2>/dev/null || echo "UNKNOWN")
if [ "$STATUS" = "PASS" ]; then
echo "=== Layer 13: PASS ==="
exit 0
elif echo "$STATUS" | grep -q "^FAIL"; then
echo "=== Layer 13: FAILED ($STATUS) ==="
exit 1
else
echo "=== Layer 13: UNKNOWN ($STATUS) ==="
exit 1
fi