Commit Graph
771 Commits
Author SHA1 Message Date
Intelli 1490dc7c89 Reduce ClickHouse consumer payload retention 2026-10-04 11:05:16 -06:00
Intelli af78ea00d4 Added a:sign support for rollbacks/restores 2026-10-02 17:29:11 -06:00
Intelli 4c8038cd26 Fixed BatchUpdateException for 'meta' column on outdated MySQL schemas 2026-10-02 15:50:02 -06:00
tonyjamesstarkandClaude 29d22d5a58 Fixed console purges with a radius purging all worlds (#1026)
/co purge rejects a numeric radius, but the check only saw the radius
when the sender had a location. From the console, /co purge t:30d r:50
parsed no radius and no world, so it ran as a server-wide purge. The
radius is now also parsed against a placeholder location, so it is
rejected for every sender. r:#world is unaffected.


Claude-Session: https://claude.ai/code/session_01PdEuKbjcVoVinVQJq4te1f

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-02 13:58:53 -06:00
tonyjamesstarkandClaude 4e70d63978 Fixed SQLite purges with world and block filters purging other tables (#1025)
On SQLite, /co purge r:#world i:<block> built the retain condition for
world-scoped tables without checking the block restriction, so it purged
co_container, co_chat and the other world-scoped tables in that world
although a block restriction should leave them untouched. The
entity_container/entity_interaction branch and the MySQL path already
check it.


Claude-Session: https://claude.ai/code/session_01PdEuKbjcVoVinVQJq4te1f

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-02 13:58:17 -06:00
CloudMindeandIntelli 00e7b3819b Add #sign block-group (#1024)
* Add #sign block-group

* Fix #sign group: use singular name, version-aware detection, remove TRACK_ANY

---------

Co-authored-by: Intelli <6790859+Intelli@users.noreply.github.com>
2026-10-02 13:55:11 -06:00
Sage Kummer 079b97405b Stop threads and subscribers that outlive a plugin disable (#1020)
* Stop threads and subscribers that outlive a plugin disable

The WorldEdit subscriber, the cache sweeper thread, per-click inspector threads and the bStats scheduler all kept running after CoreProtect was disabled, and the Folia shutdown checkpoint could wait 30 seconds on tasks that never run. Shutdown now unregisters and stops each of them, inspector lookups use a small named pool, and the checkpoint waits 5 seconds.

* Recreate inspector executor on enable and account for shutdown leftovers

The inspector executor was a static final that stayed shut down after a disable, so a same-instance enable could never run another lookup. It is now created on each enable. At shutdown, lookups still queued after the drain window release their throttle slot, since shutdownNow() returns them without running their finally block, and running lookups that survive the interrupt are logged by thread and player after a bounded wait. The Folia checkpoint timeout change is reverted to keep this change to thread cleanup.
2026-10-02 13:34:34 -06:00
Sage Kummer 48c9bf3c0f Apply Folia inventory rollbacks on each player's scheduler (#1018)
* Apply Folia inventory rollbacks on each player's scheduler

Inventory rollbacks run in the region that owns chunk 0,0 and changed every player's inventory from there, which Folia does not allow. On Folia the rows are now grouped per player and applied on that player's entity scheduler, the batch waits for those tasks, and the rollback counters are updated atomically. Paper and Spigot are unchanged.

* Run Folia inventory tasks as mutations of the rollback context

The per-player inventory tasks now go through the rollback's entity context. A task checks for cancellation before it changes anything, runs as an active mutation so cleanup waits for it to finish, and adds its item count to that context instead of the shared counter for the sender, so a late task cannot change a rollback that already ended or a newer one from the same sender. The counter helpers go back to their upstream form since player tasks no longer write to them.
2026-10-02 13:34:22 -06:00
Sage Kummer 804e398eb1 Read bisected halves through the WorldEdit edit, not the live world (#1016)
* Read bisected halves through the WorldEdit edit, not the live world

When an edit removes a door or tall plant, the other half was read from the live world on the edit thread, which does not own the chunk and can see blocks the edit already changed. The half is now read through the edit extent, or the FAWE processor's chunk arrays, and carried on WorldEditBlockState. The type list Queue checked inline moves to BlockGroup.LOGGED_BY_LOWER_HALF so both sides use the same one.

* Capture the other bisected half before the WorldEdit change applies

CoreProtect's extent sits at BEFORE_CHANGE, below WorldEdit's batching, so its reads reach the live world, and both the lower half and the removed partner were read after setBlock(). Read the other half before setBlock() instead, and only use it when it is the same material with the opposite half, so an unrelated block next to a removed half is no longer logged from block data alone. A door or tall flower is already logged by its lower half, so its partner no longer gets a second, duplicate row. The FAWE lower half is validated the same way.
2026-10-02 13:34:11 -06:00
Sage Kummer 18643de5a1 Make /co reload swap world configs and leave block groups alone (#1015)
* Make /co reload swap world configs and leave block groups alone

The per-world config map was a plain HashMap that getConfig wrote into on a miss, and reload cleared it before refilling, so a listener hitting that window could leave a world on the global config until the next reload. The map is now concurrent and read-only for listeners, and reload swaps new configs in without a gap. Reload also stops rebuilding the version adapters and block groups, which only depend on the server version, while listeners read them.

* Rebuild adapters and block groups on /co reload again

Several block groups are filled from datapack tags (buttons, pressure
plates, doors, saplings, flowers, carpets, logs and more), so they are
not fixed by the server version. Running the adapters and
BlockGroup.initialize() only at startup would leave those groups stale
after a datapack reload until the server restarts. Restore the previous
behaviour and keep this change to the world config map.
2026-10-02 13:34:00 -06:00
Sage Kummer 2863de7f30 Fix races on the pending item transaction lists (#1013)
* Fix races on the pending item transaction lists

Item listeners appended to the shared per-player lists with get, add and put while the consumer read and then cleared them, so items could be logged twice or not at all, and the anvil path replaced pending items outright. Listeners now append inside ConcurrentHashMap.compute and the consumer takes each list with remove. The prepared copy is kept for every database type so a retried relational batch still writes its rows.

* Close discard and clone-failure gaps in pending item transactions

Listeners now append their item and register its generation in one
Queue.addPendingItems call that holds the Queue lock, the same lock
ItemTransactionProcess.discard takes, so discard can no longer clear an
item whose transaction has not been queued yet. ItemLogger.prepare now
only detaches the pending lists; cloning and merging move into
PreparedTransaction.log, which runs after the prepared transaction is
stored for the batch, so a cloning failure leaves the detached items
available to the retried transaction.
2026-10-02 13:32:56 -06:00
Sage Kummer 80caab5938 Restore paintings and item frames on Folia without teleporting (#1011)
* Restore paintings and item frames on Folia without teleporting

Rollback spawns a painting or item frame and then teleports it into place, but Folia always throws from Entity#teleport, so paintings are lost and frames never get their recorded facing or item. On Folia the entity is now spawned directly at its recorded block with its facing, art or item set in the pre-spawn consumer. Paper and Spigot are unchanged.

* Spawn Folia hangings through the adapter on 1.19.4 and 1.20.1

Compiling against the current API binds World#spawn(Location, Class,
Consumer) to the java.util.function.Consumer overload, which only exists
from Bukkit 1.20.2. Folia 1.19.4 and 1.20.1 only have the
org.bukkit.util.Consumer overload, so restoring a painting or item frame
there threw NoSuchMethodError. The spawn now goes through
BukkitAdapter.spawn, which calls the java.util.function.Consumer overload
when World has it and otherwise invokes the org.bukkit.util.Consumer
overload by reflection.
2026-10-02 13:32:46 -06:00
Sage Kummer 4274f88d0a Log armor stand breaks from the retired callback on Folia (#1010)
* Log armor stand breaks from the retired callback on Folia

When an armor stand dies from non-player damage, the break and its equipment are logged from a task on the stand's scheduler. On Folia the stand is removed in the same tick, so the task is retired instead of run and nothing is logged. The same runnable is now passed as the retired callback, so exactly one of them logs the break.

* Log armor stand breaks from EntityDeathEvent instead of isDead

The damage listeners now only capture the attacker and the stand's
contents, and EntityDeathEvent logs the break when the stand actually
dies. The scheduled task and Folia's retired callback just discard a
capture that was never used, so a stand that survived the hit and was
unloaded or otherwise removed before the callback ran no longer
produces break and container rows, and the retired callback never
touches the world.
2026-10-02 13:32:30 -06:00
Sage Kummer 2f50109bdd Materialize the MySQL sign text filter as a derived table (#1008)
* Materialize the MySQL sign text filter as a derived table

MySQL cannot semi-join the rowid IN (UNION ...) subquery the sign text filter builds, so it runs it as a dependent subquery, re-probing all eight branches for every candidate row and never using the line_N prefix indexes. Wrapping the union in a derived table lets MySQL materialize it once through those indexes. Other databases build the same query as before.

* Inline the MySQL sign text filter so restricted lookups stay indexed

The derived table materialized every sign row matching the text across the whole table before the location, time and user restrictions were applied, so a common prefix at one sign location scanned the full table. MySQL now gets the same inline line predicates DuckDB already uses: restricted lookups filter the rows found through the wid, time or user index, and unrestricted lookups still reach the line prefix indexes through an index merge.

* Split the sign filter comment into two sentences
2026-10-02 13:32:05 -06:00
Intelli 6e9c8302fa Name DuckDB workers to avoid Spark profile misattribution 2026-10-02 13:17:27 -06:00
Intelli 7582ab4d8e Added logging for cushions (fixes #1023) 2026-09-28 22:27:54 -06:00
Intelli ac94648185 Fixed copper golem transaction logging on newer Paper versions (fixes #1033) 2026-09-28 18:52:32 -06:00
Intelli 6a27fbfaba Fixed ClickHouse 26.9+ initialization failure caused by server response compression (fixes #1032) 2026-09-28 18:29:18 -06:00
Intelli d5cad31d6c Optimize ClickHouse player lookups with a metadata index (fixes #1031) 2026-09-27 00:18:18 -06:00
Intelli e518b2ae02 Optimized ClickHouse lookups with safe PREWHERE filters (fixes #1031) 2026-09-25 20:51:18 -06:00
Intelli 0d96edbf2b Changed required ClickHouse version to v26.1 2026-09-25 15:40:28 -06:00
Sage Kummer bdc53a1250 Key the explosion scan by packed block position (#1021)
The explosion scan cloned a Location for every block and built five more per block to find attached neighbours, hashing each one on the region thread. It now keys the map by a packed long position and walks the neighbours from an offset table. The same blocks are logged.
2026-09-25 15:12:42 -06:00
Sage Kummer 35f64f09a4 Close lookup ResultSets on exception paths (#1019)
Lookup.countLookupRows and LookupRaw.performLookupRaw closed their ResultSet as the last statement of the try, so any error while reading rows left it, and the statement behind it, open until the connection was returned. Both now close it on every path.
2026-09-25 15:11:38 -06:00
Sage Kummer fc5a12c0b7 Key populated chunks by world and drop them after 240 seconds (#1017)
populatedChunks had no world in its key, so a chunk generated in one world suppressed water, lava and vine rows at the same chunk coordinates in every other world, and entries were only removed when a later fluid row happened to land there. It is now kept per world and swept after 240 seconds, the longest window BlockPlaceLogger checks.
2026-09-25 15:10:55 -06:00
Sage Kummer 335fec2036 Skip a null tamed owner name when restoring an entity (#1012)
A tamed animal whose owner has no known name is stored with a null owner, and restoring it throws a NullPointerException at the owner step, which drops the rest of the restore. A null owner name is now skipped like an empty one.
2026-09-25 15:10:27 -06:00
Sage Kummer 72d5655ee9 Skip matchMaterial's regex for enum names and cache blockdata encodings (#1022)
The consumer ran Material.matchMaterial's two regex passes on names they cannot change, and re-encoded the same blockdata strings into ids on every row. Plain enum names now go straight to getMaterial, and stringToByteData caches its results per string, bounded, reset when the id maps reload and skipped on ClickHouse.
2026-09-25 15:08:11 -06:00
Sage Kummer 97fced4cb6 Allocate world, material, blockdata, art and entity ids under one lock (#1009)
The id allocators read the counter, add one and store it without a lock, so two threads that see new names at the same time (region threads on Folia, lookup and API threads everywhere) can give the same id to two different names. Allocation now holds the ConfigHandler monitor reloadAndGetId already uses, with a re-check, and queues the insert after releasing it.
2026-09-25 14:56:30 -06:00
Sage Kummer 96eec2b768 Skip block entity snapshots on the dispenser logging path (#1007)
Every dispense copied the dispenser's block entity twice on the region thread (block.getState() in the dispense listeners and inventory.getHolder() in onInventoryInteract), and both copies were only used for their type, location and live inventory. Paper can hand out non-snapshot states for this, so the dispense path now asks for those through a new PaperInterface.getBlockState(Block, boolean), falling back to a normal snapshot on Spigot.
2026-09-25 14:12:34 -06:00
Intelli bba6f9f522 Improved DuckDB startup exception 2026-09-25 13:22:08 -06:00
Intelli 3314d993a6 Bump API documentation to v13 2026-09-24 15:57:17 -06:00
Intelli 68739a2596 Target Paper 26.3 2026-09-23 22:37:10 -06:00
Intelli 4e5ddd2334 Improve rollback diagnostics and abort failed database initialization 2026-09-23 20:12:06 -06:00
Intelli bbe5917ec5 Fix entity container rollbacks aborting on missing items or full inventories 2026-09-23 17:42:44 -06:00
Intelli 78fbf12d9e CoreProtect Community Edition v24.1 2026-09-23 14:37:50 -06:00
Intelli 56ba90b58b Align ClickHouse lookup pagination with DuckDB 2026-09-23 13:59:32 -06:00
Intelli 91f08590d0 Align ClickHouse entity inspection pruning with DuckDB 2026-09-23 13:39:58 -06:00
Intelli 3af1079b51 v13 API documentation improvements 2026-09-21 13:15:49 -06:00
Intelli 37be5ebf15 Updated v13 API documentation 2026-09-21 12:59:07 -06:00
Mykhailo Alipa 5db696103a feat(api): add message prefix filters to typed lookups (#1001)
* feat(api): add message prefix filters to typed lookups

* refactor(api): reuse existing message filter helper directly

* chore: remove test file from message filter PR

* feat(api): make message prefix exclusions explicit

* refactor(api): name text prefix filters by their predicates
2026-09-21 12:35:23 -06:00
Intelli 2f719054eb Fixed BatchUpdateException / sign data truncation for MySQL databases 2026-09-19 12:52:18 -06:00
Intelli 46b56d577a Fix DuckDB entity interaction failure recovery 2026-09-19 12:49:46 -06:00
DarkChroma 0ec76561dc Prevent DuckDB entity interaction queue stalls (#1002) 2026-09-19 12:37:10 -06:00
Intelli 9cf12ee5bf Prevent DuckDB entity UUID conflicts with active readers 2026-09-18 14:14:40 -06:00
Intelli 5318bd2031 Fix duplicate right-click inspections triggering database busy messages (#1000) 2026-09-18 13:33:49 -06:00
Intelli 94ecdb3427 Prevent duplicate UUID inserts during delayed entity spawn logging 2026-09-17 17:14:27 -06:00
Intelli d664ece2a8 Fixed UUID cache gap 2026-09-17 14:07:47 -06:00
Intelli 11b4795bf3 Updated documentation 2026-09-16 15:36:25 -06:00
Mykhailo Alipa 45da5eee0c feat(api): extend typed block lookups and add entity lookup (#995)
* feat(api): add material filters to typed block lookup

* feat(api): support scoped typed block lookups

* refactor(api): simplify block material predicates

* feat(api): add typed entity lookup and filters

* fix(api): separate block history and optimize entity scopes

* fix(api): cast ClickHouse entity tracking IDs safely
2026-09-15 13:13:52 -06:00
Intelli 645bdb52d8 config.yml cleanup 2026-09-14 19:17:47 -06:00
Thoria 0ac4761a3a Add dispensers config option to disable dispenser logging (fixes #727) (#989)
* Added dispensers config option to disable dispenser logging (fixes #727)

* Only suppress dispenser-caused bone meal and ignition

* Only track bone meal applied by a dispenser

* Identify dispenser fireball ignition by its shooter

* Clarify the dispensers config header
2026-09-14 19:14:57 -06:00