Server launcher and start-up checks for other machines

- server/run_server.sh: checks Godot is 4.7+, imports a fresh copy once
  (the class cache in .godot/ isn't in git), then starts the server
- The dedicated server refuses to start, with a clear reason, on Godot
  older than 4.7 or when the game's scripts failed to load, instead of
  claiming to listen
- tests/run_all.sh imports a fresh copy first
- Setup doc: 4.7+ (not 4.3+), importing a fresh copy, running a server
This commit is contained in:
Paul Bailey
2026-11-04 15:34:28 +00:00
parent d0f473e57b
commit ae2592822e
4 changed files with 97 additions and 4 deletions

View File

@@ -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 in it except **Godot itself** (which you install once per machine). No assets to
download — the M0 world is generated in code. 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# - **macOS / Windows / Linux:** download the *standard* build (NOT the .NET/C#
build — this project is pure GDScript) from https://godotengine.org/download 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 - Use **4.7.x or newer 4.x**. The project is on `4.7.2.stable`. **Older
older editor will warn that the project is from a newer version). 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, - 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 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 ## Tests
Run everything (fails on any failed check or script error): Run everything (fails on any failed check or script error):

View File

@@ -144,7 +144,26 @@ func is_server() -> bool:
# --- server ------------------------------------------------------------------- # --- 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: 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) var err := start_server(String(_args.get("config", ServerConfig.DEFAULT_PATH)), _args)
if not err.is_empty(): if not err.is_empty():
printerr("[server] ", err) printerr("[server] ", err)

44
server/run_server.sh Executable file
View File

@@ -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 "$@"

View File

@@ -4,6 +4,11 @@
# Usage: tests/run_all.sh (from the repo root; needs `godot` on PATH) # Usage: tests/run_all.sh (from the repo root; needs `godot` on PATH)
cd "$(dirname "$0")/.." || exit 1 cd "$(dirname "$0")/.." || exit 1
status=0 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 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="" extra=""
[ "$t" = "test_boundary" ] && extra="--fixed-fps 30" [ "$t" = "test_boundary" ] && extra="--fixed-fps 30"