Purpur Inventory Helper

A Purpur 26.2 plugin with Sorting Sticks, automatic broken-tool replacement, alternative Dispenser/Cobweb recipes, Powder Snow crafting, and risky over-level enchantment combining in ordinary anvils.

The approved behavior is specified in the SoMC OKF wiki.

The repository, checkout and new JARs use purpur-inventory-helper. Runtime SpigotInventoryHelper, Java entrypoint/packages, data namespace and sorting-stick/recipe keys remain unchanged. Replace the old JAR instead of loading both distributions. Old tags and spigot-inventory-helper-* assets are retained; the previous v1.2.0 download was checked for identical bytes after the rename.

Requirements

  • Purpur 26.2 build 2618
  • Java 25 or newer
  • ProtocolLib 5.4.0 (required runtime library; missing ProtocolLib prevents plugin loading)

Build

./gradlew clean check jar

The plugin JAR is written to build/libs/.

check includes nativeInventoryTest. It downloads the exact Purpur 26.2 build 2618 launcher, verifies SHA-256 4a32d046a118804d89ca74ba89b798c98f6d8d1f310c18077ac573597049de31, and runs Paperclip in patch-only mode under build/inventory-runtime/; it does not start a server, open a port, create a world, or accept the EULA. The native suite executes real ServerPlaceRecipe, ServerRecipeBook known/highlight codec persistence, and ResultSlot ingredient consumption for both Sorting Stick variants and the alternative Dispenser recipe, including repeated batch completion. The narrowly scoped anvil-generation adapter also uses this checksum-pinned native runtime as a compile-only dependency. Server/API and ProtocolLib classes are not bundled. CraftServer, CraftPlayer, and client boundaries are doubled; byte I/O is a controlled port. Anvil tests execute actual ProtocolLib forwarding, one registered local Netty pipeline/event loop, the declared library scheduler with a queued main-task port, and the real native packet queue, including final cancellation/replacement, skipped handling, owner disable and late-forward retirement. This is not evidence of live client rendering, search interaction, socket/installed-stack packet transport, or a full server lifecycle. Those remain non-blocking live checks.

Sorting Stick recipe

Place these ingredients vertically in the crafting grid's center column:

Chest or Barrel
Redstone Dust
Stick

Either storage-block variant produces one Sorting Stick. Both variants are universally discovered in the recipe book's Equipment category for online and joining players. Discovery is persistent and idempotent; reloading the plugin retains player recipe-book knowledge and ordinary vanilla state. If the two recipes cannot register as one complete group, recipe-book discovery is disabled while existing manual crafting and inventory behavior remain available.

Alternative Dispenser recipe

One ordinary Dropper + Tripwire Hook produces one ordinary Dispenser through a shapeless recipe in the recipe book's Redstone category. The alternative is universally discovered for online and joining players, supports native autofill and batch completion, and leaves Minecraft's Bow-based Dispenser recipe unchanged. If the alternative cannot register safely, existing Inventory Helper behavior remains available and discovery retries after a later successful registration.

Powder Snow recipe

One ordinary Snow Block + empty Bucket produces one ordinary Powder Snow Bucket through a shapeless recipe in both the personal 2×2 and crafting-table 3×3 grids. All players discover it in the recipe book's MISC category after successful registration. Native autofill and ordinary or shift/batch crafting consume one Snow Block and one Bucket per output, with no additional empty Bucket remainder. The filled Bucket is unstackable; native output-space and cancellation rules remain authoritative. Placement, recovery and Powder Snow hazards are unchanged.

The recipe owns only spigotinventoryhelper:snow_powder_snow and its discovery listener. Conflicts/failures withhold its discovery without disabling other features; discovery can retry on later joins and successful re-enable. Disable removes owned registration/listeners, not native recipe-book knowledge or unrelated recipes. Native automated tests cover both grids, actual registered events/packet completion, metadata/quantity conservation and failure boundaries; real-client visuals and live placement/recovery remain unperformed non-blocking follow-ups.

Sorting inventories

Hold an authentic Sorting Stick and right-click:

  • A chest or either half of a double chest to sort its complete inventory.
  • A barrel to sort its inventory.
  • Air or another block to sort player inventory slots 9–35.

