0.6.1: Emberwild is now Cinderwild (the old name's taken on Steam). Saves, identity and settings are copied across from the Emberwild folder on first start; the site moves to cinderwild.psbailey.uk; same protocol (18), so 0.6.0 and 0.6.1 play together

This commit is contained in:
Paul Bailey
2026-11-07 16:14:11 +00:00
parent 04b9d17826
commit c2c86b5f78
36 changed files with 262 additions and 173 deletions

View File

@@ -16,8 +16,10 @@ addon SDK with guard rails.
## Status
See `docs/ROADMAP.md` for milestones and `docs/ARCHITECTURE.md` for the design.
**Emberwild 0.6.0** is released (download page and player guide:
https://emberwild-95k.pages.dev, builds on the Gitea releases). Built so far:
**Cinderwild 0.6.1** is released (download page and player guide:
https://cinderwild.psbailey.uk, builds on the Gitea releases). Up to 0.6.0 it
was called Emberwild (a name already taken on Steam); before that, Valheimlike.
Built so far:
- M0-M1: the island, survival, crafting, building, weather, wildlife, hunting, fishing.
- M2: self-hosted multiplayer (Host Game, dedicated servers, rules, chat, admin).
- P1-P5 (`docs/PROGRESSION_DESIGN.md`): iron, stone building, the wolf moon and
@@ -60,7 +62,7 @@ godot --headless --path . -- --server
## Packaged builds
```bash
tools/export.sh # build/Emberwild-<version>-macos.zip and -windows.zip
tools/export.sh # build/Cinderwild-<version>-macos.zip and -windows.zip
tools/export.sh macOS # or just one ("macOS" / "Windows")
```
Needs the Godot export templates for your Godot version (Editor > Manage
@@ -69,17 +71,17 @@ Export Templates). The version number is `application/short_version` in
- **Mac:** a universal .app, ad-hoc signed but not notarised. On another Mac,
the first launch needs right-click > Open (or System Settings > Privacy &
Security > Open Anyway).
- **Windows:** unzip and run `Emberwild.exe` (keep `Emberwild.pck` beside
- **Windows:** unzip and run `Cinderwild.exe` (keep `Cinderwild.pck` beside
it). It's unsigned, so SmartScreen says "Windows protected your PC":
More info > Run anyway.
- A packaged copy is also a dedicated server:
`Emberwild.app/Contents/MacOS/Emberwild --headless -- --server` (Mac) or
`Emberwild.exe --headless -- --server` (Windows). Saves and settings live in
`Cinderwild.app/Contents/MacOS/Cinderwild --headless -- --server` (Mac) or
`Cinderwild.exe --headless -- --server` (Windows). Saves and settings live in
the same user folder as a copy run from source.
## Trailer and download page
```bash
tools/trailer/make_trailer.sh # build/Emberwild-trailer.mp4: recorded in-game (Movie Maker), needs ffmpeg
tools/trailer/make_trailer.sh # build/Cinderwild-trailer.mp4: recorded in-game (Movie Maker), needs ffmpeg
tools/site/make_shots.sh # build/shots/*.jpg: the page's screenshots, rendered in a window
tools/site/make_site.sh # build/site/: index.html, guide.html, the shots, poster and trailer for the current release
```

View File

@@ -46,7 +46,7 @@ made a little older (deeper, slower, warmer) by `tools/voice/make_voice.py`.
The recordings are generated from our own text; rebuild them with
`tools/voice/make_voice.sh` after changing any line.
## Emberwild's own (CC0)
## Cinderwild's own (CC0)
`music/`, `ambience/` and `sfx/synth/` are synthesised from scratch by
`tools/audio/make_audio.py` (no samples): the five music tracks (calm,
night, storm, danger, boss), the fire crackle, the creature voices, and the

View File

@@ -25,8 +25,8 @@ elsewhere, use **Tailscale** (recommended) or port forwarding (below).
- **Leaving** (Disconnect, or quitting the game) saves the island and
stops the server. Next time, Host Game carries on with the same island
(the seed field is locked once it exists).
- Its files: `~/Library/Application Support/Godot/app_userdata/Emberwild/host/`
(macOS; `%APPDATA%\Godot\app_userdata\Emberwild\host\` on Windows):
- Its files: `~/Library/Application Support/Godot/app_userdata/Cinderwild/host/`
(macOS; `%APPDATA%\Godot\app_userdata\Cinderwild\host\` on Windows):
`server.json`, `server.log`, and the world in `worlds/server-world/`.
- macOS may ask once whether Godot may accept incoming network
connections: say **Allow**, or friends can't connect.
@@ -47,14 +47,14 @@ The first player to join a new server becomes its admin (so join it
yourself before telling friends the address). More admins: `/admin <name>`.
**As a service** (starts on boot, restarts if it stops):
- **macOS:** `server/com.emberwild.server.plist`: edit the paths in it,
then `cp server/com.emberwild.server.plist ~/Library/LaunchAgents/` and
`launchctl load -w ~/Library/LaunchAgents/com.emberwild.server.plist`.
- **macOS:** `server/com.cinderwild.server.plist`: edit the paths in it,
then `cp server/com.cinderwild.server.plist ~/Library/LaunchAgents/` and
`launchctl load -w ~/Library/LaunchAgents/com.cinderwild.server.plist`.
It runs while you're logged in (set the Mac to log in automatically for an
always-on server). Log: `~/Library/Logs/emberwild-server.log`.
- **Linux:** `server/emberwild-server.service`: the steps are in the file
(`sudo systemctl enable --now emberwild-server`; logs with
`journalctl -u emberwild-server -f`).
always-on server). Log: `~/Library/Logs/cinderwild-server.log`.
- **Linux:** `server/cinderwild-server.service`: the steps are in the file
(`sudo systemctl enable --now cinderwild-server`; logs with
`journalctl -u cinderwild-server -f`).
As a service there's no terminal to type commands into: use chat in game,
or edit `server.json` and restart.
@@ -76,7 +76,7 @@ the ones you'll want first:
| `difficulty` | normal | easy / normal / hard |
| `sleep` | all_online | or `half`, `anyone`, `off` |
| `backups_kept`, `backup_hours` | 10, 6 | see below |
| `check_updates` | true | at start-up, say in the log if a newer Emberwild is out (and whether its protocol differs, so new players couldn't join) |
| `check_updates` | true | at start-up, say in the log if a newer Cinderwild is out (and whether its protocol differs, so new players couldn't join) |
| `allowlist`, `banned`, `admins` | | player ids (use `/ban`, `/admin` rather than editing) |
## Backups

View File

@@ -1,14 +1,14 @@
# Emberwild 0.6.0 playtest
# Cinderwild 0.6.0 playtest
Thanks for playing. This build is the **Valley** and the **Cove**, and
we'd like to know what breaks, what's confusing, and what's no fun. The
game's player guide is on the download page: https://emberwild-95k.pages.dev
game's player guide is on the download page: https://cinderwild.psbailey.uk
## Reporting a bug
Press **Esc** in the game, then **Report a bug**:
- **Open the bug report page** opens a new issue on the Emberwild Gitea page
- **Open the bug report page** opens a new issue on the Cinderwild Gitea page
in your browser, already filled in with your version, computer, mode,
land, the in-game time and where you are. Say what happened, what you
expected, and how to make it happen again, then press **Create Issue**.
@@ -25,9 +25,9 @@ Where the log is:
| | |
|---|---|
| Mac | `~/Library/Application Support/Godot/app_userdata/Emberwild/logs/godot.log` |
| Windows | `%APPDATA%\Godot\app_userdata\Emberwild\logs\godot.log` |
| Linux | `~/.local/share/godot/app_userdata/Emberwild/logs/godot.log` |
| Mac | `~/Library/Application Support/Godot/app_userdata/Cinderwild/logs/godot.log` |
| Windows | `%APPDATA%\Godot\app_userdata\Cinderwild\logs\godot.log` |
| Linux | `~/.local/share/godot/app_userdata/Cinderwild/logs/godot.log` |
A screenshot helps for anything that looks wrong (Cmd+Shift+4 on a Mac,
Win+Shift+S on Windows).

View File

@@ -121,7 +121,7 @@ an out-of-date game can't join an up-to-date server: today that means
`git pull` and a restart on every machine.
*Built so far (the check):* `scripts/core/updates.gd` asks the feed in
Project Settings (`emberwild/updates/feed`, now the git server's
Project Settings (`cinderwild/updates/feed`, now the git server's
`releases/latest` API; Gitea, Forgejo and GitHub answer alike) once at
start-up. It reads the release's `version.json` asset (written by
`tools/export.sh`: version, protocol, per-platform file, sha256 and size).

View File

@@ -40,7 +40,7 @@ server/run_server.sh --server-name "Paul's island" --password secret --port 2424
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/Emberwild/server/`).
`~/Library/Application Support/Godot/app_userdata/Cinderwild/server/`).
The shared island is saved in `worlds/server-world/` in that same folder,
within 10 seconds of any change (and when someone leaves). Stop it with
Ctrl+C. Players join with **Join Game** on the title screen. Over
@@ -82,13 +82,13 @@ godot --headless --path . --script res://tests/test_survival_stats.gd
## Save files
Worlds are per machine, under
`~/Library/Application Support/Godot/app_userdata/Emberwild/worlds/` on
macOS (`%APPDATA%\Godot\app_userdata\Emberwild\worlds\` on Windows). Copy a
`~/Library/Application Support/Godot/app_userdata/Cinderwild/worlds/` on
macOS (`%APPDATA%\Godot\app_userdata\Cinderwild\worlds\` on Windows). Copy a
world's folder across to move it to another machine.
The game used to be called Valheimlike, and its data lived in
`app_userdata/Valheimlike/`. The first time Emberwild starts it copies worlds,
your identity and server settings across to `app_userdata/Emberwild/`
`app_userdata/Valheimlike/`. The first time Cinderwild starts it copies worlds,
your identity and server settings across to `app_userdata/Cinderwild/`
(`scripts/core/boot.gd`). The old folder is left alone as a backup, and you
can delete it once you're happy.

View File

@@ -275,7 +275,7 @@ small Diet bars, and a low group blinks.
- **Autosave** every 2 minutes, on Save & Quit (to the title or desktop) and
when the window is closed.
- Saves live in `user://worlds/<id>/` (on macOS:
`~/Library/Application Support/Godot/app_userdata/Emberwild/worlds/`) as
`~/Library/Application Support/Godot/app_userdata/Cinderwild/worlds/`) as
`meta.json` + `world.json`. Writes are atomic, and the previous save is
kept as `world.json.bak`.
- A save stores only what the player changed: player position and look,

View File

@@ -8,7 +8,7 @@ custom_features=""
export_filter="all_resources"
include_filter="data/*.json, data/worldlets/*.json"
exclude_filter="tests/*, docs/*, tools/*, server/*"
export_path="build/macos/Emberwild.zip"
export_path="build/macos/Cinderwild.zip"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -26,11 +26,11 @@ custom_template/release=""
debug/export_console_wrapper=0
application/icon=""
application/icon_interpolation=4
application/bundle_identifier="com.pbailey.emberwild"
application/bundle_identifier="com.pbailey.cinderwild"
application/signature=""
application/app_category="Games"
application/short_version="0.6.0"
application/version="0.6.0"
application/short_version="0.6.1"
application/version="0.6.1"
application/copyright=""
display/high_res=true
codesign/codesign=1
@@ -66,7 +66,7 @@ custom_features=""
export_filter="all_resources"
include_filter="data/*.json, data/worldlets/*.json"
exclude_filter="tests/*, docs/*, tools/*, server/*"
export_path="build/windows/Emberwild/Emberwild.exe"
export_path="build/windows/Cinderwild/Cinderwild.exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -89,11 +89,11 @@ application/modify_resources=false
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version="0.6.0"
application/product_version="0.6.0"
application/file_version="0.6.1"
application/product_version="0.6.1"
application/company_name=""
application/product_name="Emberwild"
application/file_description="Emberwild"
application/product_name="Cinderwild"
application/file_description="Cinderwild"
application/copyright=""
application/trademarks=""
application/export_angle=0

View File

@@ -1,5 +1,5 @@
#!/bin/sh
# Starts Emberwild (the game, with its window and title screen).
# Starts Cinderwild (the game, with its window and title screen).
#
# ./play.sh
#

View File

@@ -14,12 +14,12 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true
[application]
config/name="Emberwild"
config/name="Cinderwild"
config/description="Low-poly survival game — Valheim look, Rust depth, self-hosted, moddable."
run/main_scene="res://scenes/title.tscn"
config/features=PackedStringArray("4.7", "Forward Plus")
config/icon="res://icon.svg"
config/version="0.6.0"
config/version="0.6.1"
[autoload]
@@ -93,7 +93,7 @@ weather_wind={
"value": 1.0
}
[emberwild]
[cinderwild]
updates/feed="https://git.psbailey.uk/api/v1/repos/Admin/Emberwild/releases/latest"

View File

@@ -2,16 +2,17 @@ extends Node
## Autoload "Boot", first in the list: one-off start-up housekeeping that has
## to happen before anything reads user://.
##
## The game was called "Valheimlike" until it became "Emberwild". Godot names
## the user-data folder after the project, so the rename moved it from
## .../app_userdata/Valheimlike to .../app_userdata/Emberwild. The first time
## Emberwild starts, this copies saved worlds, your identity (key, name, look)
## and server settings across. It copies rather than moves, so the old folder
## stays as a backup, and it never overwrites anything already in the new
## folder. Test folders (test_*) and logs are skipped.
## The game has been renamed twice: "Valheimlike", then "Emberwild" (0.1.0 to
## 0.6.0), now "Cinderwild". Godot names the user-data folder after the
## project, so each rename moved it (.../app_userdata/<name>). The first time
## Cinderwild starts, this copies saved worlds, your identity (key, name,
## look), settings and server settings across from the newest old folder there
## is (it already holds what came from the one before). It copies rather than moves, so the old folders stay
## as a backup, and it never overwrites anything already in the new folder.
## Test folders (test_*) and logs are skipped.
const OLD_NAME := "Valheimlike"
const MARKER := "migrated_from_valheimlike.txt"
const OLD_NAMES := ["Emberwild", "Valheimlike"] ## newest first
const MARKER := "migrated_from_%s.txt"
const SKIP := ["logs", "shader_cache", "vulkan", "test_worlds", "test_net"]
var copied := 0
@@ -22,14 +23,25 @@ func _enter_tree() -> void:
## Returns the number of files copied (0 if there was nothing to do).
func migrate_user_data() -> int:
var new_dir := OS.get_user_data_dir()
var old_dir := new_dir.get_base_dir().path_join(OLD_NAME)
for old_name in OLD_NAMES:
var old_dir := new_dir.get_base_dir().path_join(old_name)
if DirAccess.dir_exists_absolute(old_dir) and old_dir != new_dir:
# Only the newest: it already holds whatever came across from the
# one before (a world deleted since shouldn't come back).
return migrate_from(old_dir, new_dir, old_name)
return 0
## Copies `old_dir` into `new_dir` once (a marker file in `new_dir` says it's
## been done). Returns the number of files copied.
func migrate_from(old_dir: String, new_dir: String, old_name: String) -> int:
if new_dir == old_dir or not DirAccess.dir_exists_absolute(old_dir):
return 0
if FileAccess.file_exists(new_dir.path_join(MARKER)):
var marker := new_dir.path_join(MARKER % old_name.to_lower())
if FileAccess.file_exists(marker):
return 0
copied = 0
_copy_tree(old_dir, new_dir)
var f := FileAccess.open(new_dir.path_join(MARKER), FileAccess.WRITE)
var f := FileAccess.open(marker, FileAccess.WRITE)
if f != null:
f.store_string("Copied %d files from %s on %s. The old folder was left as it was.\n" % [
copied, old_dir, Time.get_datetime_string_from_system()])
@@ -43,6 +55,8 @@ func _copy_tree(from: String, to: String) -> void:
if da == null:
return
for f in da.get_files():
if f.begins_with("migrated_from_"):
continue # (the old folder's own markers)
var dst := to.path_join(f)
if not FileAccess.file_exists(dst) and DirAccess.copy_absolute(from.path_join(f), dst) == OK:
copied += 1

View File

@@ -1,6 +1,6 @@
extends RefCounted
## Report a bug (0.6.0, the playtest; pause menu): opens a new issue on the
## Emberwild repo in your browser, already filled in with a title to edit,
## Cinderwild repo in your browser, already filled in with a title to edit,
## a few questions to answer, and the details we'd ask for anyway (version,
## computer, single-player or online, which land, the day and time, where you
## are). You read it, add what happened, and press Create Issue: nothing is
@@ -46,7 +46,7 @@ static func mode(world: Node) -> String:
## The details, one per line (what's in the issue, and what "Copy" copies).
static func details(world: Node) -> Dictionary:
var d := {
"Version": "Emberwild %s (protocol %d)" % [String(ProjectSettings.get_setting("application/config/version", "?")), int(_al("Net").PROTOCOL)],
"Version": "Cinderwild %s (protocol %d)" % [String(ProjectSettings.get_setting("application/config/version", "?")), int(_al("Net").PROTOCOL)],
"Computer": "%s %s" % [OS.get_name(), OS.get_version()],
"Graphics": "%s (%s)" % [RenderingServer.get_video_adapter_name(), RenderingServer.get_current_rendering_method()],
"Mode": mode(world),

View File

@@ -1,13 +1,13 @@
extends Node
## The update check (docs/ROADMAP.md, M4 "Updates", first step): is there a
## newer Emberwild than this one? Asked once at start-up, of the feed in
## Project Settings (`emberwild/updates/feed`). The feed is either
## newer Cinderwild than this one? Asked once at start-up, of the feed in
## Project Settings (`cinderwild/updates/feed`). The feed is either
## - a release API answer (Gitea, Forgejo or GitHub `releases/latest`): its
## `version.json` asset is fetched next, and its other assets are the
## downloads, by file name; or
## - a `version.json` itself:
## {"version": "0.2.0", "protocol": 13, "notes": "...", "page": "https://...",
## "downloads": {"macos": {"file": "Emberwild-0.2.0-macos.zip", "sha256": "...",
## "downloads": {"macos": {"file": "Cinderwild-0.2.0-macos.zip", "sha256": "...",
## "size": 61164958, "url": "(optional; else the asset)"},
## "windows": {...}}}
## tools/export.sh writes that file next to the zips, for the release.
@@ -22,7 +22,7 @@ signal checked(state: String)
const TIMEOUT := 10.0
var version: String = String(ProjectSettings.get_setting("application/config/version", "0.0.0"))
var feed: String = String(ProjectSettings.get_setting("emberwild/updates/feed", ""))
var feed: String = String(ProjectSettings.get_setting("cinderwild/updates/feed", ""))
var prefs_path := "user://updates.json"
var enabled := true
## idle | off | checking | current | available | failed
@@ -101,7 +101,7 @@ func _finish(s: String) -> void:
if s == "failed":
print("[updates] couldn't check for updates: ", error)
elif s == "available":
print("[updates] Emberwild %s is out (this is %s)" % [latest_version(), version])
print("[updates] Cinderwild %s is out (this is %s)" % [latest_version(), version])
checked.emit(s)
func _get_json(url: String) -> Variant:
@@ -112,7 +112,7 @@ func _get_json(url: String) -> Variant:
var http := HTTPRequest.new()
http.timeout = TIMEOUT
add_child(http)
var err := http.request(url, ["Accept: application/json", "User-Agent: Emberwild/" + version])
var err := http.request(url, ["Accept: application/json", "User-Agent: Cinderwild/" + version])
if err != OK:
http.queue_free()
error = "couldn't ask %s (%d)" % [url, err]

View File

@@ -176,7 +176,7 @@ func _start_dedicated() -> void:
func _on_update_checked(s: String) -> void:
if s == "available":
print("[server] Emberwild %s is out (protocol %d); this server runs %s (protocol %d). %s" % [
print("[server] Cinderwild %s is out (protocol %d); this server runs %s (protocol %d). %s" % [
Updates.latest_version(), Updates.latest_protocol(), Updates.version, PROTOCOL,
"Players with the new version can't join until it's updated." if Updates.latest_protocol() != PROTOCOL else ""])
@@ -694,7 +694,7 @@ func _c2s_hello(d: Dictionary) -> void:
return
if int(d.get("protocol", 0)) != PROTOCOL:
var theirs := int(d.get("protocol", 0))
_reject(peer, "Different game version: this server runs Emberwild %s (protocol %d), you have protocol %d. %s" % [
_reject(peer, "Different game version: this server runs Cinderwild %s (protocol %d), you have protocol %d. %s" % [
Updates.version, PROTOCOL, theirs, "Update the game to join." if theirs < PROTOCOL else "The server needs updating."])
return
# (A crossing to another land comes with a one-time pass instead, checked

View File

@@ -6,7 +6,7 @@ extends RefCounted
const DEFAULT_PATH := "user://server/server.json"
const DEFAULTS := {
"server_name": "Emberwild server",
"server_name": "Cinderwild server",
"motd": "Welcome!",
"port": 24242,
"max_players": 8,

View File

@@ -127,7 +127,7 @@ func bind(p: CharacterBody3D) -> void:
_main.add_child(tips_row)
var upd := CheckButton.new()
upd.text = "Check for updates at start (Emberwild %s)" % Updates.version
upd.text = "Check for updates at start (Cinderwild %s)" % Updates.version
upd.focus_mode = Control.FOCUS_NONE
upd.button_pressed = Updates.enabled
upd.add_theme_font_size_override("font_size", 16)
@@ -187,7 +187,7 @@ func bind(p: CharacterBody3D) -> void:
var rt := UITheme.label("Report a bug", 26)
rt.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_report.add_child(rt)
var how := UITheme.label("This opens a new issue on Emberwild's Gitea page in your browser, filled in with your version, computer and where you are in the game. Say what happened, then press Create Issue. (You'll need to be signed in there.) Nothing is sent from the game itself.", 15)
var how := UITheme.label("This opens a new issue on Cinderwild's Gitea page in your browser, filled in with your version, computer and where you are in the game. Say what happened, then press Create Issue. (You'll need to be signed in there.) Nothing is sent from the game itself.", 15)
how.autowrap_mode = TextServer.AUTOWRAP_WORD
how.custom_minimum_size = Vector2(380, 0)
_report.add_child(how)

View File

@@ -80,7 +80,7 @@ func _ready() -> void:
col.offset_right = 520
col.add_theme_constant_override("separation", 14)
root.add_child(col)
col.add_child(UITheme.label("EMBERWILD", 54))
col.add_child(UITheme.label("CINDERWILD", 54))
col.add_child(UITheme.label("Survive the island. Build a home.", 18, UITheme.TEXT_DIM))
var spacer := Control.new()
spacer.custom_minimum_size = Vector2(0, 24)
@@ -413,7 +413,7 @@ func _host_page() -> Control:
func _fill_host() -> void:
var c: RefCounted = load("res://scripts/net/server_config.gd").open(Net.host_config)
var n: String = c.data.server_name
_host_name.text = "%s's island" % Net.get_identity().name if n == "Emberwild server" else n
_host_name.text = "%s's island" % Net.get_identity().name if n in ["Cinderwild server", "Emberwild server"] else n
_host_port.text = str(int(c.data.port))
_host_pw.text = String(c.data.password)
var preset := "co-op"
@@ -801,7 +801,7 @@ var version_label: Label
## The version, bottom left; and a card bottom right when a newer one is out.
func _build_update_banner(root: Control) -> void:
# (bottom right: the menu column runs down the left and would cover it)
version_label = UITheme.label("Emberwild %s" % Updates.version, 13, UITheme.TEXT_DIM)
version_label = UITheme.label("Cinderwild %s" % Updates.version, 13, UITheme.TEXT_DIM)
version_label.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT)
version_label.grow_horizontal = Control.GROW_DIRECTION_BEGIN
version_label.grow_vertical = Control.GROW_DIRECTION_BEGIN
@@ -842,7 +842,7 @@ func _on_update_checked(s: String) -> void:
return
var notes := String(Updates.latest.get("notes", ""))
var how := "" if Updates.packaged() else "\nRunning from the source: git pull in the game folder, then restart."
_update_text.text = "Emberwild %s is out (you have %s).%s%s" % [Updates.latest_version(), Updates.version,
_update_text.text = "Cinderwild %s is out (you have %s).%s%s" % [Updates.latest_version(), Updates.version,
("\n" + notes) if not notes.is_empty() else "", how]
_update_btn.text = "Download" if Updates.packaged() and not String(Updates.download().get("url", "")).is_empty() else "Release page"
_update_btn.disabled = Updates.download_url().is_empty()
@@ -858,7 +858,7 @@ static func version_advice(reason: String) -> String:
if not reason.begins_with("Different game version"):
return ""
if Updates.state == "available":
return "\nEmberwild %s is out: Download is on the title page." % Updates.latest_version()
return "\nCinderwild %s is out: Download is on the title page." % Updates.latest_version()
if reason.ends_with("Update the game to join."):
return "\nThere's no newer release yet: the server may be running a newer copy from the source."
return ""

View File

@@ -0,0 +1,36 @@
# Cinderwild dedicated server as a systemd service (Linux).
#
# 1. Put the game where it'll live, e.g. /opt/cinderwild (a git clone), and
# install Godot 4.7+ as /usr/local/bin/godot.
# 2. Make a user for it: sudo useradd -r -m -d /var/lib/cinderwild cinderwild
# 3. Copy this file: sudo cp server/cinderwild-server.service /etc/systemd/system/
# and edit User, WorkingDirectory and GODOT below if yours differ.
# 4. Start it, and on boot: sudo systemctl enable --now cinderwild-server
# Logs: journalctl -u cinderwild-server -f
# Stop / restart: sudo systemctl stop|restart cinderwild-server
#
# server.json and the world live in the user's Godot data folder:
# /var/lib/cinderwild/.local/share/godot/app_userdata/Cinderwild/server/
# Admin commands: join the game and use chat (/help), or edit server.json and
# restart. (The console needs a terminal, so it isn't used here.)
[Unit]
Description=Cinderwild dedicated server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=cinderwild
WorkingDirectory=/opt/cinderwild
Environment=GODOT=/usr/local/bin/godot
# run_server.sh checks the Godot version and imports a fresh copy first.
ExecStart=/opt/cinderwild/server/run_server.sh
Restart=on-failure
RestartSec=10
# The world saves within 10 s of any change, so a stop loses at most that.
KillSignal=SIGINT
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Emberwild dedicated server as a launchd agent (macOS): starts when you log
Cinderwild dedicated server as a launchd agent (macOS): starts when you log
in, and restarts if it stops.
1. Edit the two paths below (the game folder, and your user name in the log
path) if yours differ.
2. Copy it: cp server/com.emberwild.server.plist ~/Library/LaunchAgents/
3. Start it: launchctl load -w ~/Library/LaunchAgents/com.emberwild.server.plist
Stop it: launchctl unload ~/Library/LaunchAgents/com.emberwild.server.plist
Log: tail -f ~/Library/Logs/emberwild-server.log
2. Copy it: cp server/com.cinderwild.server.plist ~/Library/LaunchAgents/
3. Start it: launchctl load -w ~/Library/LaunchAgents/com.cinderwild.server.plist
Stop it: launchctl unload ~/Library/LaunchAgents/com.cinderwild.server.plist
Log: tail -f ~/Library/Logs/cinderwild-server.log
server.json and the world are in
~/Library/Application Support/Godot/app_userdata/Emberwild/server/
~/Library/Application Support/Godot/app_userdata/Cinderwild/server/
For admin commands, join the game and use chat (/help), or edit server.json
and stop/start it. (For a console, run server/run_server.sh in Terminal
instead.)
@@ -20,7 +20,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.emberwild.server</string>
<string>com.cinderwild.server</string>
<key>ProgramArguments</key>
<array>
<string>/Users/home/valheimlike/server/run_server.sh</string>
@@ -43,8 +43,8 @@
<key>ThrottleInterval</key>
<integer>15</integer>
<key>StandardOutPath</key>
<string>/Users/home/Library/Logs/emberwild-server.log</string>
<string>/Users/home/Library/Logs/cinderwild-server.log</string>
<key>StandardErrorPath</key>
<string>/Users/home/Library/Logs/emberwild-server.log</string>
<string>/Users/home/Library/Logs/cinderwild-server.log</string>
</dict>
</plist>

View File

@@ -1,36 +0,0 @@
# Emberwild dedicated server as a systemd service (Linux).
#
# 1. Put the game where it'll live, e.g. /opt/emberwild (a git clone), and
# install Godot 4.7+ as /usr/local/bin/godot.
# 2. Make a user for it: sudo useradd -r -m -d /var/lib/emberwild emberwild
# 3. Copy this file: sudo cp server/emberwild-server.service /etc/systemd/system/
# and edit User, WorkingDirectory and GODOT below if yours differ.
# 4. Start it, and on boot: sudo systemctl enable --now emberwild-server
# Logs: journalctl -u emberwild-server -f
# Stop / restart: sudo systemctl stop|restart emberwild-server
#
# server.json and the world live in the user's Godot data folder:
# /var/lib/emberwild/.local/share/godot/app_userdata/Emberwild/server/
# Admin commands: join the game and use chat (/help), or edit server.json and
# restart. (The console needs a terminal, so it isn't used here.)
[Unit]
Description=Emberwild dedicated server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=emberwild
WorkingDirectory=/opt/emberwild
Environment=GODOT=/usr/local/bin/godot
# run_server.sh checks the Godot version and imports a fresh copy first.
ExecStart=/opt/emberwild/server/run_server.sh
Restart=on-failure
RestartSec=10
# The world saves within 10 s of any change, so a stop loses at most that.
KillSignal=SIGINT
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target

View File

@@ -1,5 +1,5 @@
#!/bin/sh
# Starts a dedicated Emberwild server (no window, no local player).
# Starts a dedicated Cinderwild 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

View File

@@ -9,7 +9,7 @@ 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_alpha test_bear test_thieves test_barrow test_boss test_den_sites test_fishing_diet test_food test_hazards test_iron test_stone test_net_lobby test_net_world test_net_shared test_net_life test_net_boss test_net_rules test_net_lands test_host test_tutorial test_tutorial_full test_hints test_updates test_audio test_items_more test_worldlets test_worldlet_pick test_water_bodies test_cove test_wreck test_shore test_boat test_bug_report test_tunnel test_map test_terrain_edges test_boundary; do
for t in test_boot test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_alpha test_bear test_thieves test_barrow test_boss test_den_sites test_fishing_diet test_food test_hazards test_iron test_stone test_net_lobby test_net_world test_net_shared test_net_life test_net_boss test_net_rules test_net_lands test_host test_tutorial test_tutorial_full test_hints test_updates test_audio test_items_more test_worldlets test_worldlet_pick test_water_bodies test_cove test_wreck test_shore test_boat test_bug_report test_tunnel test_map test_terrain_edges test_boundary; do
extra=""
[ "$t" = "test_boundary" ] && extra="--fixed-fps 30"
# A suite that waits on a world that failed to load would hang forever,

71
tests/test_boot.gd Normal file
View File

@@ -0,0 +1,71 @@
extends SceneTree
## The rename (Emberwild -> Cinderwild, 0.6.1): the first start copies your
## saves, identity and settings across from the old user-data folder, once,
## without overwriting anything already there, and only from the newest old
## name (so a world you deleted under Emberwild doesn't come back from the
## Valheimlike folder).
## Run: godot --headless --path . --script res://tests/test_boot.gd
var _fails := 0
func check(cond: bool, what: String) -> void:
print((" ok " if cond else " FAIL ") + what)
if not cond:
_fails += 1
func _write(path: String, text: String) -> void:
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
var f := FileAccess.open(path, FileAccess.WRITE)
f.store_string(text)
f.close()
func _read(path: String) -> String:
return FileAccess.get_file_as_string(path) if FileAccess.file_exists(path) else ""
func _rm(dir: String) -> void:
if not DirAccess.dir_exists_absolute(dir):
return
var da := DirAccess.open(dir)
for f in da.get_files():
DirAccess.remove_absolute(dir.path_join(f))
for d in da.get_directories():
_rm(dir.path_join(d))
DirAccess.remove_absolute(dir)
func _initialize() -> void:
var B: Node = root.get_node("Boot")
var base := OS.get_user_data_dir().path_join("test_boot")
_rm(base)
var ember := base.path_join("Emberwild")
var valheim := base.path_join("Valheimlike")
var cinder := base.path_join("Cinderwild")
_write(ember.path_join("worlds/home/state.json"), "ember home")
_write(ember.path_join("identity.json"), "me")
_write(ember.path_join("logs/godot.log"), "old log")
_write(ember.path_join("migrated_from_valheimlike.txt"), "old marker")
_write(valheim.path_join("worlds/deleted/state.json"), "gone")
_write(cinder.path_join("audio.json"), "new settings")
_write(ember.path_join("audio.json"), "old settings")
print("the first start")
var n: int = 0
for old_name in B.OLD_NAMES:
var old_dir := base.path_join(old_name)
if DirAccess.dir_exists_absolute(old_dir):
n = B.migrate_from(old_dir, cinder, old_name)
break
check(n == 2, "copies the old folder's files (%d)" % n)
check(_read(cinder.path_join("worlds/home/state.json")) == "ember home" and _read(cinder.path_join("identity.json")) == "me",
"your worlds and identity come across")
check(_read(cinder.path_join("audio.json")) == "new settings", "nothing already there is overwritten")
check(not FileAccess.file_exists(cinder.path_join("logs/godot.log")), "logs stay behind")
check(not FileAccess.file_exists(cinder.path_join("worlds/deleted/state.json")), "the older Valheimlike folder isn't raked over")
check(FileAccess.file_exists(ember.path_join("identity.json")), "the old folder's left as it was")
check(FileAccess.file_exists(cinder.path_join("migrated_from_emberwild.txt")) and not FileAccess.file_exists(cinder.path_join("migrated_from_valheimlike.txt")),
"a marker says it's done")
print("the next start")
_write(ember.path_join("worlds/later/state.json"), "later")
check(B.migrate_from(ember, cinder, "Emberwild") == 0 and not FileAccess.file_exists(cinder.path_join("worlds/later/state.json")), "it only happens once")
_rm(base)
print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails])
quit(1 if _fails > 0 else 0)

1
tests/test_boot.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://bnsn0icdtxs16

View File

@@ -1,6 +1,6 @@
extends SceneTree
## 0.6.0, the playtest: Report a bug. The pause menu has a "Report a bug"
## page; its link is a new issue on the Emberwild repo, filled in with
## page; its link is a new issue on the Cinderwild repo, filled in with
## questions to answer and the details (version, computer, mode, land, time,
## where you are), short enough for a browser; the log's path is shown.
## Run: godot --headless --path . --script res://tests/test_bug_report.gd
@@ -43,11 +43,11 @@ func _body() -> void:
print("the link")
var url: String = BugReport.issue_url(w)
check(url.begins_with("https://git.psbailey.uk/Admin/Emberwild/issues/new?title=Bug%3A%20&body="), "a new issue on the Emberwild repo")
check(url.begins_with("https://git.psbailey.uk/Admin/Emberwild/issues/new?title=Bug%3A%20&body="), "a new issue on the Cinderwild repo")
check(url.length() < 4000, "short enough for any browser (%d characters)" % url.length())
var body := url.get_slice("&body=", 1).uri_decode()
var ver: String = String(ProjectSettings.get_setting("application/config/version"))
for want in ["**What happened?**", "**How can we make it happen again?**", "- Version: Emberwild %s" % ver,
for want in ["**What happened?**", "**How can we make it happen again?**", "- Version: Cinderwild %s" % ver,
"- Computer: ", "- Mode: single-player", "- Land: Cove", "- Time: day ", "- Where: ", "- Seed: 1337", "godot.log"]:
check(body.contains(want), "it says %s" % want)
check(BugReport.log_path().ends_with("logs/godot.log") and BugReport.log_path().begins_with("/"), "the log's full path (%s)" % BugReport.log_path())

View File

@@ -53,11 +53,11 @@ func _run() -> void:
Net.arrived.connect(func(l): _arrived = l)
for d in [SRV, BOBS]:
_rm_tree(d)
# (EMBERWILD_SERVER_BIN=<an exported game>: the hub, and the land servers
# (CINDERWILD_SERVER_BIN=<an exported game>: the hub, and the land servers
# it starts, are that build: checks a release does it too.)
_spawn(["--server", "--port", str(PORT), "--config", ProjectSettings.globalize_path(SRV + "/server.json"),
"--server-name", "Lands Test", "--seed", str(SEED), "--debug-cheats", "--no-creatures"], "",
OS.get_environment("EMBERWILD_SERVER_BIN"))
OS.get_environment("CINDERWILD_SERVER_BIN"))
await _sleep(4.0)
await _body()
if world != null and is_instance_valid(world):

View File

@@ -58,7 +58,7 @@ func _body() -> void:
var plat: String = C.platform_key()
print("a version.json feed")
pages[U.feed] = JSON.stringify({"version": "9.1.0", "protocol": 99, "notes": "The Cove.", "page": "https://example.test/r/9.1.0",
"downloads": {plat: {"file": "Emberwild-9.1.0-%s.zip" % plat, "url": "https://example.test/dl/%s.zip" % plat, "sha256": "ab"}}})
"downloads": {plat: {"file": "Cinderwild-9.1.0-%s.zip" % plat, "url": "https://example.test/dl/%s.zip" % plat, "sha256": "ab"}}})
check(await _check_now() == "available", "9.1.0 is out: available")
check(U.latest_version() == "9.1.0" and U.latest_protocol() == 99, "its version and protocol")
check(U.download_url() == "https://example.test/dl/%s.zip" % plat and U.download().sha256 == "ab", "this platform's download (%s), with its checksum" % plat)
@@ -67,9 +67,9 @@ func _body() -> void:
U.feed = "https://example.test/api/releases/latest"
pages[U.feed] = JSON.stringify({"tag_name": "v9.2.0", "html_url": "https://example.test/releases/v9.2.0", "assets": [
{"name": "version.json", "browser_download_url": "https://example.test/a/version.json"},
{"name": "Emberwild-9.2.0-%s.zip" % plat, "browser_download_url": "https://example.test/a/game-%s.zip" % plat}]})
{"name": "Cinderwild-9.2.0-%s.zip" % plat, "browser_download_url": "https://example.test/a/game-%s.zip" % plat}]})
pages["https://example.test/a/version.json"] = JSON.stringify({"version": "9.2.0", "protocol": 99,
"downloads": {plat: {"file": "Emberwild-9.2.0-%s.zip" % plat, "sha256": "cd"}}, "windows_only_key": true})
"downloads": {plat: {"file": "Cinderwild-9.2.0-%s.zip" % plat, "sha256": "cd"}}, "windows_only_key": true})
check(await _check_now() == "available" and U.latest_version() == "9.2.0", "the release's version.json is read: 9.2.0")
check(U.download_url() == "https://example.test/a/game-%s.zip" % plat, "the download is the release asset with that file name")
check(U.page == "https://example.test/releases/v9.2.0", "and the release page is kept")
@@ -97,7 +97,7 @@ func _body() -> void:
print("the title screen")
var TS: Script = load("res://scripts/ui/title_screen.gd")
var msg := "Different game version: this server runs Emberwild 9.3.0 (protocol 99), you have protocol 12. Update the game to join."
var msg := "Different game version: this server runs Cinderwild 9.3.0 (protocol 99), you have protocol 12. Update the game to join."
check(TS.version_advice(msg).contains("9.3.0 is out"), "refused for an old version: it says the new one is out")
check(TS.version_advice("Server is full").is_empty(), "(other refusals: nothing added)")
pages[U.feed] = JSON.stringify({"version": "9.4.0", "notes": "Sea fishing."})

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Emberwild's own sounds: the music, the ambience beds and the sound effects
"""Cinderwild's own sounds: the music, the ambience beds and the sound effects
that the Kenney CC0 packs don't cover, all synthesised here (no samples, no
third-party audio), so they're ours to release under CC0 too.

View File

@@ -4,9 +4,9 @@
# tools/export.sh # macOS and Windows
# tools/export.sh macOS # just one ("macOS" or "Windows")
#
# build/Emberwild-<version>-macos.zip the .app (universal: Apple silicon + Intel),
# build/Cinderwild-<version>-macos.zip the .app (universal: Apple silicon + Intel),
# ad-hoc signed, not notarised
# build/Emberwild-<version>-windows.zip Emberwild.exe + Emberwild.pck (+ a console
# build/Cinderwild-<version>-windows.zip Cinderwild.exe + Cinderwild.pck (+ a console
# wrapper), unsigned
#
# Needs the Godot export templates for this Godot's version (Editor > Manage
@@ -29,21 +29,21 @@ mkdir -p build
if [ "$which" = all ] || [ "$which" = macOS ]; then
rm -rf build/macos && mkdir -p build/macos
"$GODOT" --headless --path . --export-release "macOS" build/macos/Emberwild.zip > build/export-macos.log 2>&1
if [ -f build/macos/Emberwild.zip ]; then
mv build/macos/Emberwild.zip "build/Emberwild-$VERSION-macos.zip"
echo "built build/Emberwild-$VERSION-macos.zip"
"$GODOT" --headless --path . --export-release "macOS" build/macos/Cinderwild.zip > build/export-macos.log 2>&1
if [ -f build/macos/Cinderwild.zip ]; then
mv build/macos/Cinderwild.zip "build/Cinderwild-$VERSION-macos.zip"
echo "built build/Cinderwild-$VERSION-macos.zip"
else
echo "macOS export failed: see build/export-macos.log"; exit 1
fi
fi
if [ "$which" = all ] || [ "$which" = Windows ]; then
rm -rf build/windows && mkdir -p build/windows/Emberwild
"$GODOT" --headless --path . --export-release "Windows" build/windows/Emberwild/Emberwild.exe > build/export-windows.log 2>&1
if [ -f build/windows/Emberwild/Emberwild.exe ]; then
(cd build/windows && rm -f "../Emberwild-$VERSION-windows.zip" && zip -qr "../Emberwild-$VERSION-windows.zip" Emberwild)
echo "built build/Emberwild-$VERSION-windows.zip"
rm -rf build/windows && mkdir -p build/windows/Cinderwild
"$GODOT" --headless --path . --export-release "Windows" build/windows/Cinderwild/Cinderwild.exe > build/export-windows.log 2>&1
if [ -f build/windows/Cinderwild/Cinderwild.exe ]; then
(cd build/windows && rm -f "../Cinderwild-$VERSION-windows.zip" && zip -qr "../Cinderwild-$VERSION-windows.zip" Cinderwild)
echo "built build/Cinderwild-$VERSION-windows.zip"
else
echo "Windows export failed: see build/export-windows.log"; exit 1
fi
@@ -58,7 +58,7 @@ out = {"version": version, "protocol": protocol, "date": datetime.date.today().i
if notes:
out["notes"] = notes
for plat in ("macos", "windows"):
f = "build/Emberwild-%s-%s.zip" % (version, plat)
f = "build/Cinderwild-%s-%s.zip" % (version, plat)
if os.path.exists(f):
h = hashlib.sha256(open(f, "rb").read()).hexdigest()
out["downloads"][plat] = {"file": os.path.basename(f), "sha256": h, "size": os.path.getsize(f)}

View File

@@ -20,12 +20,12 @@ AUTH="Authorization: token $GITEA_TOKEN"
git ls-remote --exit-code origin "refs/tags/$TAG" >/dev/null || { echo "push the tag first: git push origin $TAG"; exit 1; }
BODY=$(python3 -c "import json,sys; print(json.dumps({'tag_name': sys.argv[1], 'name': 'Emberwild ' + sys.argv[2], 'body': sys.argv[3], 'draft': False, 'prerelease': False}))" "$TAG" "$VERSION" "$NOTES")
BODY=$(python3 -c "import json,sys; print(json.dumps({'tag_name': sys.argv[1], 'name': 'Cinderwild ' + sys.argv[2], 'body': sys.argv[3], 'draft': False, 'prerelease': False}))" "$TAG" "$VERSION" "$NOTES")
ID=$(curl -s -H "$AUTH" -H "Content-Type: application/json" -d "$BODY" "$API/releases" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id') or ''); sys.stderr.write(d.get('message','') + '\n') if not d.get('id') else None")
[ -n "$ID" ] || { echo "couldn't create the release (above)"; exit 1; }
echo "release $TAG created (id $ID)"
for f in version.json "Emberwild-$VERSION-macos.zip" "Emberwild-$VERSION-windows.zip"; do
for f in version.json "Cinderwild-$VERSION-macos.zip" "Cinderwild-$VERSION-windows.zip"; do
[ -f "build/$f" ] || { echo "missing build/$f"; exit 1; }
R=$(curl -s -H "$AUTH" -F "attachment=@build/$f" "$API/releases/$ID/assets?name=$f")
echo "$R" | grep -q '"browser_download_url"' && echo " uploaded $f" || { echo " upload of $f failed: $R"; exit 1; }
@@ -34,7 +34,7 @@ done
# Check what anyone downloads is what was built.
REPO=${API%/api/v1/repos/*}/$(echo "$API" | sed 's#.*/repos/##')
for p in macos windows; do
f="Emberwild-$VERSION-$p.zip"
f="Cinderwild-$VERSION-$p.zip"
want=$(python3 -c "import json,sys; print(json.load(open('build/version.json'))['downloads'][sys.argv[1]]['sha256'])" $p)
got=$(curl -s -L "$REPO/releases/download/$TAG/$f" | shasum -a 256 | cut -d' ' -f1)
[ "$want" = "$got" ] && echo " $f: checksum ok" || { echo " $f: CHECKSUM MISMATCH"; exit 1; }

View File

@@ -1,11 +1,11 @@
## Getting started {#getting-started}
Emberwild drops you on a wooded island ringed by mountains, with a lake in the middle and sea cliffs on one side. You start with nothing: everything comes from the island.
Cinderwild drops you on a wooded island ringed by mountains, with a lake in the middle and sea cliffs on one side. You start with nothing: everything comes from the island.
### Installing
- **Mac:** unzip and move Emberwild to Applications. It isn't notarised by Apple, so the first time, right-click it and choose **Open** (or System Settings > Privacy & Security > Open Anyway).
- **Windows:** unzip and run `Emberwild.exe`. Keep `Emberwild.pck` in the same folder or the game won't start. It's unsigned, so if SmartScreen says "Windows protected your PC", choose **More info**, then **Run anyway**.
- **Mac:** unzip and move Cinderwild to Applications. It isn't notarised by Apple, so the first time, right-click it and choose **Open** (or System Settings > Privacy & Security > Open Anyway).
- **Windows:** unzip and run `Cinderwild.exe`. Keep `Cinderwild.pck` in the same folder or the game won't start. It's unsigned, so if SmartScreen says "Windows protected your PC", choose **More info**, then **Run anyway**.
### The title screen
@@ -430,11 +430,11 @@ The music follows what's happening: calm by day, a night theme after dark, a sto
### Reporting a bug
**Report a bug** in the pause menu opens a new issue on Emberwild's [Gitea page](https://git.psbailey.uk/Admin/Emberwild/issues) in your browser, already filled in with your version, computer, mode, land, the in-game time and where you are. Say what happened and how to make it happen again, then press **Create Issue** (you'll need to be signed in there). Nothing is sent from the game itself. **Copy the details** puts the same details on your clipboard instead, and **Show the log file** opens the folder with `godot.log`: attach it for a crash or an error.
**Report a bug** in the pause menu opens a new issue on Cinderwild's [Gitea page](https://git.psbailey.uk/Admin/Emberwild/issues) in your browser, already filled in with your version, computer, mode, land, the in-game time and where you are. Say what happened and how to make it happen again, then press **Create Issue** (you'll need to be signed in there). Nothing is sent from the game itself. **Copy the details** puts the same details on your clipboard instead, and **Show the log file** opens the folder with `godot.log`: attach it for a crash or an error.
### Updates
With the update check on, the title screen tells you when a newer Emberwild is out and offers a **Download** button or the release page.
With the update check on, the title screen tells you when a newer Cinderwild is out and offers a **Download** button or the release page.
## Tips for your first days {#tips}

View File

@@ -3,12 +3,12 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Emberwild: a self-hosted co-op survival game</title>
<meta name="description" content="Emberwild is a low-poly survival game: gather, hunt, build a home and hold out through the night, then dig through the sea cliffs to the Cove. Alone, or with friends on your own server.">
<title>Cinderwild: a self-hosted co-op survival game</title>
<meta name="description" content="Cinderwild is a low-poly survival game: gather, hunt, build a home and hold out through the night, then dig through the sea cliffs to the Cove. Alone, or with friends on your own server.">
<meta name="theme-color" content="#15110d">
<meta property="og:type" content="website">
<meta property="og:url" content="{{SITE_URL}}/">
<meta property="og:title" content="Emberwild">
<meta property="og:title" content="Cinderwild">
<meta property="og:description" content="Survive the island. Build a home. Self-hosted co-op survival for Mac and Windows.">
<meta property="og:image" content="{{SITE_URL}}/poster.jpg">
<meta name="twitter:card" content="summary_large_image">
@@ -137,7 +137,7 @@
<nav class="top" aria-label="Site">
<div class="wrap">
<a class="brand" href="#">EMBERWILD</a>
<a class="brand" href="#">CINDERWILD</a>
<a class="l opt" href="#trailer">Trailer</a>
<a class="l opt" href="#world">The game</a>
<a class="l" href="guide.html">Guide</a>
@@ -148,7 +148,7 @@
<header class="hero">
<div class="wrap">
<span class="pill">Version {{VERSION}} · playtest</span>
<h1>EMBERWILD</h1>
<h1>CINDERWILD</h1>
<p class="tag">Survive the island. Build a home. Find what lies beyond.</p>
<p class="sub">A low-poly survival game for Mac and Windows. Gather, hunt and build, hold out
through wolf moons and hard winters, then dig down through the sea cliffs to the Cove.
@@ -168,7 +168,7 @@
<h2 id="trailer-title">The trailer</h2>
<p class="lead">Under a minute, recorded in the game.</p>
<div class="video">
<video controls preload="metadata" poster="poster.jpg" aria-label="Emberwild trailer">
<video controls preload="metadata" poster="poster.jpg" aria-label="Cinderwild trailer">
<source src="trailer.mp4" type="video/mp4">
Your browser can't play this video. <a href="trailer.mp4">Download the trailer</a>.
</video>
@@ -216,6 +216,7 @@
<p class="lead">The Cove gets its own. This is a playtest build: thanks for trying it.</p>
<div class="new">
<ul class="ticks">
<li>A new name: Emberwild is now Cinderwild (another game already had the old one). Your worlds come with you.</li>
<li>A wreck on the Cove's beach, with a treasure map in the captain's sea chest for each player.</li>
<li>Your own X on your map: dig up the pirate chest for a ship's compass, a spyglass, a cutlass and gold.</li>
<li>The drowned crew rise to guard it. Dig by day if you're wise.</li>
@@ -271,7 +272,7 @@
<h3>Windows <span class="yours">· your computer</span></h3>
<p class="meta">64-bit · {{WIN_SIZE}}</p>
<a class="btn" href="{{WIN_URL}}">Download for Windows</a>
<p class="note">Unzip, then run <code>Emberwild.exe</code>. If SmartScreen warns,
<p class="note">Unzip, then run <code>Cinderwild.exe</code>. If SmartScreen warns,
choose <b>More info</b> &gt; <b>Run anyway</b> (it isn't signed yet).</p>
</div>
</div>
@@ -300,7 +301,7 @@
</tbody>
</table>
<p class="note">A spare computer can run a dedicated server from the same download:
<code>Emberwild --headless -- --server</code>.
<code>Cinderwild --headless -- --server</code>.
<a href="guide.html#multiplayer">More in the guide</a>.</p>
</div>
</section>
@@ -314,7 +315,7 @@
<a href="{{REPO_URL}}/issues">Report a bug</a>
<a href="{{REPO_URL}}">Source</a>
</nav>
<span>Emberwild {{VERSION}}. Made with Godot.</span>
<span>Cinderwild {{VERSION}}. Made with Godot.</span>
</div>
</footer>

View File

@@ -221,8 +221,8 @@ PAGE = """<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Emberwild guide: everything in the game</title>
<meta name="description" content="How to play Emberwild: controls, survival, food, building, weather, wildlife, hunting, fishing, iron, the Old Stag, the journey to the Cove and multiplayer, with every item, recipe and creature.">
<title>Cinderwild guide: everything in the game</title>
<meta name="description" content="How to play Cinderwild: controls, survival, food, building, weather, wildlife, hunting, fishing, iron, the Old Stag, the journey to the Cove and multiplayer, with every item, recipe and creature.">
<style>
:root { --bg: #15110d; --panel: #221b14; --line: #3a2e22; --text: #f1e8da; --dim: #b8a892;
--ember: #e8833a; --ember-hi: #f6a35c; --mark: #5a3a14; }
@@ -262,7 +262,7 @@ PAGE = """<!doctype html>
<a class="skip" href="#content">Skip to the guide</a>
<div class="layout">
<nav class="toc" aria-label="Guide contents">
<a class="home" href="index.html">EMBERWILD</a>
<a class="home" href="index.html">CINDERWILD</a>
<label for="q" class="group" style="display:block;margin:0 0 6px">Search the guide</label>
<input id="q" type="search" placeholder="wolves, bed, iron..." autocomplete="off">
<ol>
@@ -273,12 +273,12 @@ PAGE = """<!doctype html>
</ol>
</nav>
<main id="content">
<h1>The Emberwild guide</h1>
<p class="lead">Everything in Emberwild {VERSION}: how to play, what can happen, and every item, recipe and
<h1>The Cinderwild guide</h1>
<p class="lead">Everything in Cinderwild {VERSION}: how to play, what can happen, and every item, recipe and
creature. <a href="index.html">Download the game</a>.</p>
<p id="none" class="none hidden" role="status">Nothing matches that.</p>
{SECTIONS}
<footer>Emberwild {VERSION}. The reference tables are made from the game's own data each time this page is built.</footer>
<footer>Cinderwild {VERSION}. The reference tables are made from the game's own data each time this page is built.</footer>
</main>
</div>
<script>

View File

@@ -15,16 +15,16 @@
cd "$(dirname "$0")/../.." || exit 1
REPO=${REPO:-https://git.psbailey.uk/Admin/Emberwild}
SITE=${SITE:-https://emberwild-95k.pages.dev} # (where the page is served: for link previews)
SITE=${SITE:-https://cinderwild.psbailey.uk} # (where the page is served: for link previews)
SHOTS="meadow hut wolves winter cove treasure crabs"
[ -f build/version.json ] || { echo "no build/version.json: run tools/export.sh first"; exit 1; }
[ -f build/Emberwild-trailer.mp4 ] || { echo "no trailer: run tools/trailer/make_trailer.sh first"; exit 1; }
[ -f build/Cinderwild-trailer.mp4 ] || { echo "no trailer: run tools/trailer/make_trailer.sh first"; exit 1; }
for s in $SHOTS; do
[ -f "build/shots/$s.jpg" ] || { echo "no build/shots/$s.jpg: run tools/site/make_shots.sh first"; exit 1; }
done
rm -rf build/site && mkdir -p build/site
cp build/Emberwild-trailer.mp4 build/site/trailer.mp4
cp build/Cinderwild-trailer.mp4 build/site/trailer.mp4
cp build/trailer/poster.jpg build/site/poster.jpg
mkdir -p build/site/shots
for s in $SHOTS; do cp "build/shots/$s.jpg" build/site/shots/; done

View File

@@ -2,7 +2,7 @@
# Records the test trailer (tools/trailer/trailer.gd) with Godot's Movie
# Maker and encodes it, with a generated music pad under the game's sound:
#
# tools/trailer/make_trailer.sh # -> build/Emberwild-trailer.mp4 (+ poster.jpg)
# tools/trailer/make_trailer.sh # -> build/Cinderwild-trailer.mp4 (+ poster.jpg)
#
# Needs ffmpeg. Takes about a minute: the shots are rendered frame by frame
# at 30 fps whatever the machine's speed, so the result never stutters.
@@ -13,7 +13,7 @@ command -v ffmpeg >/dev/null || { echo "needs ffmpeg (brew install ffmpeg)"; exi
mkdir -p build/trailer
RAW=build/trailer/raw.avi
OUT=build/Emberwild-trailer.mp4
OUT=build/Cinderwild-trailer.mp4
rm -f "$RAW"
"$GODOT" --path . --resolution 1280x720 --fixed-fps 30 --write-movie "$RAW" \
--script tools/trailer/trailer.gd -- --no-creatures > build/trailer/render.log 2>&1

View File

@@ -182,7 +182,7 @@ func _clock(hours: float) -> void:
# --- the shots -------------------------------------------------------------------------
func _shot_dawn() -> void:
big.text = "EMBERWILD"
big.text = "CINDERWILD"
sub.text = "Survive the island. Build a home."
var c := Vector3(0, 0, -10)
await _shot(7.5, func(t: float, k: float):
@@ -335,7 +335,7 @@ func _shot_cove() -> void:
_park_player(Vector3(pier.start.x, pier.start.y + 1.0, pier.start.z)), "Find what lies beyond.")
func _shot_end() -> void:
big.text = "EMBERWILD"
big.text = "CINDERWILD"
sub.text = "Self-hosted co-op survival · %s" % String(ProjectSettings.get_setting("application/config/version", ""))
# Rising over the Cove's bay, looking back at the land.
var pier: Node3D = world.pier