From 01e534e2785e59c0ec20b89a04783deabfcbd566 Mon Sep 17 00:00:00 2001 From: Voyager Date: Sun, 16 Aug 2026 20:57:54 -0400 Subject: [PATCH] feat: automate PortMaster save directory --- docs/DEVELOPMENT.md | 5 ++++- docs/PORTMASTER.md | 14 ++++++++++---- network/player_data_root.gd | 10 ++++++++++ scripts/build_portmaster.sh | 11 ++++++++++- scripts/portmaster/NETfishing.sh | 19 +++++++++++++++++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index d71ac14..c82c549 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -365,7 +365,10 @@ rm -rf -- "$test_root" ``` Do not point `NETFISHING_DATA_DIR` at an arbitrary empty directory; it is an -explicit portable-data override and must identify a valid data root. +explicit portable-data override and must identify a valid data root. The +PortMaster launcher is the narrow exception: it also sets +`NETFISHING_CREATE_DATA_DIR=1`, allowing the game to initialize its known empty +external save directory on first launch. Headless tests cannot prove visual alignment, mouse routing, shader appearance, controller feel, or resize behavior. Presentation changes require graphical diff --git a/docs/PORTMASTER.md b/docs/PORTMASTER.md index f83b252..fd933d6 100644 --- a/docs/PORTMASTER.md +++ b/docs/PORTMASTER.md @@ -38,8 +38,12 @@ directory. The templates in `scripts/portmaster/` are authoritative. mapping manager handle gameplay input. - The launcher calls `pm_platform_helper` for the game executable after starting GPTOKEYB and calls `pm_finish` after the game exits. -- Persistent device data remains under `netfishing/conf/data`, - `netfishing/conf/config`, and `netfishing/conf/cache`. +- New installations automatically create player data under + `ports/saves/netfishing/` and do not present the in-game folder picker. +- Existing installations with a selected data root keep that location when + upgraded. Device-local data, configuration, and cache remain under + `netfishing/conf/data`, `netfishing/conf/config`, and + `netfishing/conf/cache`. - The archive must not contain `conf/`, saves, identities, logs, source files, `.git`, or `.godot` content. - Release downloads must publish only the canonical `netfishing.zip` for this @@ -95,13 +99,15 @@ device directory and run: ``` Before upgrading an existing installation, preserve -`/mnt/mmc/ports/netfishing/conf/` on the device. After installation: +`/mnt/mmc/ports/netfishing/conf/` and `/mnt/mmc/ports/saves/netfishing/` on the +device. After installation: 1. Confirm the installed executable and PCK hashes match the staged release. 2. Confirm the installed launcher contains the canonical PortMaster header, starts GPTOKEYB with `netfishing.gptk`, and calls `pm_platform_helper` for the game executable. -3. Confirm `conf/` was not replaced or removed. +3. Confirm `conf/` and the external save directory were not replaced or + removed. 4. Launch the installed port through the normal muOS menu. 5. Verify the displayed game version and controller face-button mapping. 6. Verify PortMaster's force-quit chord exits the game. This is Start+Select on diff --git a/network/player_data_root.gd b/network/player_data_root.gd index 6eb5061..9e50fe7 100644 --- a/network/player_data_root.gd +++ b/network/player_data_root.gd @@ -12,6 +12,7 @@ const LAYOUT_VERSION := 1 const MANIFEST_FILENAME := "netfishing_data.json" const README_FILENAME := "README.txt" const ENVIRONMENT_VARIABLE := "NETFISHING_DATA_DIR" +const CREATE_ENVIRONMENT_VARIABLE := "NETFISHING_CREATE_DATA_DIR" const APPLICATION_ID := "netfishing" const APP_DATA_PORTABLE_PATH := "user://portable-data" const PATH_ALLOWED: StringName = &"allowed" @@ -48,6 +49,15 @@ func resolve() -> bool: var environment_path: String = OS.get_environment(ENVIRONMENT_VARIABLE) if not environment_path.is_absolute_path(): return _fail("NETFISHING_DATA_DIR must be an absolute path.") + if ( + OS.get_environment(CREATE_ENVIRONMENT_VARIABLE) == "1" + and not FileAccess.file_exists( + environment_path.path_join(MANIFEST_FILENAME) + ) + ): + var created: Dictionary = create_unbound_root(environment_path) + if not bool(created.get("ok", false)): + return false return _activate_existing(environment_path, "", false) var bootstrap: Dictionary = _read_json(BOOTSTRAP_PATH, 64 * 1024) if bootstrap.is_empty() and FileAccess.file_exists(BOOTSTRAP_PATH + ".backup"): diff --git a/scripts/build_portmaster.sh b/scripts/build_portmaster.sh index 3d7a585..3625584 100755 --- a/scripts/build_portmaster.sh +++ b/scripts/build_portmaster.sh @@ -181,7 +181,10 @@ Install the complete \`netfishing.zip\` archive with PortMaster or HarbourMaster The package requires an ARM64 device, two analog sticks, GLIBC 2.28 or newer, and the \`weston_pkg_0.2\` runtime. -Save data and device-local configuration remain under \`netfishing/conf/\`. +New installations store player data under \`ports/saves/netfishing/\` without +opening a folder picker. Existing installations preserve their previously +selected data root. Device-local configuration remains under +\`netfishing/conf/\`. PortMaster launches use the light performance profile by default. Put the word \`normal\` in \`netfishing/conf/performance_profile\` to opt a capable device into the normal rendering profile. See \`netfishing/licenses/\` for bundled @@ -223,6 +226,12 @@ grep -Fq 'NETFISHING_PERFORMANCE_PROFILE=light' \ "${STAGE_ROOT}/NETfishing.sh" grep -Fq 'CONTROLLER_MAPPING_FILE="$CONFDIR/cache/controller_mapping.txt"' \ "${STAGE_ROOT}/NETfishing.sh" +grep -Fq 'SAVEDIR="$PORTS_ROOT/saves/netfishing"' \ + "${STAGE_ROOT}/NETfishing.sh" +grep -Fq '"NETFISHING_DATA_DIR=$SAVEDIR"' \ + "${STAGE_ROOT}/NETfishing.sh" +grep -Fq '"NETFISHING_CREATE_DATA_DIR=1"' \ + "${STAGE_ROOT}/NETfishing.sh" grep -Fq 'printf '\''%s\n'\'' "$netfishing_controllerconfig" > "$CONTROLLER_MAPPING_FILE"' \ "${STAGE_ROOT}/NETfishing.sh" grep -Fq '"$GAME_LAUNCHER"' "${STAGE_ROOT}/NETfishing.sh" diff --git a/scripts/portmaster/NETfishing.sh b/scripts/portmaster/NETfishing.sh index a2574de..fe6d778 100755 --- a/scripts/portmaster/NETfishing.sh +++ b/scripts/portmaster/NETfishing.sh @@ -28,6 +28,9 @@ if [ ! -d "$GAMEDIR" ] && [ -d "/mnt/mmc/ports/netfishing" ]; then fi CONFDIR="$GAMEDIR/conf" +PORTS_ROOT="${GAMEDIR%/netfishing}" +SAVEDIR="$PORTS_ROOT/saves/netfishing" +DATA_BOOTSTRAP_DIR="$CONFDIR/data/godot/app_userdata/NETFISHING" GAME_EXECUTABLE="$GAMEDIR/NETfishing.aarch64" GAME_LAUNCHER="$GAMEDIR/launch-netfishing.sh" GPTOKEYB_CONFIG="$GAMEDIR/netfishing.gptk" @@ -42,6 +45,21 @@ fi mkdir -p "$CONFDIR/data" "$CONFDIR/config" "$CONFDIR/cache" "$WESTON_DIR" chmod +x "$GAME_EXECUTABLE" +# New PortMaster installs use a predictable save directory beside the port +# instead of presenting a folder picker on a small screen. Preserve an +# established data-root choice when upgrading an existing installation. +NETFISHING_DATA_ENVIRONMENT=() +if [ -f "$SAVEDIR/netfishing_data.json" ] || { + [ ! -f "$DATA_BOOTSTRAP_DIR/data_root_bootstrap.json" ] && + [ ! -f "$DATA_BOOTSTRAP_DIR/data_root_bootstrap.json.backup" ] +}; then + mkdir -p "$SAVEDIR" + NETFISHING_DATA_ENVIRONMENT+=( + "NETFISHING_DATA_DIR=$SAVEDIR" + "NETFISHING_CREATE_DATA_DIR=1" + ) +fi + > "$GAMEDIR/log.txt" && exec > >(tee "$GAMEDIR/log.txt") 2>&1 if [ ! -f "$controlfolder/libs/${WESTON_RUNTIME}.squashfs" ]; then @@ -134,6 +152,7 @@ $ESUDO env CRUSTY_RESOLUTION="${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \ XDG_CONFIG_HOME="$CONFDIR/config" \ XDG_CACHE_HOME="$CONFDIR/cache" \ GODOT_SILENCE_ROOT_WARNING=1 \ + "${NETFISHING_DATA_ENVIRONMENT[@]}" \ "${NETFISHING_GAME_ENVIRONMENT[@]}" \ "$GAME_LAUNCHER" \ "$CONTROLLER_MAPPING_FILE" \