Player hotbar, armor, and off-hand slots remain unchanged during player sorting. Compatible stacks are consolidated first, then items are ordered alphabetically by Minecraft material. Dissimilar metadata variants of the same material retain their relative order. Locked containers and interactions cancelled by protection plugins are not sorted.

Automatic tool replacement

When a main-hand tool breaks, an item with the exact same material is moved into the broken tool's selected hotbar slot on the next server tick. Main-storage candidates are preferred before tools in other hotbar slots. The complete replacement item is moved unchanged, preserving durability, enchantments, custom names, and other metadata. Off-hand breaks are ignored, and no unrelated item is moved when a match is unavailable.

Reinforcing anvils

Craft one Anvil, Chipped Anvil or Damaged Anvil with one Netherite Block, shapeless in either crafting grid. The universally discovered MISC book entry is searchable as Anvil Reinforcement. The live preview/private messages teach both effective risk tables; book previews never guarantee success or mint usable protection.

Next reinforcement level Default failure
1 20%
2 40%
3 60%
4 80%
5 99%

Each accepted single attempt pays one source anvil and one block. Success returns the next level; failure destroys both without output or a crafting explosion. Damage, names/lore and unrelated metadata are preserved. Bulk/shift, unavailable destinations and sixth-level attempts are refused before payment or a roll. The five-step default survival is intentionally 0.0384%, approximately one in2,604 chains.

A placed anvil's reinforcement lowers risk excess to max(0, actual resulting enchantment excess − reinforcement level) before applying the combine curve/cap below. Output eligibility/levels do not change. Actual over-limit combining remains advanced at0%: zero XP, one accepted roll/outcome, and all reinforcement spent once. Success resets protection without repairing the anvil; failure destroys committed items and the owned used anvil with one existing explosion, subject to final native protections. Cancellations never force block removal or refund already-committed inputs. Ordinary combining/repair/naming preserves protection and native costs/refusals/wear.

Native item, loaded-chunk and falling-entity PDC carry ownership; supported placement, break/drop, wear, falling, destruction and reload handling do not spawn duplicate anvils or inherit a replaced owner's charge. Unknown/refused persistence remains withheld rather than authorizing protection. No forced chunk loads or plugin tick-thread disk writes. Tests use native codecs/events/menus with explicit world/loaded-context ports, not a live disk-restart/crash-atomic transaction or arbitrary event-bypassing API-mutation guarantee.

Advanced anvil combining

All players can combine two same-material, non-book items in an ordinary anvil. Enhanced combining applies only when the combined enchantments exceed a normal maximum: equal levels become one higher; unequal levels keep the higher. Compatible applicable enchantments carry over. Enhanced conflicts, inapplicable enchantments and results the server cannot serialize exactly are refused rather than silently discarded.

Within-limit combinations (for example Sharpness I + I → II or IV + IV → V) stay entirely vanilla, including normal XP/prior-work costs, naming, repair and refusals. Removing advanced inputs and inserting normal items switches back immediately; no closing/reopening or confirmation is required. Old advanced clicks cannot take a newly prepared normal result.

The result preserves the first/left item's exact name, lore and unrelated metadata, with combined enchantments and normal durability repair. Rename text is ignored for enhanced merges. Book operations, rename-only, repair-only and unrelated operations remain vanilla.

Enhanced merges charge no XP and have no "Too Expensive" restriction. The private action bar displays the result and exact success/failure chances; private chat explains that taking the result commits the attempt. Each accepted attempt consumes one item from each input. Success delivers one result; failure destroys both consumed items and the owned used anvil, subject to native protection, and causes an explosion that can hurt players and damage blocks. Native protections remain authoritative, but a protected/refused explosion does not refund the inputs.

Default risk uses the largest resulting enchantment excess above its normal maximum:

Excess Failure chance
0 Native combining; no enhanced roll
1 20%
2 25%
3 35%
4 50%
5 70%
6 95%
7 or more 99%

