Skip to content

fix(cheats): keep the teleport minimap repair in saved games - #2646

Merged
hckrman101 merged 2 commits into
NotRexed:mainfrom
mooki000:fix/debug-teleport-keeps-minimap
Nov 12, 2026
Merged

hckrman101 merged 2 commits into
NotRexed:mainfrom
mooki000:fix/debug-teleport-keeps-minimap

Conversation

@mooki000

@mooki000 mooki000 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

What this is for: keep the minimap restored after saving and restarting following a Debug teleport (#2588). What is measured: Linux / RTX 4090, main 09aac439 versus 4b2388fc: the live flag, the game's own saved flag, and inspected pictures before teleport, after teleport, and after a fresh process restart. Not measured: frame rates, Windows/AMD/Steam Deck, the reporter's save, or the exact Lake Hylia route that originally set the flag.

Problem

This completes @jarynclouatre's approach in #2402, based on @Zarigis's flag diagnosis in #2217. The existing accepted-teleport guard is right; the remaining problem is how the flag is written.

Two separate results on main:

  1. The live repair works. An accepted teleport clears MapIconDeactivateByAreaActor, and the minimap appears again. The log says the area left had hidden the minimap ... shown again.
  2. That repair is not saved. System → Save writes a fresh save with the flag still set. Loading that save in a new process hides the minimap again.

The existing helper writes only the Bool entry's live byte. The engine also keeps a saved copy and update bookkeeping. The normal Bool setter at 0x806a2b20 takes (manager, value, hash) and queues the change through that machinery. The queue consumer updates the live and saved values, or marks a pending saved update; a direct byte write bypasses it. Sources: host/hos_main.c on main, the local binding trace, and the save/reload comparison below; no generated game code is included.

What changed

  • host/hos_main.c: after the same accepted-request check, call the engine's Bool setter for the existing minimap-suppression hash instead of the live-only helper. Preserve the caller's registers and log restore queued, matching the deferred operation.
  • tests/test_teleport_minimap_flag.py: keep the existing request/refusal coverage and give the fixture separate live and saved state, an engine-update step and a reload. Also check setter arguments, register restoration, and an unavailable setter binding. --source allows the same test to exercise main's implementation.
  • readme.md, the debug tools paragraph: how a save that already lost its minimap is mended.

The destination/event path, interface settings, and refusal behavior are unchanged. A destination can still hide its own minimap. The age switch's immediate live update remains unchanged because the destination choice immediately reads the new age; the minimap repair in its shared teleport path uses the engine setter. Sources: host/hos_main.c:1001-1015,1030-1076 at 4b2388fc.

How to recover an affected save with this change

Load it, use Debug → Teleport → Hyrule Field, wait for gameplay to resume, then System → Save. The native save and fresh restart in the fixed runs below retain the minimap. Older save slots are not rewritten; repeat the recovery if loading another affected slot.

How it was tested

The runs below were made on 4b2388fc over main 09aac439. The branch has since been rebased onto main de866017 as 03a63ea3 (with the recovery note moved from the developer notes to the readme's debug tools paragraph); the regression test was run again there with GCC and Clang (passes) and against main's host/hos_main.c (fails with both).

Arch Linux, kernel 7.2.15-1-omarchy, RTX 4090, NVIDIA 615.78.08; headless, 2048×1152 output, 1280×720 captures, frame generation off. Main 09aac43907a90ae62c8ccd98c77b471d2b94eac2; fix 4b2388fc77f1f5eb078531ab51171294a0007b28. Runs on 11 November 2026, local UTC−06:00. Prepared caches were reused and shader precompilation disabled for these runs. Sources: both build logs and the runs' hookshot_log.txt / recorded environments.

Game-free regression

Both GCC 16.2.1 and Clang 23.1.3:

Source passed to the same updated fixture Result
Main 09aac439 Fails: a native save after teleport kept the minimap hidden; the minimap disappeared again after a save and reload
Fix 4b2388fc Passes: accepted teleport repairs live and saved minimap state; refusals and missing bindings leave it alone

The fixture is Linux-only, as before; it needs no game files. Source: tests/test_teleport_minimap_flag.py and the captured GCC/Clang regression output.

Whole tests/run_all.py, once with CXX=g++: 453 passed, 3 failed, 20 skipped, including a passing minimap test. The failures are outside the changed implementation:

Those failures were retained, not patched or rerun here; no new full-main baseline was run. Source: the full-suite output, ending with 453 passed, 3 failed, 20 skipped and No coredumps found.

In the game

A normal main run from Hyrule Field through the Lake Hylia debug destination and then Lost Woods kept its minimap. To test an already affected save reproducibly, a copy of a local test save had only the previously reported suppression flag set. This is controlled reproduction of the persistent-flag case, not reproduction of the original area trigger. Sources: inspected normal-route captures and the asserted one-byte save preparation.

Run / start Build Observed result Native saved flag
Normal route / 21:00:43 main Minimap visible before and after both debug teleports 0
Affected-state recovery / 21:17:22 main Hidden flag 1 → live 0; minimap returns; System → Save still writes hidden 1
Reload and repair / 21:49:25 fix Main's native save reloads hidden; accepted teleport changes live and saved values to 0; native Save 0
Fresh restart / 22:01:55 fix Continue from the repaired native save; minimap remains visible, live/saved values both 0 0

Sources: per-run logs, read-only flag samples, native saved-file inspection, and the corresponding inspected pictures. All four runs ended normally, with no fatal/crash/device-lost, Xid/cache-operation or NVRM lines and no core dumps. They were shared runs: no performance claim. Start → end load averages (1/5/15 minute) were respectively 31.34/14.47/12.45 → 10.10/13.48/12.68, 10.15/9.55/10.30 → 7.76/9.05/10.09, 66.19/28.82/22.11 → 6.80/18.64/19.76, and 7.62/9.02/14.16 → 7.62/8.84/13.88 (recorded run summaries).

The fixed run's log contains:

cheats: teleport: the area left had hidden the minimap (MapIconDeactivateByAreaActor): restore queued

One scripting mistake is retained in the evidence: the first-use map screen was still open, so one teleport request timed out without being taken. After inspecting and closing the menu, the same process completed the repair and native save. Opening and closing the actual full map left the stuck flag at 1; it was not a recovery shortcut in this test. Sources: the request replies, map-page captures, and read-only flag samples.

Pictures and captions

Minimap before teleport, after arrival at Lake Hylia, and after teleporting to Lost Woods.

the minimap before a teleport, at the Lake Hylia destination and after teleporting to the Lost Woods

Main's temporary restoration; loss after loading its native save; fixed restoration; minimap still present after native Save and a fresh restart.

main restores the minimap only until the save is loaded again; with the fix it stays after a save and a restart

Every frame in both labelled comparisons was inspected.

Not tested

The reporter's exact save/route, a naturally entered map-hiding area, the v2.1.0 binary itself, a 90-fps cap, Windows, AMD, Steam Deck, physical controllers, and Switch age as a runtime action. The save was synthetic only at the initial stuck-flag setup; all subsequent saves in the comparison were made by the game.

Risks

The binding is for the already supported game build, like the adjacent teleport bindings. The change is queued rather than synchronously forcing the live byte; the game owns its application and saved-copy bookkeeping. A missing binding leaves suppression unchanged instead of pretending to repair it. No save file is edited by Hookshot's host code.

Overlaps

Refs #2402 and #2217. The overlap check reported clean merges for its code-file overlaps. I read #2371's host hunk (destination-picture capture ticking) and #2566's host/docs hunks (output/aspect handling); neither modifies this repair block. Other PRs sharing these large files were not functionally reviewed here.

Refs #2588 (closed as fixed by #2402; this completes that fix for the saved flag. The exact route that first set the flag remains unconfirmed).

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra noreply@openai.com

Complete the live-state repair from NotRexed#2402 by requesting the game-data change through the engine Bool setter. A direct live-byte clear leaves the serialized value unchanged, so the minimap disappears again after a save and reload.

Keep the existing accepted-teleport guard and age-selection behavior. Extend the game-free fixture with separate live and saved state, an engine update, reload, refused requests and missing bindings.

Refs NotRexed#2402
Refs NotRexed#2588

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <noreply@openai.com>

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a clean follow-up to #2402: same accepted-request guard, but the clear now goes through the game's own setter instead of poking the live byte, so the save's copy follows. The save → fresh-restart run in the description is the evidence that counts here, and the fixture now models live and saved state separately, so it fails on main's source and passes on this one.

Checked:

  • Registers: *c = saved before and after the setter call leaves the caller's context as it was; the setter runs on the caller's SP, like the other GameData calls here.
  • The guard is unchanged: a throttled or refused teleport, or an answer of 0 from the event manager, doesn't queue anything (the test covers each case).
  • No setter binding means no write and no "restore queued" line, instead of claiming a repair. Good.
  • CI: the failing Python checks are the same set main fails right now (test_env_on, test_menu_i18n, test_hdr_encode, test_linux_docker_packages, test_mouse_native, plus the fixtures #2648 repairs). test_teleport_minimap_flag.py passes on both Linux parts.

Non-blocking, for a follow-up:

  • Switch age writes Sequence_IsChildhood with the same live-only oot_gamedata_set_bool. If your finding holds for every Bool, a System → Save after Switch age would store the old age's saved copy. The description says Switch age wasn't tested as a runtime action. Could you check it once in a later PR (switch age, save, restart)? If it's the same bug, queuing the setter alongside the live write (the destination still needs the live value straight away) would fix it.
  • Nit: the comment above oot_gamedata_set_bool still just says it writes the bool directly. Worth adding "live value only, a save keeps the old one" so nobody picks it for a flag that has to persist.

Approving.

Comment thread host/hos_main.c
set_bool(c);
*c = saved;
hos_log("cheats: teleport: the area left had hidden the minimap (MapIconDeactivateByAreaActor): restore queued");
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: when the setter isn't bound, this does nothing and logs nothing. A line like ...minimap: setter not found, left hidden would make a bug report from a different game build easier to read. Not blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 3eb1591b: with the setter or the game data manager not found it now logs cheats: teleport: the area left had hidden the minimap (MapIconDeactivateByAreaActor): the game's Bool setter not found, left hidden (or the game data manager not found), and the fixture's missing-binding case expects that line and no restore queued. That check fails on the commit before it.

Review of NotRexed#2646: with the game's Bool setter or the game data manager not
found (another game build), the teleport left the minimap hidden and logged
nothing. It now says which of the two was not found. The test expects that
line and no "restore queued" in that case.

Refs NotRexed#2588

Assisted-by: Claude Fable 5.2
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@hckrman101
hckrman101 merged commit 59316c4 into NotRexed:main Nov 12, 2026
6 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants