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:
| Kind | Path | Loaded by |
|---|---|---|
| Campaign (shipped) | worlds3d/worldNNN.vwr relative to the working folder (MissionWorldPath(), GameHelpers.hpp) | LoadMission() |
| A player's galaxy | customworlds/gamerG/galaxy/worldNNN.vwr in the player's folder (Editor::GalaxyWorldPath()) | LoadMission() while galaxyGamer_ >= 0 (StartGalaxy(), --galaxy) |
| Custom slot | customworlds/gamerG/custom_00N.vwr in the player's folder | LoadCustomWorldForEditing(), LoadCustomWorldForPlayTest() |
| Title scene | worlds3d/title.vwr | LoadTitleScene() |
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
- 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.
- Resume bookkeeping. Unless a resume is being replayed, the old
resume.txtis deleted and recording stops. Transient state (title scene, win cheer, pending skateboard, teleport guard) is cleared. - 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 asvalidatedBytes, so a file replaced in between cannot change what is played. - 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. - Identity.
loadedMissionPath_andloadedMissionHash_ = 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()). - Presentation.
RebuildWorldPresentation()(below). - 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. - 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 toSeedRandom(). - Fresh Blupi.
blupi_ = BlupiController(), thenSetSolidBoxes(worldRuntime_.SolidBigDecorBoxes())so solid BigDecor blocks him (a fresh controller knows none). - 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 callsBlupiController::SettleOnGround(), so he stands (and can walk on the first tick) instead of starting in the air. The chase camera is reset behind him. - Save.
saveData_.SetMissionNumber(n)and the save file is written.missionStartPending_is set, and on the nextUpdate()in PlayStartResumeLog()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();
World::loadFromStream()validates the file completely (The .vwr Format).ResyncFromWorld()derives everything the game needs from the world: sky region, mission number, spawn cell, hint zones (CollectHintZones()), BigDecor cells (CollectBigDecor(), shifted into render space) and the live object list fromCollectMoveObjects()(see Object Lifecycle). The editor calls the same function after every edit, so edited objects appear without a save.
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:
- Every saw cell (378-383) with air above becomes an explicit floor block, and the saw moves up one cell. The floor material is the most common full, still, harmless block within three cells at the same level (or one below); rock if none.
- The saw's plate-turn record moves with it.
- A switch whose row (X ± 20, same Y and Z) contained moved saws gets a type 6 record so it still drives the row one cell up.
- A saw with something already above it is left where it is and becomes an independent saw.
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:
ResyncFromWorld()again (a no-op unless the editor changed the world), copy the BigDecor cells, hand the BigDecor shapes measured from the 3D models toWorldRuntimeand the resulting solid boxes to Blupi.- Background:
Content/backgrounds/decorNNN.pngfor 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. - A new TerrainRenderer for the world, block models applied (
ApplyBlockModels()), andterrainRevision_set to the world'sblockRevision().
Galaxy, custom and play-test worlds
- Galaxy.
StartGalaxy()opens the editor's galaxy screen if the selected player has no main hub yet; otherwise it setsgalaxyGamer_, restores the galaxy's mission and lives and callsLoadMission(). Everything else is the campaign path. See Your Own Galaxy. - Editing a custom slot.
LoadCustomWorldForEditing()loads the file, rebuilds the presentation and starts the editor's free camera above the terrain's centroid (or at (0, 15, 0) for an empty world). - Play-testing.
LoadCustomWorldForPlayTest()loads the file with a freshInteractionSystem(no lives carried over), places Blupi at the spawn or the starter position and enters Play. No resume log is recorded; Win or Lost return to the editor.
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()).
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
VerifyShippedWorlds: every world inworlds3d/loads through the game's reader (including the saw upgrade), carries its mission number and givesWorldRuntimeits objects.WorldTests.IndependentSawFlagIsPreservedWithoutRewritingLegacyData: the flag survives and loading alone never changes bytes.VerifyBlupiMovement: placement establishes standing before the first input tick.- Autoplay playthroughs and the resume equality self-tests (Testing).