Skip to content

The current world is read by its owner, which the World page and the tree show - #115

Merged
Pelotrio merged 3 commits into
claude/systems-filesfrom
claude/systems-world
Oct 1, 2026
Merged

Pelotrio merged 3 commits into
claude/systems-filesfrom
claude/systems-world

Conversation

@Pelotrio

@Pelotrio Pelotrio commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Fourth PR of docs/SYSTEMS.md, stacked on #114: the current world is read by one owner, which the World page and the Project tree show.

Finish line

Behavior Test
The World page opened through navigation reads once; navigating to it again reads nothing PageReadsTest.theWorldPageReadsOnceWhenOpenedAndAgainOnlyAfterTheGameSavedTheWorld
The game saving the world while the page is hidden reads nothing then, and once when the page is shown again; shown again without a change, nothing the same test
The owner reads the world the game plays again when the game saves it WorldReadingTest.theWorldTheGamePlaysIsReadAgainWhenTheGameSavesIt
The owner follows the world the game plays, reads it as open, and the world before tells nothing WorldReadingTest.theWorldTheGamePlaysIsFollowedAndTheOneBeforeTellsNothing
A new world a game Companion is not connected to opens becomes current WorldReadingTest.aNewWorldAGameCompanionIsNotConnectedToOpensIsFollowed
Deleting the current world makes the one played before current WorldReadingTest.theWorldPlayedBeforeIsFollowedWhenTheCurrentOneIsDeleted
A world the game does not play can be moved or deleted while the page shows it; on Windows a watch of its datapacks would hold it WorldReadingTest.aWorldTheGameDoesNotPlayCanBeMovedWhileItIsShown
A world the connected game left can be moved WorldReadingTest.aWorldTheConnectedGameLeftCanBeMoved
A world a game Companion is not connected to held can be moved once it left it WorldReadingTest.aWorldAGameCompanionIsNotConnectedToHoldsCanBeMovedOnceItLeftIt
A new icon or datapack of the world the game plays is a change WorldReadingTest.aNewIconOrDatapackOfTheWorldTheGamePlaysIsAChange
Without a world, the page is told why WorldReadingTest.withoutAWorldThePageIsToldWhy
A follower whose names fail does not stop the watch of others FileReadingTest.aFollowerThatFailsDoesNotStopTheWatchOfOthers

What changes

  • WorldReading owns the instance's current world: the one the game has open, otherwise the one played last. Which world that is, it decides on its strand again when the game connects, plays another world or leaves one, when a world of saves is created, removed or changed, and after Companion changed the datapacks. It reads the current world's folder as a FileReading (level.dat, level.dat_old, session.lock, datapacks and icon.png).
  • The world's own folder and its datapacks are watched only while the connected game plays it. On Windows a folder cannot be deleted or renamed while a folder inside it is watched, which would break the game's Delete World. The game tells it left a world only once it let go of it. A game Companion is not connected to has its worlds read when Companion looks again, never watched. The triggers are registered before the first look, so none is missed in between.
  • The World page follows the owner and the datapacks the game names, as a page of the loader: it reads when first shown and after a change, not whenever it is shown.
  • The Project tree's World rows show what the owner read; they no longer read level.dat themselves.
  • WorldReadings, through which the page told the tree what it read, goes.
  • FileReading can follow a whole folder, some of whose entries make up the value, and can stop watching while it is read only when asked; docs/SYSTEMS.md records both.
  • FileWatch: a follower whose name filter fails no longer stops the one watcher thread.

Left as it is

  • The World tab's title follows what the page last showed, so a hidden World tab names the world it showed until it is shown again. The pages PR redraws it from the owner with loader.updates, which docs/SYSTEMS.md describes for titles.
  • A game Companion is not connected to that opens an existing world, or saves one, is seen only once it connects or a world of saves is created or removed: its worlds are not watched, and Windows does not report a lock taken inside a world to the watch of saves. The game connects to Companion on its own.
  • A world the game does not hold, whose files another program overwrites in place, is read again only when something else makes Companion look at it: saves sees a world's folder created or removed, not a file changed inside it.
  • A level.dat that stays unreadable keeps the world read before; the page shows that until the file can be read again.

:companion:test passes (1641).

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T13:58:14.155108Z fed8e23 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6cfac508a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

