CustomWorldStorage

Where player-made worlds live: ten slots per player, backups, thumbnails, sharing, edited shipped worlds and the player's galaxy.

Location

Headersrc/GalaxyEggbert/Editor/CustomWorldStorage.hpp
Sourcessrc/GalaxyEggbert/Editor/CustomWorldStorage.cpp; web download and upload in src/GalaxyEggbert/Editor/PlatformFiles.{hpp,cpp}
NamespaceGalaxyEggbert::Editor (free functions and constants, no class)
Engine dependencyNone: std::filesystem, the world model (World, MoveObjectRecord), Game/UserFolder.hpp and Game/DemoReplay.hpp. Only PlatformFiles.cpp has Emscripten code.

Purpose

CustomWorldStorage is the file layer of the 3D world editor and of the player's own galaxy. It names every file, lists what is on disk, and copies, backs up, restores, exports and imports worlds. It is deliberately not recorded in SaveData: the editor scans the directories each time, so a list cannot drift away from the files. Worlds belong to one of the three players (gamer slots 0-2, shown as A-C) chosen on the main menu.

All paths are inside the player's user folder (Game/UserFolder.hpp, since 2026-11-10). On the desktop that is $XDG_DATA_HOME/Galaxy Eggbert (by default ~/.local/share/Galaxy Eggbert) on Linux, %LOCALAPPDATA%\Galaxy Eggbert on Windows (not verified), or a folder given with --user DIR or GALAXY_USER_DIR. The web build uses /save, which is kept in the browser's storage, and Android uses the app's private storage. Before 2026-11-10 the files were in the working folder; an existing player's files are copied over on the first start.

Folder layout

customworlds/                      CustomWorldsRoot()
  gamer0/ gamer1/ gamer2/          CustomWorldsDir(gamer), players A-C
    custom_001.vwr ... custom_010.vwr   the ten slots (SlotWorldPath)
    custom_003.png                 a slot's thumbnail (ThumbnailPath)
    custom_011.vwr, other names    older worlds: listed, never deleted
    backups/custom_003.1.vwr       backups, .1 newest ... .3 oldest
    .demo-seeded, .gallery-seeded  seeding markers
    galaxy/                        GalaxyDir(gamer): the player's galaxy
      world001.vwr                 main hub
      world010.vwr ... world140.vwr    world hubs 1-14
      world011.vwr ... world148.vwr    missions 1-8 of each world
      world199.vwr                 bonus world
      name.txt, backups/
  shared/                          SharedWorldsDir(): A-world-3.vwr, A-galaxy.galaxy ...
  campaign-backups/                xnjttjpo: backups of edited shipped worlds
  campaign-edits/                  xnjttjpo: a copy of every save of a shipped world
  campaign-originals/              desktop: shipped worlds as they came, before the first edit

On the web the originals are kept in memory in worlds3d-shipped beside worlds3d/, because the page's packed data holds the shipped worlds anyway.

Collaborators

