diff --git a/docs/SETUP_ON_NEW_MACHINE.md b/docs/SETUP_ON_NEW_MACHINE.md index b8eee82..630520f 100644 --- a/docs/SETUP_ON_NEW_MACHINE.md +++ b/docs/SETUP_ON_NEW_MACHINE.md @@ -4,14 +4,39 @@ This project is a self-contained git repo. Everything needed to build and run is in it except **Godot itself** (which you install once per machine). No assets to download — the M0 world is generated in code. -## 1. Install Godot 4.3+ +## 1. Install Godot 4.7+ - **macOS / Windows / Linux:** download the *standard* build (NOT the .NET/C# build — this project is pure GDScript) from https://godotengine.org/download -- Use **4.7.x or newer 4.x**. The project was upgraded to `4.7.2.stable` (an - older editor will warn that the project is from a newer version). +- Use **4.7.x or newer 4.x**. The project is on `4.7.2.stable`. **Older + versions (4.3 etc.) can't run it**: they fail with script errors. Check + with `godot --version`, or `/Applications/Godot.app/Contents/MacOS/Godot + --version` on a Mac. - macOS shortcut: `brew install --cask godot`. If Homebrew can't unpack it, download the zip from the GitHub release page and move `Godot.app` to - `/Applications`. + `/Applications` (replacing any older one). + +## 2. First run on a fresh copy: import once +Godot keeps its list of named classes (e.g. `Inventory`) in `.godot/`, which +isn't in git. Opening the project in the editor creates it. If you only run +**headless** (a server, or the tests), import once after cloning: +```bash +godot --headless --path . --import +``` +Without it you get errors like `Could not find type "Inventory"`. +`server/run_server.sh` and `tests/run_all.sh` do this for you. + +## 3. Running a dedicated server +```bash +server/run_server.sh # settings from server.json +server/run_server.sh --server-name "Paul's island" --password secret --port 24242 +``` +It checks the Godot version, imports a fresh copy, then starts the server on +UDP 24242 with no window. Settings live in `server.json` (its path is printed +at start-up; on macOS it's under +`~/Library/Application Support/Godot/app_userdata/Valheimlike/server/`). +Stop it with Ctrl+C. Players join with **Join Game** on the title screen. Over +Tailscale, use the server machine's 100.x address or its machine name. See +`docs/MULTIPLAYER_PLAN.md` for all the options. ## Tests Run everything (fails on any failed check or script error): diff --git a/scripts/net/net.gd b/scripts/net/net.gd index 7a25acd..baaaf1a 100644 --- a/scripts/net/net.gd +++ b/scripts/net/net.gd @@ -144,7 +144,26 @@ func is_server() -> bool: # --- server ------------------------------------------------------------------- +## Why this Godot can't run the server ("" = it can): too old, or the game's +## scripts failed to load (usually a fresh copy that's never been imported). +func startup_problem() -> String: + var v := Engine.get_version_info() + if v.major < 4 or (v.major == 4 and v.minor < 7): + return "This game needs Godot 4.7 or newer, but this is %s. Download 4.7.2 from https://godotengine.org/download" % v.string + var root: Window = (Engine.get_main_loop() as SceneTree).root + for n in ["ItemDB", "Actions", "Saves"]: + var node := root.get_node_or_null(n) + if node == null or node.get_script() == null: + return "The game's scripts didn't load (see the errors above). If this is a fresh copy of the project, " + \ + "import it once first: godot --headless --path . --import (server/run_server.sh does this for you)" + return "" + func _start_dedicated() -> void: + var bad := startup_problem() + if not bad.is_empty(): + printerr("[server] Not starting: ", bad) + get_tree().quit(1) + return var err := start_server(String(_args.get("config", ServerConfig.DEFAULT_PATH)), _args) if not err.is_empty(): printerr("[server] ", err) diff --git a/server/run_server.sh b/server/run_server.sh new file mode 100755 index 0000000..d8bb032 --- /dev/null +++ b/server/run_server.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# Starts a dedicated Valheimlike server (no window, no local player). +# +# server/run_server.sh defaults from server.json +# server/run_server.sh --port 24242 --server-name "Paul's island" --password secret +# +# Any flags are passed to the server (see docs/MULTIPLAYER_PLAN.md §2). +# Set GODOT=/path/to/godot to use a particular Godot. Stop it with Ctrl+C. +# +# Before starting it checks that Godot is 4.7 or newer, and imports a fresh +# copy of the project once (Godot's class cache lives in .godot/, which isn't +# in git, so a new clone can't run headless until it has been imported). + +cd "$(dirname "$0")/.." || exit 1 + +GODOT="${GODOT:-godot}" +if ! command -v "$GODOT" >/dev/null 2>&1; then + # macOS: use the app bundle if `godot` isn't on the PATH. + if [ -x /Applications/Godot.app/Contents/MacOS/Godot ]; then + GODOT=/Applications/Godot.app/Contents/MacOS/Godot + else + echo "Can't find Godot. Install 4.7.2 from https://godotengine.org/download, or set GODOT=/path/to/godot" >&2 + exit 1 + fi +fi + +version=$("$GODOT" --version 2>/dev/null | tail -n 1) +major=$(echo "$version" | cut -d. -f1) +minor=$(echo "$version" | cut -d. -f2) +if [ -z "$major" ] || [ "$major" -lt 4 ] || { [ "$major" -eq 4 ] && [ "$minor" -lt 7 ]; }; then + echo "This game needs Godot 4.7 or newer, but $GODOT is ${version:-unknown}." >&2 + echo "Download 4.7.2 from https://godotengine.org/download (or set GODOT=/path/to/a/newer/godot)" >&2 + exit 1 +fi + +if [ ! -f .godot/global_script_class_cache.cfg ]; then + echo "First run on this copy: importing the project (a few seconds)..." + if ! "$GODOT" --headless --path . --import >/dev/null 2>&1 || [ ! -f .godot/global_script_class_cache.cfg ]; then + echo "Import failed. Try: $GODOT --headless --path . --import" >&2 + exit 1 + fi +fi + +exec "$GODOT" --headless --path . -- --server "$@" diff --git a/tests/run_all.sh b/tests/run_all.sh index 76a97b8..5e160ea 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -4,6 +4,11 @@ # Usage: tests/run_all.sh (from the repo root; needs `godot` on PATH) cd "$(dirname "$0")/.." || exit 1 status=0 +# A fresh clone has no class cache yet (.godot/ isn't in git): import once. +if [ ! -f .godot/global_script_class_cache.cfg ]; then + echo "Importing the project first (fresh copy)..." + godot --headless --path . --import >/dev/null 2>&1 +fi for t in test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_fishing_diet test_net_lobby test_net_world test_terrain_edges test_boundary; do extra="" [ "$t" = "test_boundary" ] && extra="--fixed-fps 30"