launcher: interrupted apply recovers cd-active.new (completion marker) or cd-active.old, config.json merged field by field on save across windows, wine version check with warning below 5.0, mirror worlds as check boxes (new multi option type, old values still load), copy progress in mb in the wizard, saves table fits 1280x720
This commit is contained in:
1 parent
526f6072f6
commit
a6708e5fff
28 files changed
+843
-112
No files matched your search
@@ -82,10 +82,9 @@ Test World with all mods; boot-to-level + all exe mods (River loading card); pra
|
||||
NoClassDefFoundError.
|
||||
* **OneDrive / redirected Documents** (decided: no warning) as data folder: hard links inside a synced folder and
|
||||
on-demand files are fragile; the wizard could warn when the chosen folder is under `%OneDrive%`.
|
||||
* **Interrupted apply** (app killed between the two renames of `buildActive`): `cd-active` is
|
||||
missing until the next apply; the GUI then restores the original (dropping the mods), CLI
|
||||
`--launch` says "CD folder missing". `cd-active.new` is complete at that point and could be moved
|
||||
in instead.
|
||||
* A `config.json` saved by a stale window after another instance applied still overwrites
|
||||
activeMods/gameExeSha256 (L3 only covers the check); a merge-on-save would be needed.
|
||||
* Old Wine versions are not detected (no version check); missing Wine gives a clear message.
|
||||
* ~~**Interrupted apply**~~ – fixed 2026-11-07: `cd-active.new.complete` marker +
|
||||
`ModManager.recoverInterruptedApply()` (GUI start, Play, `--launch`), see llamamod-app.md.
|
||||
* ~~A `config.json` saved by a stale window overwrites activeMods/gameExeSha256~~ – fixed
|
||||
2026-11-07: field-level merge-on-save under `.config.lock` (`Config.save`).
|
||||
* ~~Old Wine versions are not detected~~ – 2026-11-07: `wine --version` in Settings, warning below
|
||||
Wine 5.0 (Settings in red, once before Play); missing Wine gives a clear message.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
+24
-3
@@ -56,6 +56,7 @@ different folder chosen in the wizard is remembered via `java.util.prefs`.
|
||||
| Path | Content |
|
||||
|---|---|
|
||||
| `config.json` | settings, enabled/active mods, mod option values (`modOptions`), CD fingerprint |
|
||||
| `.config.lock`, `.apply.lock` | file locks: config merge-on-save, apply/restore |
|
||||
| `original/hd`, `original/cd` | pristine copy of `Setup/<lang>/Hd` and `…/Cd` from the ISO |
|
||||
| `game/` | copy of the HD part the game runs from: **patched** `groove.exe`, DLLs, music, save games |
|
||||
| `cd-active/` | what the game sees as its CD: hard links to originals + mod outputs |
|
||||
@@ -66,6 +67,13 @@ different folder chosen in the wizard is remembered via `java.util.prefs`.
|
||||
| `wine/` | own Wine prefix (Linux) |
|
||||
| `logs/` | game.log, wineboot.log, registry.log |
|
||||
|
||||
**Config merge-on-save**: `Config.save` takes a file lock on `.config.lock` (plus a JVM-wide
|
||||
monitor), re-reads config.json and three-way merges field by field against the values this
|
||||
instance last loaded/saved: fields it did not change keep what another window/CLI/editor wrote
|
||||
meanwhile (mod options merge per mod and key; unknown keys of newer versions are kept); a field
|
||||
changed on both sides = last writer wins. The object then holds the merged values. A `new
|
||||
Config()` that never touched a file overwrites everything (installer, tests).
|
||||
|
||||
## First start
|
||||
|
||||
The wizard takes an ISO file, an extracted folder or a mounted CD. ISO 9660 is read in pure Java
|
||||
@@ -125,7 +133,10 @@ Windows the launcher refuses other paths with a message (choose a folder like `C
|
||||
Wine cannot create win32 prefixes; system.reg only appears after `wineserver -w`).
|
||||
`WINEDLLOVERRIDES=winemenubuilder.exe=d;mscoree=d;mshtml=d` (no menu entries, no Mono/Gecko
|
||||
prompts). Optional virtual desktop: `wine explorer /desktop=LlamaMod,WxH Z:\…\groove.exe`.
|
||||
Wine missing → clear error message. Running instances are detected via `/proc/*/environ`
|
||||
Wine missing → clear error message. `wine --version` (cached per command) is shown in Settings;
|
||||
below `GameLauncher.MIN_WINE` = 5.0 the line turns red and Play warns once per session ("Play
|
||||
anyway"); unparsable output never warns or blocks. Only Wine 6.0.3 is actually tested; 5.0 is a
|
||||
guess at the oldest usable version. Running instances are detected via `/proc/*/environ`
|
||||
(`WINEPREFIX=<our prefix>`), so two games never share the prefix.
|
||||
|
||||
## Mods
|
||||
@@ -140,7 +151,13 @@ SHA-256(CD fingerprint (names and sizes) | app version+build id | `ExePatcher.RE
|
||||
skips the mod, and an app update re-runs every mod once (a changed mod without a `version()` bump
|
||||
never serves stale output). Manifests store `"app"`; after each apply, cache entries of other app
|
||||
builds (including all of 0.1.0, which have no `"app"`) are deleted. Then `cd-active` is rebuilt in
|
||||
`cd-active.new` and swapped in (hard link → symlink (not Windows) → copy). Applying (and restoring)
|
||||
`cd-active.new` and swapped in (hard link → symlink (not Windows) → copy). Once `cd-active.new` is
|
||||
complete, `cd-active.new.complete` (content: the activeKey of that build) is written; it is removed
|
||||
after the swap. If the app is killed between the two renames, `ModManager.recoverInterruptedApply()`
|
||||
(GUI start, Play, CLI `--launch`) moves `cd-active.new` in when its marker matches config.json's
|
||||
activeKey (exe and config are only written after the swap, so they still describe the previous
|
||||
selection), otherwise moves `cd-active.old` back; without either nothing is touched (Play then
|
||||
restores the original as before). Applying (and restoring)
|
||||
is refused while the game runs (`GameLauncher.isRunning()`) and holds an exclusive lock on
|
||||
`<data>/.apply.lock` (a second window/CLI/editor gets "Another LlamaMod window or command is applying
|
||||
mods"); every run is logged to `logs/apply.log`.
|
||||
@@ -163,7 +180,11 @@ above, collapsible headers with "n of m on") and sorts them by name inside a gro
|
||||
**Options** (`mods/ModOption`, record `(key, label, description, type, defaultValue, min, max,
|
||||
choices)`): factories `ModOption.bool(key, label, desc, def)`, `integer(key, label, desc, def, min,
|
||||
max)`, `string(…, def)`, `choice(key, label, desc, def, ModOption.of(value, label)…)`, `seed(key,
|
||||
label, desc, def)` (empty default = a random seed is chosen on first use and saved). In `apply`:
|
||||
label, desc, def)` (empty default = a random seed is chosen on first use and saved), `multi(key, label,
|
||||
desc, def, ModOption.of(value, label)…)` (any subset; value `all`, `none` or the chosen values
|
||||
comma-separated in choice order, e.g. `1,3`; empty reads as `all`, so a former STRING option holding
|
||||
"all"/"1,3" keeps its value and cache key; `o.selected(value)` → list of chosen values; UI: a grid of
|
||||
check boxes; first user: mirror `worlds`). In `apply`:
|
||||
`ctx.option(key)` (normalized string), `ctx.optionBool`, `ctx.optionInt`, `ctx.optionSeed` (long:
|
||||
numeric text as is, other text hashed). Values live in config.json (`modOptions.<id>.<key>`, only
|
||||
user-set values and generated seeds); invalid stored values fall back to the default. The UI shows
|
||||
|
||||
@@ -75,9 +75,9 @@ Severity: **H** = blocks or seriously misleads; **M** = friction every session;
|
||||
| 22 | L | Mods intro | Fixed 640 px wrap; long technical sentence. | **Fixed**: "Tick the mods you want, then click Apply and Play…" |
|
||||
| 23 | L | Wizard done | "Choose mods on the Mods tab, edit save games…". | **Fixed**: "Next: tick the mods you like on the Mods tab, click Apply, then Play. The Save games tab can unlock worlds." |
|
||||
| 24 | L | Launcher edition | No help/manual at all (the manual window lives in the editor plugin and needs an editor context). | Open – a Help button would need `ManualWindow` without `EditorContext` |
|
||||
| 25 | L | Mirror mode | *Worlds* is free text ("all" / "1,3"); a list of checkboxes would be friendlier. | Open – now explained inline; changing the option type would change saved configs |
|
||||
| 26 | L | Wizard | The copy page shows the file name, not "123 of 568 MB" or a time. | Open (copy takes seconds from an ISO) |
|
||||
| 27 | L | Save games | The chapter table shows 4–5 rows at 720 px height. | Partly – the lower header gives it 46 px more |
|
||||
| 25 | L | Mirror mode | *Worlds* is free text ("all" / "1,3"); a list of checkboxes would be friendlier. | **Fixed** 2026-11-07: check boxes per world (new `ModOption.Type.MULTI`; stored "all"/"1,3" still load) |
|
||||
| 26 | L | Wizard | The copy page shows the file name, not "123 of 568 MB" or a time. | **Fixed** 2026-11-07: "Copying 123 of 568 MB (file)" |
|
||||
| 27 | L | Save games | The chapter table shows 4–5 rows at 720 px height. | **Fixed** 2026-11-07: lives moved into the world-map section (one header less), narrower number/check columns, centered coins – 6 rows at 1280×720 and 840×720 (was 4.5) |
|
||||
|
||||
## Changes (files)
|
||||
|
||||
|
||||
Reference in new issue
Block a user