McRogueFace/mcrf-init.sh

422 lines
14 KiB
Bash
Raw Normal View History

#!/bin/bash
#
# mcrf-init.sh - Initialize a McRogueFace game project
#
# Creates a game project directory with symlinks to a pre-built engine,
# so game developers only write Python + assets, never rebuild the engine.
#
# Usage:
# mcrf-init # initialize current directory
# mcrf-init my-game # create and initialize my-game/
#
# Setup (add to ~/.bashrc):
# alias mcrf-init='/path/to/McRogueFace/mcrf-init.sh'
# # or: export PATH="$PATH:/path/to/McRogueFace"
#
# After init:
# make run # run the game
# make dist # package for distribution
set -e
# --- Resolve engine root (where this script lives) ---
ENGINE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# --- Determine game directory ---
if [ -n "$1" ]; then
GAME_DIR="$(pwd)/$1"
mkdir -p "$GAME_DIR"
else
GAME_DIR="$(pwd)"
fi
# --- Colors ---
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BOLD='\033[1m'
NC='\033[0m'
info() { echo -e "${GREEN}[mcrf]${NC} $1"; }
warn() { echo -e "${YELLOW}[mcrf]${NC} $1"; }
error() { echo -e "${RED}[mcrf]${NC} $1"; }
# --- Safety checks ---
if [ "$GAME_DIR" = "$ENGINE_ROOT" ]; then
error "Cannot init inside the engine directory itself."
exit 1
fi
if [ ! -f "$ENGINE_ROOT/build/mcrogueface" ]; then
error "Engine not built. Run 'make' in $ENGINE_ROOT first."
exit 1
fi
# Engine version for packaging
ENGINE_VERSION=$(grep 'MCRFPY_VERSION' "$ENGINE_ROOT/src/McRogueFaceVersion.h" \
| sed 's/.*"\(.*\)"/\1/')
info "Initializing McRogueFace game project"
info " Engine: $ENGINE_ROOT (v${ENGINE_VERSION})"
info " Game: $GAME_DIR"
echo
# --- Create game directories ---
mkdir -p "$GAME_DIR/assets"
mkdir -p "$GAME_DIR/scripts"
# --- Set up build/ (Linux) ---
info "Setting up build/ (Linux)..."
mkdir -p "$GAME_DIR/build"
# Binary
ln -sfn "$ENGINE_ROOT/build/mcrogueface" "$GAME_DIR/build/mcrogueface"
# Shared libraries (entire lib/ tree: .so files + Python stdlib + extensions)
ln -sfn "$ENGINE_ROOT/build/lib" "$GAME_DIR/build/lib"
# Game content: symlink to project's own directories
ln -sfn ../assets "$GAME_DIR/build/assets"
ln -sfn ../scripts "$GAME_DIR/build/scripts"
# --- Set up build-windows/ (if engine has it) ---
if [ -f "$ENGINE_ROOT/build-windows/mcrogueface.exe" ]; then
info "Setting up build-windows/..."
mkdir -p "$GAME_DIR/build-windows"
# Executable
ln -sfn "$ENGINE_ROOT/build-windows/mcrogueface.exe" "$GAME_DIR/build-windows/mcrogueface.exe"
# DLLs
for dll in "$ENGINE_ROOT/build-windows/"*.dll; do
[ -f "$dll" ] && ln -sfn "$dll" "$GAME_DIR/build-windows/$(basename "$dll")"
done
# Python stdlib zip
[ -f "$ENGINE_ROOT/build-windows/python314.zip" ] && \
ln -sfn "$ENGINE_ROOT/build-windows/python314.zip" "$GAME_DIR/build-windows/python314.zip"
# Python lib directory
[ -d "$ENGINE_ROOT/build-windows/lib" ] && \
ln -sfn "$ENGINE_ROOT/build-windows/lib" "$GAME_DIR/build-windows/lib"
# Game content
ln -sfn ../assets "$GAME_DIR/build-windows/assets"
ln -sfn ../scripts "$GAME_DIR/build-windows/scripts"
else
warn "No Windows build found (skipping build-windows/)"
fi
# --- WASM build info ---
info "WASM support: use 'make wasm' to build (requires emsdk)"
# --- Starter game.py ---
if [ ! -f "$GAME_DIR/scripts/game.py" ]; then
info "Creating starter scripts/game.py..."
cat > "$GAME_DIR/scripts/game.py" << 'PYTHON'
"""McRogueFace Game - created by mcrf-init"""
import mcrfpy
scene = mcrfpy.Scene("title")
ui = scene.children
ui.append(mcrfpy.Caption(
text="My McRogueFace Game",
pos=(512, 300),
font=mcrfpy.Font("assets/JetbrainsMono.ttf"),
fill_color=(255, 255, 255),
font_size=48
))
ui.append(mcrfpy.Caption(
text="Press ESC to quit",
pos=(512, 400),
font=mcrfpy.Font("assets/JetbrainsMono.ttf"),
fill_color=(180, 180, 180),
font_size=24
))
def on_key(key, state):
if key == mcrfpy.Key.ESCAPE and state == mcrfpy.InputState.PRESSED:
mcrfpy.exit()
scene.on_key = on_key
mcrfpy.current_scene = scene
PYTHON
fi
# --- Copy a default font if game assets/ is empty ---
if [ -z "$(ls -A "$GAME_DIR/assets/" 2>/dev/null)" ]; then
if [ -f "$ENGINE_ROOT/assets/JetbrainsMono.ttf" ]; then
info "Copying default font to assets/..."
cp "$ENGINE_ROOT/assets/JetbrainsMono.ttf" "$GAME_DIR/assets/"
fi
fi
# --- .gitignore ---
if [ ! -f "$GAME_DIR/.gitignore" ]; then
info "Creating .gitignore..."
cat > "$GAME_DIR/.gitignore" << 'GITIGNORE'
# McRogueFace game project
build/
build-windows/
build-wasm/
dist/
__pycache__/
*.pyc
*.png.bak
GITIGNORE
fi
# --- pyrightconfig.json (for Vim/LSP autocomplete) ---
if [ ! -f "$GAME_DIR/pyrightconfig.json" ]; then
info "Creating pyrightconfig.json (IDE autocomplete)..."
cat > "$GAME_DIR/pyrightconfig.json" << PYRIGHT
{
"include": ["scripts"],
"extraPaths": ["${ENGINE_ROOT}/stubs"],
"pythonVersion": "3.14",
"pythonPlatform": "Linux",
"typeCheckingMode": "basic",
"reportMissingModuleSource": false
}
PYRIGHT
fi
# --- Makefile ---
info "Creating Makefile..."
cat > "$GAME_DIR/Makefile" << MAKEFILE
# McRogueFace Game Project Makefile
# Generated by mcrf-init
#
# Engine: ${ENGINE_ROOT}
ENGINE_ROOT := ${ENGINE_ROOT}
GAME_NAME := $(basename "$GAME_DIR")
GAME_DIR := \$(shell pwd)
# Game version - edit this or create a VERSION file
VERSION := \$(shell cat VERSION 2>/dev/null || echo "0.1.0")
# Engine version (from the linked build)
ENGINE_VERSION := \$(shell grep 'MCRFPY_VERSION' "\$(ENGINE_ROOT)/src/McRogueFaceVersion.h" \\
| sed 's/.*"\\(.*\\)"/\\1/' 2>/dev/null || echo "unknown")
.PHONY: run run-windows wasm serve-wasm dist dist-linux dist-windows dist-wasm clean-dist clean-wasm info
# ============================================================
# Run targets
# ============================================================
run:
@cd build && ./mcrogueface
run-headless:
@cd build && ./mcrogueface --headless --exec ../scripts/game.py
run-windows:
@echo "Launch build-windows/mcrogueface.exe on a Windows machine or via Wine"
serve-wasm:
@if [ ! -f build-wasm/mcrogueface.html ]; then \\
echo "No WASM build found. Run 'make wasm' first."; \\
exit 1; \\
fi
@echo "Serving WASM build at http://localhost:8080"
@cd build-wasm && python3 -m http.server 8080
# ============================================================
# WASM build (requires emsdk)
# ============================================================
wasm:
@if ! command -v emcmake >/dev/null 2>&1; then \\
echo "Error: emcmake not found. Activate emsdk first:"; \\
echo " source ~/emsdk/emsdk_env.sh"; \\
exit 1; \\
fi
@echo "Building WASM with game assets..."
@emcmake cmake -S "\$(ENGINE_ROOT)" -B build-wasm \\
-DMCRF_SDL2=ON \\
-DMCRF_GAME_SHELL=ON \\
-DMCRF_ASSETS_DIR="\$(GAME_DIR)/assets" \\
-DMCRF_SCRIPTS_DIR="\$(GAME_DIR)/scripts"
@emmake make -C build-wasm -j\$\$(nproc)
@echo ""
@echo "WASM build complete: build-wasm/"
@echo " make serve-wasm - test in browser"
@echo " make dist-wasm - package for distribution"
clean-wasm:
@rm -rf build-wasm
@echo "Cleaned build-wasm/"
# ============================================================
# Distribution packaging
# ============================================================
dist: dist-linux dist-windows dist-wasm
@echo ""
@echo "=== Packages ==="
@ls -lh dist/ 2>/dev/null
dist-linux:
@if [ ! -f build/mcrogueface ]; then \\
echo "Error: build/mcrogueface not found. Run mcrf-init first."; \\
exit 1; \\
fi
@echo "Packaging \$(GAME_NAME) for Linux..."
@mkdir -p dist
\$(eval PKG := dist/\$(GAME_NAME)-\$(VERSION)-Linux)
@rm -rf \$(PKG)
@mkdir -p \$(PKG)/lib
@# Binary
@cp "\$(ENGINE_ROOT)/build/mcrogueface" \$(PKG)/
@# Shared libraries
@for lib in libpython3.14.so.1.0 \\
libsfml-graphics.so.2.6.1 libsfml-window.so.2.6.1 \\
libsfml-system.so.2.6.1 libsfml-audio.so.2.6.1 \\
libtcod.so; do \\
[ -f "\$(ENGINE_ROOT)/__lib/\$\$lib" ] && \\
cp "\$(ENGINE_ROOT)/__lib/\$\$lib" \$(PKG)/lib/; \\
done
@# Library symlinks
@cd \$(PKG)/lib && \\
ln -sf libpython3.14.so.1.0 libpython3.14.so && \\
ln -sf libsfml-graphics.so.2.6.1 libsfml-graphics.so.2.6 && \\
ln -sf libsfml-graphics.so.2.6.1 libsfml-graphics.so && \\
ln -sf libsfml-window.so.2.6.1 libsfml-window.so.2.6 && \\
ln -sf libsfml-window.so.2.6.1 libsfml-window.so && \\
ln -sf libsfml-system.so.2.6.1 libsfml-system.so.2.6 && \\
ln -sf libsfml-system.so.2.6.1 libsfml-system.so && \\
ln -sf libsfml-audio.so.2.6.1 libsfml-audio.so.2.6 && \\
ln -sf libsfml-audio.so.2.6.1 libsfml-audio.so && \\
ln -sf libtcod.so libtcod.so.2
@# Python extension modules
@if [ -d "\$(ENGINE_ROOT)/__lib/Python/lib.linux-x86_64-3.14" ]; then \\
mkdir -p \$(PKG)/lib/Python/lib.linux-x86_64-3.14; \\
for so in "\$(ENGINE_ROOT)/__lib/Python/lib.linux-x86_64-3.14"/*.so; do \\
case "\$\$(basename \$\$so)" in \\
*test*|xxlimited*|_ctypes_test*|_xxtestfuzz*) continue ;; \\
*) cp "\$\$so" \$(PKG)/lib/Python/lib.linux-x86_64-3.14/ ;; \\
esac; \\
done; \\
fi
@# Python stdlib
@if [ -d "\$(ENGINE_ROOT)/__lib/Python/Lib" ]; then \\
mkdir -p \$(PKG)/lib/Python; \\
cp -r "\$(ENGINE_ROOT)/__lib/Python/Lib" \$(PKG)/lib/Python/; \\
rm -rf \$(PKG)/lib/Python/Lib/test \$(PKG)/lib/Python/Lib/tests \\
\$(PKG)/lib/Python/Lib/idlelib \$(PKG)/lib/Python/Lib/tkinter \\
\$(PKG)/lib/Python/Lib/turtledemo \$(PKG)/lib/Python/Lib/pydoc_data \\
\$(PKG)/lib/Python/Lib/lib2to3 \$(PKG)/lib/Python/Lib/ensurepip \\
\$(PKG)/lib/Python/Lib/_pyrepl; \\
find \$(PKG)/lib/Python/Lib -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true; \\
find \$(PKG)/lib/Python/Lib -name "*.pyc" -delete 2>/dev/null || true; \\
find \$(PKG)/lib/Python/Lib -name "test_*.py" -delete 2>/dev/null || true; \\
fi
@# Game content (real copies, not symlinks)
@cp -r assets \$(PKG)/assets
@cp -r scripts \$(PKG)/scripts
@# Run script
@printf '#!/bin/bash\\nDIR="\$\$(cd "\$\$(dirname "\$\$0")" && pwd)"\\nexport LD_LIBRARY_PATH="\$\$DIR/lib:\$\$LD_LIBRARY_PATH"\\nexec "\$\$DIR/mcrogueface" "\$\$@"\\n' > \$(PKG)/run.sh
@chmod +x \$(PKG)/run.sh
@# Archive
@cd dist && tar -czf "\$(GAME_NAME)-\$(VERSION)-Linux.tar.gz" "\$(GAME_NAME)-\$(VERSION)-Linux"
@rm -rf \$(PKG)
@echo "Created: dist/\$(GAME_NAME)-\$(VERSION)-Linux.tar.gz"
dist-windows:
@if [ ! -f "\$(ENGINE_ROOT)/build-windows/mcrogueface.exe" ]; then \\
echo "No Windows build available. Skipping."; \\
exit 0; \\
fi
@echo "Packaging \$(GAME_NAME) for Windows..."
@mkdir -p dist
\$(eval PKG := dist/\$(GAME_NAME)-\$(VERSION)-Windows)
@rm -rf \$(PKG)
@mkdir -p \$(PKG)
@# Executable and DLLs
@cp "\$(ENGINE_ROOT)/build-windows/mcrogueface.exe" \$(PKG)/
@for dll in "\$(ENGINE_ROOT)/build-windows/"*.dll; do \\
[ -f "\$\$dll" ] && cp "\$\$dll" \$(PKG)/; \\
done
@# Python stdlib zip
@[ -f "\$(ENGINE_ROOT)/build-windows/python314.zip" ] && \\
cp "\$(ENGINE_ROOT)/build-windows/python314.zip" \$(PKG)/ || true
@# Python lib directory
@if [ -d "\$(ENGINE_ROOT)/build-windows/lib" ]; then \\
cp -r "\$(ENGINE_ROOT)/build-windows/lib" \$(PKG)/lib; \\
fi
@# Game content (real copies)
@cp -r assets \$(PKG)/assets
@cp -r scripts \$(PKG)/scripts
@# Archive
@cd dist && zip -qr "\$(GAME_NAME)-\$(VERSION)-Windows.zip" "\$(GAME_NAME)-\$(VERSION)-Windows"
@rm -rf \$(PKG)
@echo "Created: dist/\$(GAME_NAME)-\$(VERSION)-Windows.zip"
dist-wasm:
@if ! command -v emcmake >/dev/null 2>&1; then \\
echo "WASM: emsdk not activated. Skipping WASM package."; \\
echo " To include WASM: source ~/emsdk/emsdk_env.sh && make dist-wasm"; \\
exit 0; \\
fi; \\
if [ ! -f build-wasm/mcrogueface.html ]; then \\
echo "Building WASM first..."; \\
\$(MAKE) wasm || exit 1; \\
fi; \\
echo "Packaging \$(GAME_NAME) for Web..."; \\
mkdir -p dist; \\
PKG="dist/\$(GAME_NAME)-\$(VERSION)-Web"; \\
rm -rf "\$\$PKG"; \\
mkdir -p "\$\$PKG"; \\
cp build-wasm/mcrogueface.html "\$\$PKG/index.html"; \\
cp build-wasm/mcrogueface.js "\$\$PKG/"; \\
cp build-wasm/mcrogueface.wasm "\$\$PKG/"; \\
cp build-wasm/mcrogueface.data "\$\$PKG/"; \\
cd dist && zip -qr "\$(GAME_NAME)-\$(VERSION)-Web.zip" "\$(GAME_NAME)-\$(VERSION)-Web"; \\
rm -rf "\$\$PKG"; \\
echo "Created: dist/\$(GAME_NAME)-\$(VERSION)-Web.zip"
clean-dist:
@rm -rf dist
@echo "Cleaned dist/"
# ============================================================
# Info
# ============================================================
info:
@echo "Game: \$(GAME_NAME) v\$(VERSION)"
@echo "Engine: McRogueFace v\$(ENGINE_VERSION)"
@echo "Root: \$(ENGINE_ROOT)"
@echo ""
@echo "Targets:"
@echo " make run Run the game"
@echo " make run-headless Run headless (for testing)"
@echo " make wasm Build for web (requires emsdk)"
@echo " make serve-wasm Test WASM build in browser"
@echo " make dist Package for all platforms"
@echo " make dist-linux Package for Linux only"
@echo " make dist-windows Package for Windows only"
@echo " make dist-wasm Package for web only"
@echo " make clean-dist Remove dist/"
@echo " make clean-wasm Remove build-wasm/"
MAKEFILE
# --- VERSION file ---
if [ ! -f "$GAME_DIR/VERSION" ]; then
echo "0.1.0" > "$GAME_DIR/VERSION"
fi
# --- Done ---
echo
info "Project ready!"
echo
echo -e " ${BOLD}make run${NC} - play the game"
echo -e " ${BOLD}make dist${NC} - package for distribution"
echo -e " ${BOLD}make info${NC} - show project info"
echo
echo -e " Edit ${BOLD}scripts/game.py${NC} and ${BOLD}assets/${NC} to build your game."
echo -e " Edit ${BOLD}VERSION${NC} to set your game version."