World Loading

From a mission number to a playable world: file, decoding, upgrades, objects, hub doors, Blupi's start and the resume log.

The main path is GalaxyEggbertGame::LoadMission(int missionNumber, const std::string* validatedBytes = nullptr) in src/GalaxyEggbertCNA/GalaxyEggbertGameMissions.cpp. It is called for the main menu's Play (mission 1), every hub sign and exit, Continue, the --mission flag, demos and the golden captures. The decoding itself is WorldRuntime::LoadFromVwrStream() (src/GalaxyEggbert/Game/WorldRuntime.cpp). Described as of 2026-11-10.

Which file

MissionPath(n) picks the file:

KindPathLoaded by
Campaign (shipped)worlds3d/worldNNN.vwr relative to the working folder (MissionWorldPath(), GameHelpers.hpp)LoadMission()
A player's galaxycustomworlds/gamerG/galaxy/worldNNN.vwr in the player's folder (Editor::GalaxyWorldPath())LoadMission() while galaxyGamer_ >= 0 (StartGalaxy(), --galaxy)
Custom slotcustomworlds/gamerG/custom_00N.vwr in the player's folderLoadCustomWorldForEditing(), LoadCustomWorldForPlayTest()
Title sceneworlds3d/title.vwrLoadTitleScene()

Mission numbers follow the original: 1 is the main hub, 10, 20, ... 120 the world hubs, 11-18, 21-28, ... their missions, 199 the bonus world, 998 and 999 Galaxy's showcase worlds. See Campaign & Worlds.

LoadMission() step by step

  1. Demo guard. A demo shows one mission. If a demo is playing or being recorded, the request ends the demo instead and nothing is loaded.
  2. Resume bookkeeping. Unless a resume is being replayed, the old resume.txt is deleted and recording stops. Transient state (title scene, win cheer, pending skateboard, teleport guard) is cleared.
  3. Read the bytes once. ReadWorldBytes(path) reads the whole file into a string. When resuming, BeginResume() has already read and hash-checked the file and passes that same snapshot as validatedBytes, so a file replaced in between cannot change what is played.
  4. Decode. worldRuntime_.LoadFromVwrStream() on a string stream (next section). On failure the game prints "not found or failed to load" and stays on the current mission.
  5. Identity. loadedMissionPath_ and loadedMissionHash_ = ResumeLog::HashBytes(bytes) (FNV-1a 64) are kept for the resume log. A galaxy world is told its mission number from its place in the galaxy (OverrideMissionNumber()).
  6. Presentation. RebuildWorldPresentation() (below).
  7. Hubs. For any mission but 1 the save remembers SetLastWorld(n / 10). AdaptHubDoors(n) opens what the save has unlocked (HubDoors): gold bars and completed signs in the main hub, mission doors in a world hub; doors slide up with sound 33. In a world hub the arrival is past the last opened door; in the main hub beside the sign of the last world played. RefreshTerrainMeshIfChanged() then patches the terrain for the changed cells.
  8. Fresh rules state (the original's PlayPrepare()): interaction_ = InteractionSystem() with only the lives carried over; vehicles, powers, keys, dynamite and the treasure count start from zero. A random seed is drawn (or taken from the resume log or demo) and passed to SeedRandom().
  9. Fresh Blupi. blupi_ = BlupiController(), then SetSolidBoxes(worldRuntime_.SolidBigDecorBoxes()) so solid BigDecor blocks him (a fresh controller knows none).
  10. Placement. At the world's spawn cell with the yellow start's heading; for a galaxy world without a spawn, at the editor's starter position; otherwise at (0, 1, 0). A hub arrival overrides it. PlaceBlupi() sets the position, gives the controller the current crate cells and calls BlupiController::SettleOnGround(), so he stands (and can walk on the first tick) instead of starting in the air. The chase camera is reset behind him.
  11. Save. saveData_.SetMissionNumber(n) and the save file is written. missionStartPending_ is set, and on the next Update() in Play StartResumeLog() records the mission's start and begins recording input (ResumeLog).

Inside WorldRuntime::LoadFromVwrStream()

world_ = std::make_unique<Worlds::World>(Worlds::World::loadFromStream(in));
UpgradeLegacySawFloors(*world_);
...                         // any exception: return false
animPhase_ = 0;             // a new world's tiles start their cycles at 0
ResyncFromWorld();

The legacy saw upgrade

Until 2026-11-08 a saw drew an invented floor under itself. Files from that time have header flag bit 0 clear. UpgradeLegacySawFloors() (src/GalaxyEggbert/Game/LegacySawFloors.hpp) converts such a world in memory before play or editing:

The world is then marked current. Loading never rewrites the file; saving the upgraded world (for example from the editor) sets flag bit 0 so the conversion does not repeat. As of 2026-11-10, 80 of the 81 files in worlds3d/ still carry flag 0 and are upgraded each time they load.

RebuildWorldPresentation()

In GalaxyEggbertGameLoad.cpp, run for every world switch:

  1. ResyncFromWorld() again (a no-op unless the editor changed the world), copy the BigDecor cells, hand the BigDecor shapes measured from the 3D models to WorldRuntime and the resulting solid boxes to Blupi.
  2. Background: Content/backgrounds/decorNNN.png for the sky region. Only 28 of the 32 possible ids have a picture; a missing one falls back to a flat clear colour. The picture's horizon colour becomes the distance haze, and the Ultra sky light is built from it.
  3. A new TerrainRenderer for the world, block models applied (ApplyBlockModels()), and terrainRevision_ set to the world's blockRevision().

Galaxy, custom and play-test worlds

Shipped worlds edited in play

The xnjttjpo cheat opens the mission being played in the 3D editor (OpenMissionInEditor(), BD-0118). On the desktop the file in worlds3d/ is edited in place; its versions go to customworlds/campaign-backups/, every save is also copied to customworlds/campaign-edits/, and the world as shipped is kept once in customworlds/campaign-originals/ for Pause's "Original" (RevertWorld()).

Web build: the campaign-edits overlay. In the browser worlds3d/ is the page's packed data, in memory, so an edit cannot persist there. Saves are kept in the browser's storage under /save/customworlds/campaign-edits/. At every start, before anything loads a world, main.cpp calls Editor::ApplyCampaignEdits("worlds3d", CampaignOriginalsDir()), which copies each kept worldNNN.vwr over the packed file of that name, first keeping the shipped file in worlds3d-shipped/ for "Original".

Tests

Related pages