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

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