fix(zomboid): re-sync the world settings from the live server

The vendored Sophie preset and the running debbzoid world had drifted, and the
repo only held the preset. That is not a restore point: force-pushing it would
have reverted the admins' in-game tuning rather than recovering it, which is
exactly how a Sophie world quietly became an Apocalypse one once already -- 139
values reverted, loot from 0.35 back to 0.9, CharacterFreePoints 0 to 60.

files/zomboid/sophie/SandboxVars.lua is now a snapshot of the live world taken
2026-08-31, not the preset as shipped. The modlist is untouched and still
upstream, which is why zomboid_preset_version now names the two halves and their
separate dates. server.ini.j2 carries the eight keys that had drifted:

  PlayerSafehouse              false -> true
  SafehouseAllowNonResidential false -> true   (the diner/gas-station case)
  SafehouseAllowRespawn        false -> true
  SafehouseAllowLoot           true  -> false
  SafehouseAllowFire           true  -> false
  TrashDeleteAll               false -> true
  MapRemotePlayerVisibility    1     -> 4
  ResetID                      6953472 -> 826046

ResetID is in that list on purpose, and matters most. It is the world's
soft-reset token: a file value that differs from the one the live world was
created with tells every connected client to roll a new character. Carrying the
live value makes a deliberate force-push a no-op instead of a server-wide wipe
prompt.

Spawn config gets its own switch, zomboid_spawn_force. spawnregions.lua and
spawnpoints.lua are the only config a running world re-reads -- at every server
start, where SandboxVars is read once, when the world is created -- so a spawn
edit is deployable on the live world without a wipe. Sharing zomboid_config_force
between them would have meant force-pushing the whole preset to land a one-line
spawn edit, rewriting the INI (hence the ResetID hazard above) and the world's
SandboxVars along with it.