ProtocolLib binds taking to the displayed menu generation before payment. A bounded 40-server-tick admission accommodates the main → Netty → native-main handoff; an owned native dispatch shim carries exact packet identity through that queue, so one click cannot borrow another click's cancellation or payment ticket. Native aggregate and packet-specific limits remain authoritative, using the actual connection's counters and violation actions; expired/refused/cancelled dispatches never pay. Transport ownership is bounded to 32 records per connection. A queued ProtocolLib-owned main callback followed by a barrier on the same native channel event loop proves whether each exact raw was forwarded; mutable packet/post-event snapshots do not. This library-owned cleanup survives this plugin's task cancellation. Revoked native wrappers retain their denial even when native admission or queue processing skips their handler, while genuinely unresolved late raws keep protective ownership until arrival, the ordered fence or disconnect. Missing/rejected library scheduling or event-loop execution fails closed; it does not establish successful cleanup. Input or acknowledged risk changes invalidate old clicks even when the result item is identical; the ordinary anvil stays open without another confirmation. The adapter is pinned to Purpur 26.2 build 2618 and fails closed when enhanced generation admission is unavailable. This is a library dependency, not a Fantasy Stick/gameplay-plugin bridge.

Use left/right pickup, shift-click or a native-eligible empty hotbar/offhand destination. Full destinations, cancelled/stale takes and unsupported cloning/drop/collection actions do not roll or spend items. Ordinary anvil wear remains in effect. Dedicated gameplay-plugin compatibility is unsupported; declared native cancellations and unrelated inventory state are preserved.

Administrators with spigotinventoryhelper.admin (default OP) and authorized console can read/set durable balance values:

/inventoryhelperadmin config <key>
/inventoryhelperadmin config <key> <value>
Key in plugins/SpigotInventoryHelper/config.yml Default
advanced-anvil-base-failure-percent 20
advanced-anvil-risk-step-percent 5
advanced-anvil-max-failure-percent 99
advanced-anvil-reinforcement-failure-1-percent 20
advanced-anvil-reinforcement-failure-2-percent 40
advanced-anvil-reinforcement-failure-3-percent 60
advanced-anvil-reinforcement-failure-4-percent 80
advanced-anvil-reinforcement-failure-5-percent 99

All reinforcement percentages are integers0–100 and the complete five-value table must be nondecreasing. The max5/one-excess-tier reduction/all-level spend rules are fixed. At each durable acknowledgment, only that requested key publishes after current permission/plugin/player lifetime checks; saved neighboring keys are not publication authority. Failed saves keep prior live values; read/completion shows effective settings. A0% reinforcement attempt always succeeds,100% always fails.

Values are integers 0–100. For positive risk excess X, failure is min(cap, base + step × X × (X − 1) / 2); X = max(0, E − R) for actual excessE and reinforcementR. Actual E0 stays native and never performs an enhanced roll; actual E>0 with X0 remains advanced, rolls at0% and spends all protection. The default cap leaves 1% success, but an explicit cap100 can permit guaranteed destruction. Saves occur off-thread and acknowledge only after durable replacement; failed saves keep effective values unchanged. Acknowledged changes invalidate existing previews, and restart loads saved values. Autocomplete is permission-aware and side-effect free. A missing file uses packaged defaults without a startup write; malformed settings withhold enhanced gameplay, leaving existing features available.

Native automated tests cover menus, registered events/commands and ProtocolLib listeners, real declared-library forwarding/local Netty pipeline/native queue dispatch, item serialization, result-taking, native packet limits, stale client generations and protected explosion boundaries. Pipeline installation on a live server, real-client transport/rendering and installed-stack behavior are not established by these controlled fixtures. Actual client visuals and live destructive testing remain separate follow-ups, not deployment evidence.

Releases

Gitea Actions checks pushes and pull requests and stores a development JAR. Pull requests validate conventional commits. Main-branch conventional commits drive semantic releases when the repository defines a RELEASE_TOKEN with contents-write permission.

S
Description
Purpur plugin for inventory sorting and automatic tool replacement
Readme
665 KiB
0 Stars 1 Watchers 0 Forks
v2.7.0
Latest
2026-10-17 04:03:03 +00:00
Languages
Java 100%