Make co-op a hidden beta in 0.8.0: the menu shows its Co-op tab only with coop_beta = 1.

No menu row sets coop_beta; it lives in nx2navi.ini under [coop] (NX2NAVI_COOP_BETA) and takes effect at once. Co-op
itself is unchanged: coop = 1 still runs a session with or without the tab. The notes and the co-op guide say how to
show the tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-11 09:47:42 -05:00
parent 7f01711087
commit d55f7e763d
5 changed files with 24 additions and 12 deletions

View File

@@ -8,6 +8,9 @@ What exists so far: rooms, the relay, a sealed channel for messages between the
menu's Co-op tab (below), which makes, joins, accepts and leaves a room. The other players are not shown in your
world, and where your Link is never leaves your PC ("Other players in the world").
**Co-op is a beta in 0.8.0.** The menu shows its Co-op tab only with `coop_beta = 1` under `[coop]` in `nx2navi.ini`;
no menu row sets it. Everything below is as it is with the tab shown.
## What joining a room means for your save
Read this before you join a room.

View File

@@ -1,8 +1,8 @@
A performance, fix and feature release for *The Legend of Zelda: Ocarina of Time* for Nintendo Switch 2 (game version
1.1.1): fewer hitches after entering an area, more frames on NVIDIA and AMD cards, the single-frame flickers and black
frames found and fixed, AMD FSR 4 upscaling, co-op over the internet to try, a debug menu for testing, and a fallback
for video cards without mesh shaders. **No game files are included**: the setup builds the game on your PC from your
own dump.
frames found and fixed, AMD FSR 4 upscaling, a debug menu for testing, a fallback for video cards without mesh
shaders, and co-op over the internet as a hidden beta. **No game files are included**: the setup builds the game on
your PC from your own dump.
## Install or update
@@ -85,9 +85,10 @@ for the launcher; a GPU with Vulkan 1.3 and `VK_EXT_external_memory_host` (curre
tested in this game yet**; on every other card it is version 3.1.6, which on an RX 7900 XT runs at the same frame
rate as before and showed clean frames in a fast camera turn. It is off unless you set it, and when it fails the FSR
3.1 library takes over. Please report how it looks on an RX 9000.
- **Co-op over the internet, to try** (off by default; the Co-op tab of the menu). Players in a room share progress by
the room's rules, each in their own save. It needs a relay server you or a friend run: the game has no address
built in. See `docs/coop.md`.
- **Co-op over the internet, a beta** (hidden: add `coop_beta = 1` under `[coop]` in `nx2navi.ini` to show the menu's
Co-op tab). Players in a room share progress by the room's rules, each in their own save; you do not see the other
players in your world. It needs a relay server you or a friend run (`tools/coop_relay.py` in the repository): the
game has no address built in. See `docs/coop.md`.
- **A fallback for video cards without mesh shaders.** The game draws part of the world with mesh shaders; on a card
without them those draws are now run as compute passes and an ordinary draw (`mesh_fallback = auto`). It draws the
same picture in our test of the path itself and runs slower than real mesh shaders. **Nobody has played with it on

View File

@@ -583,6 +583,8 @@
# The menu's Co-op tab sets the name, the relay and the rules below, and makes, joins, accepts and leaves a room.
# 0 = off: no socket is opened, no name is looked up and nothing is sent. Takes effect at once.
# coop = 0
# Co-op is a beta: the menu shows its Co-op tab only with coop_beta = 1, which no menu row sets. Takes effect at once.
# coop_beta = 0
# Your name for the other players, at most 24 bytes. Default: Player- and four random hex digits.
# coop_name =
# The relay, host or host:port (default port 24680): run one with `python tools/coop_relay.py` and forward its UDP port, or

View File

@@ -75,7 +75,7 @@ static const char *const known[] = {
"TEXUP_LOG_MS", "DYN_RES", "DYN_RES_TARGET", "DYN_RES_STEP", "TEXTURE_DETAIL",
"RECORDER_CORE", "RECORDER_PRIO", "REPLAY_TIME", "LOCK_TIME", "POOL_CREATE_BUDGET", "POOL_CREATE_MS", "POOL_FINI_IDLE", "POOL_LOAD_BACKLOG", "POOL_LOAD_MS",
"VMA_BLOCK_MB", "ASTC_BC_PROBE", "SYNC_WAKE_ALL", "LOG_ASYNC", "DEVICE_LOST_AT",
"COOP", "COOP_NAME", "COOP_SERVER", "COOP_PORT", "COOP_ROOM", "COOP_AUTO", "COOP_DROP", // co-op (coop.c); the last three have no menu row
"COOP", "COOP_BETA", "COOP_NAME", "COOP_SERVER", "COOP_PORT", "COOP_ROOM", "COOP_AUTO", "COOP_DROP", // co-op (coop.c); the last three have no menu row
"COOP_PROGRESS", "COOP_ITEMS", "COOP_SONGS", "COOP_QUEST", "COOP_KEYS", "COOP_RUPEES", "COOP_AMMO", // the room's rules (coop_game.c)
"COOP_TEST_SET", // test: sets flags by hash at given frames (coop_game.c); no menu row
"COOP_ACCEPT", // accepts a co-op room's members and rules before anything of it is applied (coop_game.c)
@@ -104,7 +104,7 @@ static const struct { const char *key, *section; } sections[] = {
{"FRAMEGEN", "display"}, {"FRAMEGEN_MULTIPLIER", "display"}, {"FG_MULT", "display"},
{"DEVICE_PROFILE", "display"}, {"ASSUME_FOCUS", "display"},
{"DISCORD_RPC", "discord"}, {"DISCORD_APP_ID", "discord"}, {"DISCORD_IMAGE", "discord"},
{"COOP", "coop"}, {"COOP_NAME", "coop"}, {"COOP_SERVER", "coop"}, {"COOP_PORT", "coop"},
{"COOP", "coop"}, {"COOP_BETA", "coop"}, {"COOP_NAME", "coop"}, {"COOP_SERVER", "coop"}, {"COOP_PORT", "coop"},
{"COOP_PROGRESS", "coop"}, {"COOP_ITEMS", "coop"}, {"COOP_SONGS", "coop"}, {"COOP_QUEST", "coop"}, {"COOP_KEYS", "coop"},
{"COOP_RUPEES", "coop"}, {"COOP_AMMO", "coop"}, {"COOP_ACCEPT", "coop"},
{"SHADOW_SCALE", "display"}, {"SHADOW_RANGE", "display"}, {"CONTACT_SHADOWS", "display"},
@@ -245,7 +245,7 @@ static const CfgMeta metas[] = {
// Co-op (coop.c): off until the player turns it on. The server is host[:port] of a relay (tools/coop_relay.py), and has no
// default until coop_app.h names one; the name is at most 24 bytes, empty for Player-xxxx; the port is the local UDP port
// (0: any). COOP_ROOM, COOP_AUTO, COOP_DROP and COOP_TEST_SET are read with cfg_get, no row.
{"COOP", "0", "0|1", LIVE}, {"COOP_NAME", "", NULL, LIVE},
{"COOP", "0", "0|1", LIVE}, {"COOP_BETA", "0", "0|1", LIVE}, {"COOP_NAME", "", NULL, LIVE},
{"COOP_SERVER", NX2NAVI_COOP_SERVER_DEFAULT, NULL, LIVE}, {"COOP_PORT", "0", "0..65535", RESTART},
// The rules a room made by this player syncs by (coop_state.h, docs/coop.md); a joiner's own are not used in the room.
{"COOP_PROGRESS", "shared", "shared|own", LIVE}, {"COOP_ITEMS", "shared", "shared|own", LIVE},

View File

@@ -1536,6 +1536,10 @@ static void tab_coop(float scale) {
static const UiTextId kTabNames[TAB_COUNT] = {UI_MENU_TAB_SCREEN, UI_MENU_TAB_GAME, UI_MENU_TAB_SOUND,
UI_MENU_TAB_CONTROLS, UI_MENU_TAB_COOP};
static int g_tab; // the tab shown
// The tabs the menu shows: the Co-op tab is a beta, shown only with coop_beta = 1 in nx2navi.ini (NX2NAVI_COOP_BETA),
// which no menu row sets. Co-op itself is untouched by it: coop = 1 still runs a session (docs/coop.md).
static int tabs_shown(void) { return cfg_bool("NX2NAVI_COOP_BETA") ? TAB_COUNT : TAB_COOP; }
static int g_tab_request = -1; // select this tab at the next layout; -1 none
static int g_focus_page = -1; // this tab's first row takes the cursor once it is shown; -1 none
@@ -1626,14 +1630,16 @@ static void layout_menu(uint32_t w, uint32_t h, float scale) {
: ImGui::IsKeyPressed(ImGuiKey_GamepadR1, false) || ImGui::IsKeyPressed(ImGuiKey_E, false) ? 1
: ImGui::IsKeyPressed(ImGuiKey_GamepadL1, false) || ImGui::IsKeyPressed(ImGuiKey_Q, false) ? -1
: 0;
if (step) g_tab_request = g_tab_sounded = (g_tab + step + TAB_COUNT) % TAB_COUNT;
const int ntabs = tabs_shown();
if (g_tab >= ntabs) g_tab_request = 0; // the Co-op tab was shown and coop_beta went off
if (step) g_tab_request = g_tab_sounded = (g_tab + step + ntabs) % ntabs;
if (step) menu_sound_play(step > 0 ? MENU_SOUND_TAB_RIGHT : MENU_SOUND_TAB_LEFT);
if (g_tab_request >= 0) g_focus_page = g_tab_request; // the new page's first row takes the cursor
const ImGuiStyle &st = ImGui::GetStyle();
float bottom = ImGui::GetFrameHeight() + st.ItemSpacing.y * 2;
if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_NoTabListScrollingButtons)) {
for (int k = 0; k < TAB_COUNT; k++) {
for (int k = 0; k < ntabs; k++) {
char label[128];
snprintf(label, sizeof label, " %s ###tab%d", menu_text(kTabNames[k]), k); // the id stays per language
// the tab labels in the title face, on the rows' button (ui/menu_skin.cpp)
@@ -1979,7 +1985,7 @@ static int overlay_frame(uint64_t frame, const MenuTarget *t, const char **shot)
const char *dir = cfg_get("NX2NAVI_MENU_TEST_DIR");
snprintf(path, sizeof path, "%s/menu-test-%llu-%d.png", dir ? dir : ".", (unsigned long long)frame, g_test.page);
*shot = path;
if (++g_test.page == (g_debug.load() ? DEBUG_MENU_TABS : TAB_COUNT)) {
if (++g_test.page == (g_debug.load() ? DEBUG_MENU_TABS : tabs_shown())) {
g_test.page = -1;
g_test.opened = 0, g_test.next_at++; // the next NX2NAVI_MENU_TEST frame opens it again
set_open(0); // the next frame lays out nothing