Skip to content

Add virtual containers: log and look up plugin-owned inventories (part 1 of #1040) - #1046

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

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

Conversation

@dsudomoin

@dsudomoin dsudomoin commented Oct 28, 2026 •

Copy link
Copy Markdown

Part 1 of #1040: storage, logging and lookups. Rollback through a plugin handler will follow in a second PR.

Summary

Plugins can log changes to inventories that have no block or entity, such as /enderchest, team storage or backpacks, and find them with /co lookup container:<type>[/<id>].

api.logVirtualContainerTransaction("Notch", new NamespacedKey(plugin, "backpack"), backpackId, before, after);
logVirtualContainerTransaction        caller's thread: validate key, copy both arrays
  Queue -> Process 37                 consumer
    ContainerLogger.logVirtual        blacklists, diff before/after, CoreProtectPreLogEvent
      co_virtual_container_map        key -> id (cached like entity_map)
      co_virtual_container            one row per stack added or removed
/co lookup container:<type>[/<id>]    single-table query, always global
  • Storage: two additive tables on all four backends. virtual_container has the entity_container columns without wid/x/y/z, and container_id instead of entity_spawn_rowid. virtual_container_map(id, container) works like entity_map. Both come from the normal startup DDL, so there is no patch script and no schema version bump.
  • Key: namespace:type/id. The id is [a-z0-9._-]{1,64} (lowercase because of the MySQL collation), and the whole key is at most 191 characters (utf8mb4 index limit on older MySQL).
  • Slots: when before and after are index-aligned snapshots, each row records the slot where the change happened, so a stack taken from two slots gives two rows. The net diff is unchanged: moving a stack inside the container still logs nothing. The slot is for the rollback handler in part 2.
  • Lookups: only container: returns these rows. Radius, world, #global and inspector lookups never include them. It accepts u: t: i: e: a:container a:+container a:-container #count #summary. r:, #container, /co rollback and /co restore reject it.
  • Purge: time purges delete the rows; world-scoped purges skip them.
  • API v13, no version bump: logVirtualContainerTransaction and CoreProtectPreLogEvent.Action.VIRTUAL_CONTAINER_TRANSACTION (location null, message is the key). Documented in docs/api/version/v13.md and docs/commands.md.

ClickHouse. EVENT_PARTITION_KEY is built from PurgePolicy, so a new purgeable family changes the key and validateTable then refuses existing databases. This PR adds a fixed timePartitioned flag to ClickHouseFamily (the 11 families that are monthly-partitioned today) and builds both the key and eventPartitionId from it. The key stays byte-identical to master; virtual_container lives in partition 0 and is purged by mutation. This is the part I'd most like your view on (also asked in #1040). It's isolated in 994212d, so it's easy to change or drop.

Testing

  • Manually tested on a server, with a plugin logging ender chests opened by command. All of the following work as expected:
    • an existing database from an older version upgrades and starts normally;
    • a player's own chest and another player's (key is the owner, user is the actor), in survival and creative;
    • a block ender chest is still logged with coordinates and doesn't appear under container:;
    • u: and r: lookups don't show virtual rows;
    • /co rollback container:... is rejected;
    • block, chest and chest-minecart logging and rollback behave as before.
  • JUnit: 45 tests on this branch, kept out of the PR since src/test is ignored here. They cover:
    • key validation, container: parsing and parameter conflicts;
    • the logged diff and its slots (full snapshots, changed stacks only, moves, partial takes, one item in two slots);
    • DDL, inserts and the lookup SQL against embedded SQLite and DuckDB;
    • the key cache and purge rules;
    • the ClickHouse writer, plus a golden test that EVENT_PARTITION_KEY and eventPartitionId match master for every family.
  • Not run against a live server: MySQL and ClickHouse. Their DDL and SQL are only checked statically.

Merge risk

Additive. The PR adds two tables and changes no existing columns, indexes or patch scripts. Existing lookups, rollbacks and purges never read the new tables, and after a downgrade they simply sit unused. The ClickHouse partition key is unchanged.

@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 🔄 Running since 2026-10-28T16:33:34.424355Z cc45973 Manual request
ℹ️ 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.

@dsudomoin

Copy link
Copy Markdown
Author

@codex review

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