public WorldReading(GameLocation location) {
this.location = Objects.requireNonNull(location, "location");
follow();
this.stopFollowingGame = location.addListener(change -> this.strand.execute(this::follow));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Re-evaluate the directory when an unconnected game changes worlds

When Companion remains open while an unconnected game moves from world A to world B, GameLocation emits no listener event because that state is derived from filesystem locks in read(), so this callback never invokes follow() for B. Events from A only reread A, leaving the World page and tree on the wrong world until a later connection event; the owner needs a filesystem-owned trigger that reruns current-directory selection.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment on lines +80 to +81
FileReading<CurrentWorld.Saved> reading = new FileReading<>(current, entry -> ENTRIES.contains(entry.toString()),
world -> CurrentWorld.read(this.location.read(), world), Duration.ofMillis(500));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Watch entries inside the datapacks directory

With an existing datapacks directory, FileWatch registers only the supplied world directory and Java watch events are nonrecursive, so creating or removing world/datapacks/Foo produces no matching event on this watch. Because CurrentWorld.read() derives new packs by listing that child directory, the published snapshot and both consumers remain stale until an unrelated level.dat or lock event occurs; the owner must also watch the child directory and its creation.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

*/
public final class WorldReading implements AutoCloseable {
/** The entries of a world's folder that make up what is read of it. */
private static final Set<String> ENTRIES = Set.of("level.dat", "level.dat_old", "session.lock", "datapacks");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include the world icon in change detection

WorldPanel still loads icon.png, but the new page loader runs again only after WorldReading.changed(), while this entry set excludes the icon and the Saved snapshot contains no icon state. If icon.png changes by itself, hiding and showing the page no longer refreshes it as the old readsWhenShown flow did, so the old icon remains until another watched world change happens; make the icon part of the owner's watched domain and equality.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Followed now = this.followed;
if (now.directory() == null) return new World(null, null, "No world has been played in this instance yet.");
try {
return new World(now.directory(), now.reading().value(), "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Publish a read failure instead of retaining the old world

After a world has been read successfully, if its level.dat becomes permanently unreadable or is deleted, FileReading retains its last successful value and fires no signal for the failure, so this call keeps returning the old Saved snapshot with an empty problem. Since the page now reads only after owner signals, it can display stale, editable world data indefinitely instead of reporting the failure on its next showing; publish the failure as part of WorldReading state while preserving transient-read retries.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment on lines +71 to +72
if (Objects.equals(current, before.directory())) {
if (before.reading() != null) before.reading().readNow();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Release the world watch after the game leaves it on Windows

On Windows, when a connected game leaves a singleplayer world for the menu, currentWorld() falls back to that same last-played directory, so this branch retains FileReading's native watch on the world. The repository's file-watching design notes that a watched directory cannot be renamed while a descendant watch handle is open, which means Minecraft's Delete World action or an external rename can fail for that closed world until Companion exits; release or relocate the native watch when the world is no longer open while preserving closed-world change detection through the owning system.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Pelotrio and others added 2 commits September 30, 2026 14:58
…tree show

docs/SYSTEMS.md, section 2.

- WorldReading owns the instance's current world: the one the game has open, otherwise the one played last. It reads
  the world's folder as a FileReading, again when the game saves the world, opens or leaves it, or its datapacks
  folder changes, and moves to another world when the game plays one.
- The World page follows it and the datapacks the game names, as a page of the loader: it reads when first shown and
  after a change, not whenever it is shown.
- The Project tree's World rows show what the owner read; they no longer read level.dat themselves.
- WorldReadings, through which the page told the tree what it read, goes.
- FileReading can follow a whole folder, some of whose entries make up the value.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…on and datapacks are followed

Which world is current is decided again when a world of saves changes, as when a
new world is created or the current one deleted, and after Companion changed the
datapacks. The world's own folder and its datapacks are watched only while the
game holds it, so the game's Delete World can remove a world Companion shows. A
new icon is a change. A follower whose name filter fails no longer stops the
watcher thread.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95d8100eac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +101 to +102
before.reading().watch(open);
watchDatapacks(open ? current : null, before.reading());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Re-evaluate watches when an unconnected game leaves a world

When Companion starts while an unconnected game holds a world and that game returns to its menu, GameLocation emits no event, so follow() is not called to recompute open. The fresh evidence in this revision is that the new reading.watch(open) and watchDatapacks(...) toggles execute only inside follow(); the world reader may notice the final level.dat save, but its callback only rereads the same directory. Both native watches can therefore remain registered after session.lock is released, and on Windows the game's subsequent Delete World can fail until another top-level saves event or Companion exits; have the owner re-evaluate the held state after its watched world changes.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

*/
public WorldReading(GameLocation location, GamePacks packs) {
this.location = Objects.requireNonNull(location, "location");
follow();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Register current-world triggers before the initial scan

If an unconnected game creates or opens a world while this owner is being constructed, the initial follow() can finish before the saves watch is registered. FileWatch does not replay events that happened before registration, and later writes to World/level.dat are below the nonrecursive saves watch, so the owner can continue reporting no world or the previous world for the whole session. Register the triggers before the initial scan, or rescan after registration, to close this lost-wakeup window.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment on lines +140 to +145
public void watch(boolean watch) {
this.strand.execute(() -> {
if (this.closed || this.watching == watch) return;
this.watching = watch;
if (watch) register();
else unregister();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Record the new watch-suspension behavior in the system design

watch(boolean) adds a new lifecycle to the shared FileReading system in which a registration is removed without closing its reading, but docs/SYSTEMS.md still states that registrations live until their readings close and describes moving the world reading instead. Because this changes a system-level file-watching invariant, leaving the recorded design unchanged will make subsequent migrations rely on the wrong lifecycle; update the systems decision with the suspension contract and rationale.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

On Windows a watch of a folder inside a world, as its datapacks, keeps the
world's folder from being deleted or moved. The connected game tells it left a
world only once it let go of it, so the watch always ends in time; a game
Companion is not connected to has its worlds read when Companion looks again.
The owner's triggers are registered before its first look. SYSTEMS.md records
readings that stop watching and the Windows rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Pelotrio
Pelotrio added this pull request to stack #121 October 1, 2026 11:43
@Pelotrio
Pelotrio merged commit 9f6328f into 1.21.1 Oct 1, 2026
2 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.

1 participant