GameMaker Studio 2 Steam Demo Writes Saves to Preview AppData After YYC Export - How to Fix
Problem: Your GameMaker YYC export passed G3 cold launch on the one-evening export pipeline. After fest_demo Steam promotion, players report progress resets every launch. File Explorer shows *`.ini** or buffer saves under a **preview / IDE**%LOCALAPPDATA%subtree—not the path insave_path_map.json`.
Who is affected now: Teams shipping Windows YYC fest demos in June–October 2026 after the GameMaker export/save resource shipped. Export receipts pass while save APIs still resolve to IDE Run semantics: wrong display name, stale #macro SAVE_DIR, or working_directory assumptions copied from internal QA.
Fastest safe fix: Probe saves on Steam-installed exe only → lock save_dir / ini basename macros for all retail branches → diff save_path_map.json and gamemaker_steam_export_receipt_v1.json between internal and fest_demo → set save_root_match: true and gate_save_root_match: pass before SteamPipe → run Gate 6 installed smoke → add save_path to Wednesday metadata diff.
Direct answer
Progress is not “lost”—it is stored under the preview AppData profile your fest binary still uses. Branch promotion copied Game Options and macros from a profile QA tested in the IDE, not the packaged YYC save root players hit on Steam. ini_open, buffers, and custom save_dir helpers must target the same logical company/game pair on every retail branch. Fix the map, the macros, the re-export, and the receipt together; IDE Run and VM test do not validate fest behavior.
Why this issue spikes in June 2026
- The GameMaker export/save sanity resource made
save_path_map.jsonstandard—teams promotefest_demobeforesave_root_matchexists on fest evidence. - YYC exports are default for performance; teams still validate saves during IDE Run where
working_directorypoints at temp folders, not the Steam install path. - Display name or game project name changed for store branding while
inibasenames stayed on the old folder. - Playtest vs fest_public isolation fails when both branches share slot UI but different
#macro SAVE_DIRvalues—see playtest isolation playbook.
Pair with 12 Free Defold GDevelop Ren'Py save-path audit templates. For GDevelop preview-folder drift, see GDevelop save-path help.
Symptoms and search phrases
- Saves work in GameMaker IDE Run; Steam-installed demo always starts at level one.
%LOCALAPPDATA%contains two folders—one with old project display name, one with fest title.gamemaker_steam_export_receipt_v1.jsonshows G3 pass but nosave_root_matchrow.- QA validated Thursday YYC zip;
fest_demodepot behaves differently. ini_open("save.ini")succeeds but file appears under unexpected subtree.working_directoryin debug overlay shows Steam common path while saves land elsewhere.- Playtest branch saves persist; fest_public does not (shared slot label, different macro).
- After enabling
DEBUG_MODEon demo, saves write to dev-only folder.
Root causes (check in order)
working_directoryused as save root — correct beside exe on Steam; wrong during IDE Run; fest build never re-tested packaged.#macro SAVE_DIRorsave_dirstill points at preview — internal playtest path baked into shared scripts.- Game Options display name changed — Windows save area follows product name;
inistill uses old basename. - Branch promotion copied options, not save macros —
fest_demoGame Options differ; GML macros unchanged. DEBUG_MODE/ dev define enabled on demo — alternate save branch inif (DEBUG_MODE)still compiled into retail.- Duplicate
gameproject/ renamed.yypwithout map update — new folder under AppData; players perceive reset. - Wrong depot promoted — playtest binary on fest branch (see save-slot label case study).
Beginner path (first 20 minutes)
Prerequisites: Windows YYC build installed from Steam or local zip (not IDE Run), GameMaker project using ini and/or buffer saves.
- Install from
fest_demo(or fest-profile local export identical to Steam). - Play to first save trigger; note the time.
- Search
%LOCALAPPDATA%for files modified in the last 10 minutes. - Compare folder name to
expected_localappdata_subtreeinsave_path_map.json.
Common mistake: Pressing Run in the IDE after export—always validate the packaged exe Steam ships.
Fastest safe fix path
Step 1 — Prove where the Steam build writes (packaged YYC only)
- Install from
fest_demobranch. - Delete documented save folders from old map (backup first).
- Launch; trigger save at first checkpoint.
- Locate new files:
Get-ChildItem -Path $env:LOCALAPPDATA -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-10) } |
Select-Object FullName, LastWriteTime
Optional in-game probe (remove before ship or guard with globalvar dev flag):
// One-shot overlay — fest debug build only
show_debug_message("working_directory=" + working_directory);
show_debug_message("LOCALAPPDATA=" + environment_get_variable("LOCALAPPDATA"));
save_open_ini();
show_debug_message("ini_slot0_exists=" + string(ini_key_exists("slot0")));
ini_close();
Pass: Path matches expected_localappdata_subtree in map—not IDE preview or retired product name.
Fail: Two subtrees exist → continue to Step 2.
Outbound references: GameMaker — Saving And Loading Data, Game Options, Microsoft — LOCALAPPDATA.
Step 2 — Normalize save_dir macros and ini basenames
Centralize save identity—no scene variables with absolute paths:
#macro SAVE_COMPANY "YourStudio"
#macro SAVE_GAME "FestDemo2026"
#macro SAVE_INI "demo_progress_v1.ini"
function save_open_ini() {
ini_open(SAVE_INI); // resolves under GameMaker save area for packaged game
}
| Anti-pattern | Fix |
|---|---|
ini_open(working_directory + "save.ini") |
Use basename only; let runtime pick save area |
Different SAVE_INI per branch (playtest.ini vs demo.ini) |
One retail basename; version inside file |
if (DEBUG_MODE) ini_open("dev_save.ini") |
Same basename; separate Steam branch, not define |
Display name changed, SAVE_GAME macro stale |
Update macro + map together |
Add to save_path_map.json:
{
"schema": "save_path_map_v1",
"engine": "gamemaker_studio_2",
"company": "YourStudio",
"game": "FestDemo2026",
"export_type": "YYC",
"expected_localappdata_subtree": "%LOCALAPPDATA%/YourStudio/FestDemo2026/",
"ini_basenames": [
{
"slot_id": "demo_progress",
"file": "demo_progress_v1.ini",
"forbidden_substrings": ["preview", "internal_playtest"]
}
],
"save_format_version": 1
}
Lock Game Options → General → Game Settings display name to match game field before re-export.
Step 3 — Diff maps and receipts between branches
Before promoting fest_demo:
fc /n release-evidence\gamemaker\internal\save_path_map.json `
release-evidence\gamemaker\fest_demo\save_path_map.json
Extend gamemaker_steam_export_receipt_v1.json:
{
"schema": "gamemaker_steam_export_receipt_v1",
"gamemaker_runtime": "2024.11.0.000",
"export_type": "YYC",
"gates": {
"G3_cold_launch": "pass",
"G6_save_root_match": "pass"
},
"save_root_probe": {
"working_directory": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\FestDemo2026\\",
"verified_localappdata_subtree": "%LOCALAPPDATA%/YourStudio/FestDemo2026/",
"ini_basename": "demo_progress_v1.ini",
"slot_count_after_round_trip": 3
},
"save_root_match": true,
"build_label": "gm-nextfest-2026-05-24-rc1",
"branch": "fest_demo"
}
Block promotion if save_root_match is not true or G6_save_root_match is not pass.
Step 4 — Re-export YYC fest profile and upload depot
- Open Windows / Steam Game Options preset for fest—not internal debug preset.
- Confirm *`#macro SAVE_` values match map before Create Executable**.
- Run save round-trip: save → quit exe → relaunch → load on same AppData subtree.
- Upload to
fest_demodepot only. - Log
build_labelin BUILD_RECEIPT.
Step 5 — One-time migration for early fest testers
If an old AppData subtree already shipped:
/// obj_save_controller — Create
if (!variable_global_exists("save_migrated_v1")) {
if (file_exists_legacy_preview_ini()) {
ini_open("demo_progress_v1.ini");
// copy keys from legacy ini_read_* helpers
ini_close();
global.save_migrated_v1 = true;
}
}
Ship player-facing note: first launch after patch may import progress—or reset if corrupt. Align store FAQ save claims.
Step 6 — Installed Steam smoke (Gate 6)
Run S4–S6 from Wednesday demo build smoke on the Steam-installed YYC build. IDE Run and unzipped Thursday folder do not satisfy this step.
Working dev path (proof table)
| Check | Command / artifact | Pass signal |
|---|---|---|
| Save macros locked | Grep SAVE_DIR, ini_open |
No absolute paths; one retail basename |
| Map parity | fc save_path_map.json internal vs fest |
No differences |
| Save round-trip | Gate 6 log | Same AppData subtree after relaunch |
| Branch gate | gamemaker_steam_export_receipt_v1.json |
save_root_match: true, G6_save_root_match: pass |
| Installed smoke | Wednesday S4–S6 on Steam build | Slot count unchanged |
| BUILD_RECEIPT row | save_path column |
Hash of map + ini_basename |
Attach evidence under release-evidence/gamemaker/<branch>/ beside export pipeline outputs—export sanity and persistence are separate gates.
Verification checklist
- [ ]
save_path_map.jsonidentical on internal andfest_demoevidence folders. - [ ]
SAVE_*macros unchanged since freeze week (or migration shipped). - [ ] No retired display name in verified
%LOCALAPPDATA%path on installed Steam build. - [ ]
save_root_match: truein fest receipt. - [ ] Save round-trip passes twice on fest
build_label. - [ ] Gate 6 installed smoke GREEN.
- [ ] Wednesday metadata diff includes
save_pathcolumn. - [ ]
DEBUG_MODEoff on demo retail defines. - [ ] Playtest and fest use different ini basenames if both live—isolation playbook.
Prevention
- Treat branch promotion as “re-run Gates G3 + G6,” not “copy depot ID.”
- Add
save_path_map.jsonto Monday export checklist beside texture page audit. - Ban branch-specific ini basenames without documented migration—grep
ini_openin scripts. - Add CI job: fail if fest
save_path_map.json≠ internal orSAVE_GAMEmacro drifts. - Pair with Construct NW.js save help in multi-engine studios.
Troubleshooting
| Symptom | Fix |
|---|---|
| IDE Run OK, Steam reset | Re-run Step 1 on installed YYC; lock macros |
| Two AppData folders | Old display name—migration or accept reset |
| ini exists, load empty | Read basename ≠ write basename in script |
| Only fest branch fails | Wrong depot promoted; verify build_label overlay |
| Playtest OK, fest bad | Separate playtest_progress_v1.ini vs demo_progress_v1.ini |
| Export receipt pass, players fail | Receipt from IDE Run, not Steam exe |
| VM export OK, YYC bad | Rare—re-probe; confirm same Game Options preset |
FAQ
Is this the same as GDevelop preview-folder drift?
Same branch promotion class—GDevelop save-path help uses Storage roots; GameMaker uses ini save area + display name + *`#macro SAVE_`**.
Does the one-evening export pipeline cover saves?
The GameMaker export sanity blog covers G1–G5; add G6_save_root_match for persistence before Steam upload.
YYC vs VM—does export type change save path?
Usually no—path drift comes from IDE vs packaged and macros, not YYC alone. Always probe the Steam-installed binary you ship.
Can I change the window title without breaking saves?
Store window title can differ from save identity—lock SAVE_GAME macro and Game Options display name together.
Does Steam Cloud fix this?
Only if implemented and FAQ-accurate. Default ini is local—document canonical path in save_path_map.json. If Cloud overwrites newer local saves after mid-cycle enable, see Steam Cloud sync overwrite fix.
Related links
- 12 Free GameMaker Steam Export and Save Sanity Resources (2026)
- GameMaker Steam Demo Export Sanity Check (one evening)
- GDevelop Steam Demo Preview Save Path After Promotion
- Construct 3 NW.js AppData Save Path Fest Branch Mismatch
- 12 Free Defold GDevelop Ren'Py Save-Path Audit Templates
- Mismatched Save Slot Labels After Branch Promotion
- Wednesday Demo Build Smoke Ritual
- GameMaker — Saving And Loading Data
Prove saves on the Steam-installed GameMaker YYC exe—IDE Run is not your fest demo.