Skip to content

Roll back and restore virtual containers through plugin handlers (part 2 of #1040) - #1047

Open
dsudomoin wants to merge 13 commits into
PlayPro:masterfrom
dsudomoin:virtual-containers-rollback
Open

dsudomoin wants to merge 13 commits into
PlayPro:masterfrom
dsudomoin:virtual-containers-rollback

Conversation

@dsudomoin

Copy link
Copy Markdown

Part 2 of #1040. Builds on #1046: only the last five commits are new here. I'll rebase once #1046 is merged.

Summary

The plugin that owns a virtual container registers a handler for its type. /co rollback and /co restore with container: hand the changes to that handler instead of touching an inventory themselves.

api.registerVirtualContainer(plugin, new NamespacedKey(plugin, "backpack"), (id, changes) -> {
    int applied = 0;
    for (VirtualContainerChange change : changes) {
        if (!backpacks.apply(id, change)) { // add or remove change.getItem(), all or nothing
            break;
        }
        applied++;
    }
    return applied;
});
/co rollback container:<type>/<id> t:1h
  Rollback.performRollbackRestore
    item rows with tbl=5 are taken out before CONTAINER_ROLLBACK_UPDATE and BLOCK_INVENTORY
    VirtualContainerRollbackHandler.prepare    group per container, newest first (restore: oldest first), claim
    VirtualContainerRollbackHandler.process    server thread, batched per tick, inside beginMutation/endMutation
      handler.apply(id, changes) -> n          the plugin applies the changes in order and returns how many it applied
      Process 39                               marks exactly those n rows rolled back or restored
  • API (v13, no version bump): registerVirtualContainer(Plugin, NamespacedKey, VirtualContainerHandler) and VirtualContainerHandler#apply(String id, List<VirtualContainerChange> changes). VirtualContainerChange has getItem(), isAddition() (the direction is already resolved for rollback vs restore) and getSlot() (the slot recorded by Add virtual containers: log and look up plugin-owned inventories (part 1 of #1040) #1046, or -1).
  • Partial application: only the leading changes the handler applied are marked, so running the command again retries the rest. If a handler throws or returns an out-of-range count, nothing in that container is marked. The failure is logged under the plugin's name and the rest of the operation continues.
  • No handler: if a type has no handler or its plugin is disabled, its changes are skipped. The console gets a count per type and the sender one line ("Skipped N virtual container changes").
  • Cancellation and conflicts: uses the rollback context requested on Apply Folia inventory rollbacks on each player's scheduler #1018 (beginMutation/endMutation, pending futures, item counts). If two operations hit the same container at once, the second is cancelled, as with entity tracking rows.
  • Command rules: always global; rejected together with r:, a world, #worldedit, #container or #preview. A single container (container:<type>/<id>) can be rolled back without u:; a whole type still needs u:, like any global a:container rollback.
  • Storage: RollbackUpdateTargets.VIRTUAL_CONTAINER, Process 39 and the ClickHouse rollback family for virtual_container. No schema change.
  • Docs: docs/commands.md and docs/api/version/v13.md (handler contract and an example).

Testing

  • Manually tested on a server, with a plugin handler for ender chests opened by command. All of the following work as expected:
    • rollback and restore of one container without u:; running the same command again changes nothing;
    • a whole type without u: is rejected, and works with u:;
    • a container the handler can't change right now (its owner is offline) is left as is and reported as skipped;
    • regular block and container rollbacks behave as before.
  • JUnit: 57 tests on this branch, PR 1's included. The new ones cover:
    • order and direction of changes for rollback and restore;
    • the applied prefix marking exactly those rows on SQLite, with a re-run finishing the rest;
    • claim conflicts;
    • throwing and out-of-range handlers, missing and disabled handlers;
    • slots read back from metadata;
    • the rollback update touching only co_virtual_container;
    • container: accepted and rejected by rollback and restore.
  • Not run against a live server: MySQL, ClickHouse and Folia.

Merge risk

Additive on top of #1046. Existing rollbacks change in one place only: item rows of virtual containers, which only container: selects, are taken out before the update queues.

A handler that breaks the contract (applies part of a change, then throws) can duplicate or lose items in its own container; the docs spell the contract out. As with regular container rollbacks, a second rollback of the same container started before the consumer commits the first one's marks would apply again.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 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 ⚠️ Failed 2026-10-28T18:01:32.640959Z
We couldn’t complete this review. Comment @codex review to try again.
8035917 PR opened
ℹ️ 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.

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