feat(chunkloader): Módulo nativo de Chunk Loader com limites LuckPerms e BlueMap #4
Reference in New Issue
Block a user
Delete Branch "feat/chunk-loader-module"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🚀 Módulo Nativo de Chunk Loader para Paper 1.21.x
O que foi implementado:
ChunkAnchorItem.java):RESPAWN_ANCHOR) com lore, PersistentDataContainer e receita de crafting (D O D / O E O / D O D).ChunkLoaders.javaeChunkLoader.java):World.addPluginChunkTicket/removePluginChunkTicket).plugins/Canalhandia/chunks.yml.canalhandia.chunkloader.limite.<N>).ChunkLoaderListener.java):BlockExplodeEvent,EntityExplodeEvent) e movimento por pistões.CanalhandiaCommand.java):/chunkloader info— Informações da chunk atual e uso de limites./chunkloader listar— Lista âncoras ativas do jogador com coordenadas./chunkloader remover <id>— Desativa e devolve a âncora ao inventário./chunkloader receita— Mostra a receita de crafting./chunkloader admin— Comandos administrativos para listar/remover/entregar âncoras.BlueMapBridge.java):ChunkLoaderTest.java(346/346 testes passando).🤖 AI Review · pragent pilot · glm-5.2:cloud ·
e71babcaTier:
full(≈1000 changed lines, new Chunk Loader module touching permissions, chunk ticketing, and YAML persistence). 9 files read; no linters run (no mvn/javac/Bukkit deps on PATH). Reviewed security/tests/perf lenses inline — no subagents. The change is well-structured and unit-tested for the model layer, but has a high-severity lifecycle bug (chunk tickets are never released on plugin disable or module toggle-off, leaving orphaned forced-load tickets across restarts), a BlueMap world-matching substring false positive, and an item-loss/dup edge in/chunkloader remover. 1 high, 3 medium, 2 low.🔋 AI usage
glm-5.2:cloud· engine: opencode · agent steps: 18 · duration: 225.6ssrc/main/java/dev/marcospaulo/canalhandia/Canalhandia.java:190src/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:568src/main/java/dev/marcospaulo/canalhandia/BlueMapBridge.java:151src/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:1932src/main/java/dev/marcospaulo/canalhandia/ChunkLoaders.java:1340src/main/java/dev/marcospaulo/canalhandia/ChunkLoaders.java:12512 inline comment(s) posted below.
src/main/java/dev/marcospaulo/canalhandia/Canalhandia.java:190— onDisable() never calls chunkLoaders.unloadAllTickets() or flush(), so plugin chunk tickets stay registered with the server after shutdown; on next enable the world may already have orphaned tickets and chunks.yml is only re-applied, never reconciled, risking duplicate/stale forced loads. — fix: In onDisable(), call chunkLoaders.unloadAllTickets() then chunkLoaders.flush() (and unregister the recipe) while the server is still alive. (https://jd.papermc.io/paper/1.21/org/bukkit/World.html#removePluginChunkTicket(int,int,org.bukkit.plugin.Plugin))src/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:568— Toggling the CHUNKLOADER module off via /canalhandia modulo chunkloader off only flips the config flag; it neither calls unloadAllTickets() nor unregisterRecipe(), so already-placed anchors keep their chunks force-loaded and the recipe stays craftable while the module is supposedly disabled. — fix: When module == CHUNKLOADER and on == false, call plugin.chunkLoaders().unloadAllTickets() and ChunkAnchorItem.unregisterRecipe(plugin); when turning on, call loadAllTickets() and registerRecipe().src/main/java/dev/marcospaulo/canalhandia/ChunkLoaders.java:1340— save() is fire-and-forget on a single-thread executor; a crash between an add/remove and the async write loses the latest change, and the executor is never shut down (no shutdown in onDisable), leaking a non-daemon-friendly queue on /reload. — fix: Expose flush() in onDisable (covered by the high finding) and consider io.submit(...).get() for the last save on disable, or call io.shutdown() in a dispose method.src/main/java/dev/marcospaulo/canalhandia/ChunkLoaders.java:1251— playerLimit() reads LuckPerms limits via Player#getEffectivePermissions, which only reflects permissions already calculated for the player; a freshly-set limite.N permission applied through a context the player hasn't recalculated can be missed, so the limit silently falls back to the default. — fix: Prefer the LuckPerms API (LuckPermsProvider.get().getUserManager().getUser(uuid).getCachedData().getPermissionData(...).permissionMap) for authoritative numeric limits, or document that a rejoin is required after a limit change.@@ -106,0 +148,4 @@String mapId = map.getId().toLowerCase(java.util.Locale.ROOT);String w = world.toLowerCase(java.util.Locale.ROOT);if (w.contains("nether")) return mapId.contains("nether");if (w.contains("the_end") || w.contains("end")) return mapId.contains("end");[MEDIUM] mapMatchesWorld treats any world whose name contains the substring "end" (e.g. "legend", "friend", "send_world") as the End map, so anchors in such custom worlds are drawn on the End map and missing from the overworld map.
Fix: Match the End by exact/equality on the full world name (or its canonical Bukkit dimension key) instead of a bare contains("end") substring test.
🪙 ~2373 tok (13% · attributed output)
@@ -1786,0 +1929,4 @@} catch (Exception ignored) {}if (sender instanceof Player player) {[MEDIUM] In chunkloaderRemove, when an admin runs the command from console the anchor block is set to AIR but no item is given (sender is not a Player), so the custom Anchor item is silently destroyed; for a Player caller, addItem() drops overflow on the ground instead of preserving it when the inventory is full.
Fix: Always drop the Anchor item at the block location (like onBlockBreak does) instead of only addItem to the sender, so console admin removal and full inventories don't lose the item.
🪙 ~3881 tok (21% · attributed output)
✅ Review #84 Resolved in commit
e70df32Canalhandia.onDisable()now unloads all chunk tickets (chunkLoaders.unloadAllTickets()), closes/flushes the executor (chunkLoaders.close()), and unregisters the anchor recipe./canalhandia modulo chunkloader <on|off>dynamically registers/unregisters recipes and tickets.mapMatchesWorlduses strict suffix/equality tests (_the_end,the_end,_nether,nether) preventing substring collisions on custom worlds.chunkloaderRemovedrops overflow naturally at player position or drops at block location for console admin removal.All 346 tests passing.
🤖 AI Review · pragent pilot · glm-5.2:cloud ·
e70df329Adds a native Paper 1.21 chunk-loader module: a RESPAWN_ANCHOR-based anchor item with crafting recipe, ChunkLoaders manager with Paper plugin chunk tickets + chunks.yml persistence, LuckPerms-based per-player limits, break/explosion/piston protection, /chunkloader commands with tab-completion, and BlueMap markers. This revision fixes the prior review's lifecycle, module-toggle, and BlueMap world-matching findings. Remaining risk is low: one authz inconsistency where the dedicated chunkloader admin permission doesn't unlock the dedicated admin subcommands, plus two minor item/info-loss edges.
🔋 AI usage
glm-5.2:cloud· engine: opencode · agent steps: 15 · duration: 114.3ssrc/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:1972src/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:2036src/main/java/dev/marcospaulo/canalhandia/CanalhandiaCommand.java:18363 inline comment(s) posted below.
@@ -1786,0 +1833,4 @@private void chunkloaderList(CommandSender sender, String[] args) {if (!(sender instanceof Player player)) {chunkloaderAdminList(sender, args.length > 0 ? args[0] : null);[LOW] chunkloaderList() redirects any non-Player sender (console, command block, third-party plugin sender) to chunkloaderAdminList() with no admin check, exposing every player's anchor coordinates — i.e. their base/farm locations — to any non-player sender holding only the default-true canalhandia.chunkloader permission.
Fix: Require admin (canalhandia.admin or canalhandia.chunkloader.admin) before delegating a non-player sender to chunkloaderAdminList(), mirroring the gate in chunkloaderAdmin().
🪙 ~4136 tok (36% · attributed output)
@@ -1786,0 +1969,4 @@}private void chunkloaderAdmin(CommandSender sender, String[] args) {if (!admin(sender)) {[MEDIUM] chunkloaderAdmin() and the
dar/giveroute gate on admin() which only checks canalhandia.admin, so a player granted canalhandia.chunkloader.admin (advertised in plugin.yml, /chunkloader help, and tab-completion) cannot use /chunkloader admin listar|remover|dar, yet the same permission IS honored by onBlockBreak and chunkloaderRemove to break/remove others' anchors — the admin permission is advertised but silently denied for the dedicated admin commands.Fix: Accept canalhandia.chunkloader.admin alongside canalhandia.admin in chunkloaderAdmin() and the
dar/givebranch (mirror the isOwner/isAdmin checks already used in chunkloaderRemove).🪙 ~4079 tok (36% · attributed output)
@@ -1786,0 +2033,4 @@}}target.getInventory().addItem(ChunkAnchorItem.create(plugin, amount));[LOW] chunkloaderGive() discards the Map returned by addItem(ChunkAnchorItem.create(plugin, amount)); when the target's inventory is full the overflow anchors are silently lost while the command still reports "Entregue N x" — an item-loss bug, and the success message is false.
Fix: Drop any overflow at the target's location (as chunkloaderRemove already does) so the anchors are never lost, or cap the reported amount to what actually fit.
📎 ref: https://jd.papermc.io/paper/1.21/org/bukkit/inventory/PlayerInventory.html#addItem(org.bukkit.inventory.ItemStack...)
🪙 ~3231 tok (28% · attributed output)
Pull request closed