config-template/ now tracks the repo unconditionally. Nothing on the host writes
that directory and the server cannot see it, so it has no hand edits to protect;
if it does not track the repo it is not a restore point, just an older world's
settings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QdWYhwUtwM2NQGukiRh12
This commit is contained in:
Bastian de Byl
2026-09-05 09:11:31 -04:00
co-authored by Claude Opus 5
parent 9954d774e7
commit ba9c4f2bfe
7 changed files with 456 additions and 226 deletions
+19 -1
View File
@@ -80,7 +80,12 @@ zomboid_max_ram: 24g
# The modlist itself lives in vars/zomboid_sophie_mods.yml -- 283 mod IDs and
# 256 workshop items vendored from the upstream Sophie 42 preset.
zomboid_preset_version: "sophie-42 @ 2026-07-31"
#
# The modlist is still the upstream preset. The world settings no longer are:
# files/zomboid/sophie/SandboxVars.lua is a snapshot of the running debbzoid
# world taken 2026-08-31, not the preset as shipped. See the seed tasks in
# containers/home/zomboid.yml.
zomboid_preset_version: "sophie-42 mods @ 2026-07-31 / live world settings @ 2026-08-31"
# Mods subtracted from the vendored Sophie lists before they reach the INI.
# Explicit and reasoned so that re-vendoring a newer preset cannot silently
@@ -139,6 +144,19 @@ zomboid_mods_renamed:
# between world regenerations. Set true to overwrite it from the templates.
zomboid_config_force: false
# Same switch, narrowed to spawnregions.lua and spawnpoints.lua. They are the
# only config a running world re-reads (at every server start, unlike
# SandboxVars, which is read only when the world is created), so a spawn edit
# can be deployed to the live world without a wipe. It is separate from
# zomboid_config_force so that landing one is not an excuse to force the INI and
# SandboxVars over the top of whatever the admins have set in-game.
#
# make deploy TAGS=zomboid-conf -e zomboid_spawn_force=true
#
# The server still has to be restarted, with players offline, before the new
# spawn config is read.
zomboid_spawn_force: false
sshpass_cron_path: "{{ podman_volumes }}/sshpass_cron"
caddy_path: "{{ podman_volumes }}/caddy"
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,28 @@
-- Deployed as Server/debbzoid_spawnpoints.lua, and currently unused: nothing
-- reads it, because no entry in spawnregions.lua names it with
-- serverfile = "debbzoid_spawnpoints.lua". The contents below are the stock
-- sample and mean nothing until such an entry exists.
--
-- This is the file to use if a spawn point has to move rather than disappear.
-- Point a region at it by serverfile instead of file, and put that region's
-- whole spawn table here -- the serverfile replaces the map's list, it does not
-- merge with it, so anything left out is gone.
--
-- Keeping "Echo Creek, KY" as a spawn choice while freeing the diner would look
-- like this, with an outdoor square -- the parking lot or the road, not another
-- building, or that building becomes the unclaimable one instead:
--
-- unemployed = {
-- { posX = <outdoor X>, posY = <outdoor Y>, posZ = 0 },
-- }
--
-- Coordinates are absolute world tiles when worldX/worldY are omitted; with
-- them, the world position is worldX * 300 + posX (same for Y).
--
-- The filename is not free-form: serverfile resolves inside the server's
-- Server/ directory, and the deploy names this copy after zomboid_server_name.
-- Rename the server and the serverfile reference has to follow, or the region
-- loses its points and drops out of the list.
function SpawnPoints()
return {
unemployed = {
@@ -1,7 +1,35 @@
-- Spawn regions offered to new characters, deployed as
-- Server/debbzoid_spawnregions.lua. PZ reads it at every server start, so a
-- change here takes effect on the live world after a restart -- no wipe.
--
-- Each entry either points at a map's own list (file = ...) or at a file in the
-- server's Server/ directory (serverfile = ...). Both are loaded by
-- media/lua/shared/SpawnRegions.lua; a region whose file is missing logs
-- "spawn points may be broken" to the server console and is dropped from the
-- list, taking its spawn choices with it.
--
-- This list is also what makes buildings unclaimable. SafeHouse.canBeSafehouse
-- walks every point of every region here, resolves each to a grid square, and
-- refuses the claim with "Spawn location. Cannot be claimed." if the square
-- being claimed is inside that square's building. It does not care which
-- profession the point belongs to, whether the region is reachable, or whether
-- the player is an admin. A point on an outdoor square blocks nothing, because
-- an outdoor square has no building.
--
-- 2026-08-31: "Echo Creek, KY" is commented out below. Its spawn list holds
-- exactly one point -- posX 3573, posY 10899, unemployed only -- and that point
-- sits inside the Echo Creek gas station/diner, which is why the diner cannot
-- be claimed as a safehouse. There is no server option that lifts the
-- restriction, so removing the region is the only way to free the building
-- short of moving the point somewhere else (see spawnpoints.lua).
--
-- The cost of removing it: unemployed characters lose Echo Creek as a spawn
-- choice. Nothing else changes -- the town, its loot and its buildings stay in
-- the world, and no existing character is affected.
function SpawnRegions()
return {
{ name = "Brandenburg, KY", file = "media/maps/Brandenburg, KY/spawnpoints.lua" },
{ name = "Echo Creek, KY", file = "media/maps/Echo Creek, KY/spawnpoints.lua" },
-- { name = "Echo Creek, KY", file = "media/maps/Echo Creek, KY/spawnpoints.lua" },
{ name = "Ekron, KY", file = "media/maps/Ekron, KY/spawnpoints.lua" },
{ name = "Fallas Lake, KY", file = "media/maps/Fallas Lake, KY/spawnpoints.lua" },
{ name = "Irvington, KY", file = "media/maps/Irvington, KY/spawnpoints.lua" },
@@ -152,8 +152,12 @@
mode: 0644
notify: restorecon podman
# Server config is seeded from the vendored Sophie 42 preset *before* first
# boot, so the server never generates a vanilla INI we then have to patch.
# Server config is seeded *before* first boot, so the server never generates a
# vanilla INI we then have to patch.
#
# The INI and SandboxVars started as the vendored Sophie 42 preset and were
# re-synced from the running debbzoid world on 2026-08-31 -- see the header of
# templates/zomboid/server.ini.j2 for the INI keys that moved and why.
#
# force is off by design: these files are a starting point, not a managed
# state. Stop the server, hand-edit them, regenerate the world -- Ansible will
@@ -170,7 +174,7 @@
notify: restorecon podman
tags: zomboid-conf
- name: seed zomboid server ini from sophie preset
- name: seed zomboid server ini
become: true
ansible.builtin.template:
src: zomboid/server.ini.j2
@@ -183,17 +187,42 @@
tags: zomboid-conf
# Copied, not templated: these are Lua and must not go through Jinja.
- name: seed zomboid sandbox and spawn config from sophie preset
- name: seed zomboid sandbox settings
become: true
ansible.builtin.copy:
src: "zomboid/sophie/{{ item }}.lua"
dest: "{{ zomboid_path }}/data/Server/{{ zomboid_server_name }}_{{ item }}.lua"
src: zomboid/sophie/SandboxVars.lua
dest: "{{ zomboid_path }}/data/Server/{{ zomboid_server_name }}_SandboxVars.lua"
force: "{{ zomboid_config_force | bool }}"
owner: "{{ podman_subuid.stdout }}"
group: "{{ podman_user }}"
mode: 0644
notify: restorecon podman
tags: zomboid-conf
# Spawn config gets its own force switch, and it is not pedantry: these two
# files are the only part of the server config that a *running* world will pick
# up. PZ reads <name>_spawnregions.lua (and any serverfile it names) at every
# server start; SandboxVars is read once, when the world is created. So a spawn
# change is deployable on the live world -- push, restart, done -- while a
# SandboxVars change is not, and needs a wipe to mean anything.
#
# Sharing zomboid_config_force between them would mean force-pushing the whole
# preset to land a one-line spawnregions edit, which also rewrites the INI (a
# ResetID mismatch tells every client to reroll) and the world's SandboxVars.
#
# make deploy TAGS=zomboid-conf -e zomboid_spawn_force=true
#
# then restart the server -- with players offline -- for it to take effect.
- name: seed zomboid spawn config
become: true
ansible.builtin.copy:
src: "zomboid/sophie/{{ item }}.lua"
dest: "{{ zomboid_path }}/data/Server/{{ zomboid_server_name }}_{{ item }}.lua"
force: "{{ zomboid_spawn_force | bool }}"
owner: "{{ podman_subuid.stdout }}"
group: "{{ podman_user }}"
mode: 0644
loop:
- SandboxVars
- spawnregions
- spawnpoints
notify: restorecon podman
@@ -231,7 +260,7 @@
state: started
daemon_reload: true
# A pristine copy of the Sophie preset, kept where the server cannot overwrite it.
# The settings this repo would deploy, kept where the server cannot overwrite them.
#
# Reference only -- nothing applies this automatically. A wipe deliberately leaves
# the live settings alone so hand tuning survives it.
@@ -242,12 +271,21 @@
# much as an input, and that is how a Sophie world quietly became an Apocalypse
# one -- 139 values reverted, loot from 0.35 back to 0.9, CharacterFreePoints
# from 0 to 60. When that happens again, this is what to copy back from.
#
# What it holds changed on 2026-08-31. It was the pristine Sophie preset; it is
# now a snapshot of the live debbzoid world, because the admins' tuning had by
# then diverged from the preset in the same 139 values and re-seeding from the
# preset would have thrown that tuning away rather than restored it.
#
# force is on here, and only here. Nothing on the host writes this directory --
# the server cannot see it -- so it has no hand edits to protect, and if it does
# not track the repo it is not a restore point, just an older world's settings.
- name: seed canonical zomboid world settings
become: true
ansible.builtin.copy:
src: "zomboid/sophie/{{ item }}.lua"
dest: "{{ zomboid_path }}/config-template/{{ item }}.lua"
force: "{{ zomboid_config_force | bool }}"
force: true
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: 0644
@@ -8,11 +8,30 @@
Only the keys this deployment actually owns are templated -- credentials,
RCON, Discord, backup cadence, and the mod lists. Every gameplay setting
(PVP, safety system, safehouses, sleep, MaxPlayers, PauseEmpty, ResetID,
the welcome message) is left exactly as the preset author shipped it.
the welcome message) is a literal, and was the preset author's value until
the live-config sync below.
Seeded once, then left alone: the deploy task uses force=false so the
server and admins can hand-edit this file between world regenerations.
Re-push it deliberately with -e zomboid_config_force=true.
Synced from the running debbzoid server on 2026-08-31, so a deliberate
force-push cannot silently revert what the admins set in-game. Eight keys
had drifted from the preset and now carry the live values:
PlayerSafehouse false -> true
SafehouseAllowNonResidential false -> true (the diner/gas-station case)
SafehouseAllowRespawn false -> true
SafehouseAllowLoot true -> false
SafehouseAllowFire true -> false
TrashDeleteAll false -> true
MapRemotePlayerVisibility 1 -> 4
ResetID 6953472 -> 826046
ResetID is in that list on purpose. It is the world's soft-reset token: if
the file's value ever differs from the one the live world was created with,
every connected client is told to roll a new character. Carrying the live
value means a force-push is a no-op instead of a server-wide wipe prompt.
-#}
{% set rename_map = {} %}
{% for r in zomboid_mods_renamed %}{% set _ = rename_map.update({r.old: r.new}) %}{% endfor %}
@@ -83,7 +102,7 @@ DefaultPort=16261
UDPPort=16262
# Reset ID determines if the server has undergone a soft-reset. If this number does match the client, the client must create a new character. Used in conjunction with PlayerServerID. It is strongly advised that you backup these IDs somewhere Min: 0 Max: 2147483647 Default: 985847558
ResetID=6953472
ResetID=826046
# Enter the mod loading ID here. It can be found in \Steam\steamapps\workshop\modID\mods\modName\info.txt
Mods={{ resolved_mod_ids | reject('in', excluded_mod_ids) | join(';') }}
@@ -131,7 +150,7 @@ AnnounceAnimalDeath=false
SaveWorldEveryMinutes=10
# Both admins and players can claim safehouses
PlayerSafehouse=false
PlayerSafehouse=true
# Only admins can claim safehouses
AdminSafehouse=false
@@ -140,13 +159,13 @@ AdminSafehouse=false
SafehouseAllowTrepass=true
# Allow fire to damage safehouses
SafehouseAllowFire=true
SafehouseAllowFire=false
# Allow non-members to take items from safehouses
SafehouseAllowLoot=true
SafehouseAllowLoot=false
# Players will respawn in a safehouse that they were a member of before they died
SafehouseAllowRespawn=false
SafehouseAllowRespawn=true
# Players must have survived this number of in-game days before they are allowed to claim a safehouse Min: 0 Max: 2147483647 Default: 0
SafehouseDaySurvivedToClaim=0
@@ -155,7 +174,7 @@ SafehouseDaySurvivedToClaim=0
SafeHouseRemovalTime=144
# Governs whether players can claim non-residential buildings.
SafehouseAllowNonResidential=false
SafehouseAllowNonResidential=true
SafehouseDisableDisguises=true
@@ -328,7 +347,7 @@ BanKickGlobalSound=true
RemovePlayerCorpsesOnCorpseRemoval=false
# If true, player can use the "delete all" button on bins.
TrashDeleteAll=false
TrashDeleteAll=true
# If true, player can hit again when struck by another player.
PVPMeleeWhileHitReaction=false
@@ -352,7 +371,7 @@ CarEngineAttractionModifier=0.5
PlayerBumpPlayer=false
# Controls display of remote players on the in-game map. 1=Hidden 2=Friends 3=Friends and nearby players 4=Everyone Min: 1 Max: 4 Default: 1
MapRemotePlayerVisibility=1
MapRemotePlayerVisibility=4
# Min: 1 Max: 300 Default: 5
BackupsCount=10
@@ -86,7 +86,8 @@ fi
# every later wipe regenerates from them -- which is how a Sophie world became an
# Apocalypse one. Nothing here corrects that automatically any more, so the
# snapshot above is the way back: config-backup/ holds the settings as they were
# before each wipe, and config-template/ holds the pristine Sophie preset.
# before each wipe, and config-template/ holds what this repo deploys -- since
# 2026-08-31 a snapshot of Greg's live tuning, not the pristine Sophie preset.
# Start server
log "Starting zomboid service..."