Uses: UserPath() (UserFolder.hpp), World::saveToFile/loadFromFile, PlaceMoveObject for the starter worlds, DemoReplay (recordings made in a galaxy travel with its pack), FreeFileName (numbered export names), and on the web OfferDownload and RequestUpload from PlatformFiles.hpp (a download, and the browser's file picker writing into shared/; elsewhere they do nothing and return false).

Used by: WorldEditor (seeding on EnterBrowser, backups and thumbnails on save), EditorBrowserScreen and GalaxyBrowserScreen (the slot and galaxy screens), GalaxyEggbertGame (opening galaxy worlds, xnjttjpo, Pause's "Original", galaxy demos) and main.cpp (the web's ApplyCampaignEdits at start).

Key members

A selection of the declarations in CustomWorldStorage.hpp, as of 2026-11-10 (Worlds::World is GalaxyEggbert::Worlds::World).

MemberSignatureWhat it does
kCustomWorldSlotCount, kBackupGenerationsconstexpr int = 10, 3Slots per player; backup generations per world.
CustomWorldsDirstd::filesystem::path CustomWorldsDir(int gamerSlot)The player's folder, customworlds/gamerN.
SlotWorldPathstd::filesystem::path SlotWorldPath(int gamerSlot, int slot)Slot 1-10's file, custom_%03d.vwr; may not exist yet.
ListOlderCustomWorlds, CopyIntoFreeSlotstd::vector<std::filesystem::path> ListOlderCustomWorlds(int gamerSlot), std::filesystem::path CopyIntoFreeSlot(int gamerSlot, const std::filesystem::path& source)Worlds outside the ten slots; copying one (or a shared world) into the first free slot, the source untouched.
NextNewWorldPathstd::filesystem::path NextNewWorldPath(int gamerSlot)The first free slot's path; empty when all ten are taken.
BackUpWorld, BackUpWorldTobool BackUpWorld(const std::filesystem::path& world), bool BackUpWorldTo(const std::filesystem::path& world, const std::filesystem::path& backupDir)Keep the file as the newest backup, shifting the older ones and dropping the oldest; the second form keeps them elsewhere (edited shipped worlds).
RestoreBackupbool RestoreBackup(const std::filesystem::path& world)The newest backup takes the world's place and the current world becomes the oldest backup, so repeated restores cycle back to the start.
DeleteSlotWorldbool DeleteSlotWorld(const std::filesystem::path& world)Deletes a world into its backups (and drops its thumbnail).
ThumbnailPathstd::filesystem::path ThumbnailPath(const std::filesystem::path& world)The PNG beside the world: the editor's view at its last save.
SharedWorldsDir, ExportWorld, ListSharedWorldsstd::filesystem::path ExportWorld(int gamerSlot, int slot)Copies a slot into shared/ as ExportName() (A-world-3.vwr), replacing an earlier export of that slot.
SeedShowcaseSlot, SeedGallerySlotbool SeedShowcaseSlot(int gamerSlot, const std::filesystem::path& showcase), bool SeedGallerySlot(int gamerSlot, const std::filesystem::path& gallery)Slot 1 gets the showcase world (999) once, only if empty; the first free slot gets the gallery world (998), kept current while unedited.
CampaignBackupsDir, CampaignEditsDir, CampaignOriginalsDirstd::filesystem::path ...()The folders for shipped worlds edited with xnjttjpo.
ApplyCampaignEditsint ApplyCampaignEdits(const std::filesystem::path& worldsDir, const std::filesystem::path& originalsDir = {})Lays each kept edit over the shipped world of that name (the web, at every start).
HasCampaignEdit, KeepCampaignOriginal, RevertCampaignEditbool HasCampaignEdit(const std::filesystem::path& world) ...Whether a shipped world is still its kept edit; keep the original before the first edit; Pause's "Original" puts it back.
GalaxyDir, GalaxyWorldPath, IsGalaxyMissionstd::filesystem::path GalaxyWorldPath(int gamerSlot, int mission), bool IsGalaxyMission(int mission) noexceptA galaxy place's file, galaxy/world%03d.vwr; valid places are 1, the hubs 10-140, their missions 1-8, and 199.
CreateEditorStarterWorld, CreateGalaxyStarterWorldWorlds::World CreateEditorStarterWorld(), Worlds::World CreateGalaxyStarterWorld(int mission)A new world: a 3 x 3 rock board with a chest and an exit arrow; for a galaxy hub, the board with a world or mission sign (and a return sign) and Blupi's start.
PackGalaxy, UnpackGalaxystd::string PackGalaxy(int gamerSlot), std::optional<GalaxyPack> UnpackGalaxy(const std::string& pack)The whole galaxy as one .galaxy file and back.
ExportGalaxy, ImportGalaxy, DeleteGalaxystd::filesystem::path ExportGalaxy(int gamerSlot), int ImportGalaxy(int gamerSlot, const std::filesystem::path& pack, int* demos = nullptr), std::filesystem::path DeleteGalaxy(int gamerSlot)Export into shared/ (numbered, never replacing); import replacing the player's galaxy (old places into backups); delete after exporting a pack first.
GalaxyName, SetGalaxyNamestd::string GalaxyName(int gamerSlot), bool SetGalaxyName(int gamerSlot, const std::string& name)The galaxy's name in galaxy/name.txt: letters, digits, spaces, - and ', at most 24 characters.

The galaxy pack format

A .galaxy file is text headers with raw bytes between them: GALAXY_PACK 1, an optional name <size> entry with the galaxy's name, then world <mission> <size> and that many .vwr bytes per place, demo <position> <size> and the .gdemo bytes for each recording the player made in the galaxy, and finally END with the FNV-1a hash of everything before it in 16 hex digits. UnpackGalaxy refuses a damaged or truncated pack, another version, a duplicated place or position, a place that is not a galaxy's, or a world that does not load. A demo recorded with another simulation revision is left out and the galaxy is kept. Imported demos go to the shared demos folder, never over one of the player's positions.

How it is used

The editor saves through WorldEditor::SaveWorld (in src/GalaxyEggbert/Editor/WorldEditorUpdate.cpp, shortened). The first save of a visit backs the old file up; a save of a shipped world opened from play is also copied into campaign-edits/; a slot's save asks the host for a thumbnail:

void WorldEditor::SaveWorld(const Worlds::World& world)
{
    if (backedUpPath_ != worldPath_)
    {
        (void)(outsideSession_ && !outsideBackupDir_.empty() ? BackUpWorldTo(worldPath_, outsideBackupDir_)
                                                             : BackUpWorld(worldPath_));
        backedUpPath_ = worldPath_;
    }
    world.saveToFile(worldPath_);
    if (outsideSession_ && !outsideCopyDir_.empty())
    {
        ...
        std::filesystem::copy_file(worldPath_, outsideCopyDir_ / worldPath_.filename(),
                                   std::filesystem::copy_options::overwrite_existing, error);
    }
    if (!outsideSession_)
    {
        thumbnailRequest_ = ThumbnailPath(worldPath_);
    }
    ...
}

When a player opens the editor, WorldEditor::EnterBrowser calls SeedShowcaseSlot(gamer, "worlds3d/world999.vwr") and SeedGallerySlot(gamer, "worlds3d/world998.vwr"). The galaxy screen opens GalaxyWorldPath() files and creates missing places from CreateGalaxyStarterWorld(). When a galaxy is imported or deleted, the host resets that player's galaxy progress in SaveData.

Rules and invariants

Tests

VerifyGEEditorStorage (tools/VerifyGEEditorStorage.cpp, registered with ge_add_editor_verify, runs in the build directory's verify-run/) covers the ten slots and older worlds, showcase and gallery seeding, the three backup generations and restore cycle, backup on the editor's first save, export and import through the world screen, galaxy paths and starter worlds, backups in another folder for xnjttjpo, the web's campaign edits and Pause's "Original", the galaxy screen's delete and restore, packing (damaged and invalid packs refused), export and import between players, the import list's paging by eight, galaxy names (including the on-screen keyboard), and galaxy recordings in packs. Live QA step files: tools/qa_steps/editor_backups.txt and editor_thumbnails.txt.

Related pages