Tags
Creators
Details
2.1.0
Compatibility
Changes
ClickSorted 2.1.0 makes sorting and preference changes fully listener-actionable for third-party plugins, adds a configurable update-check cadence, and closes a Folia edge case around shared virtual inventories.
Highlights
InventorySortEventis now listener-actionable — narrow a sort withexcludeSlot/excludeItem, inspect precise slot classification (OUT_OF_RANGE/ADMIN_LOCKED/USER_LOCKED/SORTABLE), and explain a cancellation withsetCancelReason, shown to the player rate-limited.- New
PlayerPreferenceChangeEvent— every per-player preference change (click/sort method, start corner, fill axis,enabled, sort-over-items, bundle-packing settings, locked-slot toggles, bundle blacklist add/remove/clear) now fires a cancellable, typed event before it's applied or persisted. - New
PreferenceResult— every event-firingPlayerSortingPrefssetter now returnsAPPLIED/UNCHANGED/CANCELLEDinstead of a bareboolean/void, so callers can tell a real no-op apart from a listener veto. - Configurable update-check cadence —
check_for_updates_interval_hours(default 24) controls how often the recurring Modrinth check repeats while the server runs. - Folia safety — on Folia, ClickSorted now refuses to sort a plugin-created inventory that is open to more than one viewer at once (e.g. a shared virtual GUI), since such an inventory has no single owning region thread and could otherwise race between viewers' region threads. Vanilla inventories and single-viewer plugin GUIs are unaffected.
Breaking Changes
These changes are scoped to third-party plugin developers integrating with ClickSorted's Java API — there is no impact on server admins or players.
PlayerSortingPrefssetter return types changed. Methods likesetEnabled,setClickMethod,setSortingMethod,setStartCorner,setFillAxis,setSortOverItems,setBundlePackInInventory,setBundlePackInContainers,setBundleStackLimit,addToBundleBlacklist/removeFromBundleBlacklist(and the name variants),clearBundleBlacklist, andtoggleSlotLockednow returnPreferenceResultinstead ofboolean/void. Third-party plugins calling these directly and using the return value must update to checkPreferenceResult.applied()/cancelled().setLockedSlotsis unchanged (stillvoid, fires no event).InventorySortEvent(InventoryView, Inventory, int min, int max)is deprecated (not removed) in favor of the new set-based constructor, which carries lock and admin-blacklist metadata the old one can't express. Existing code using the old constructor keeps compiling and working.
New Features
InventorySortEvent: listener-actionable
The event now carries the click's full region, per-player and admin lock sets, and the admin item-blacklist snapshot, and exposes:
getSortableSlots()/excludeSlot(int)— read and narrow the live sortable set.getRegionSlots(),getUserLockedSlots(),getAdminLockedSlots()— the raw inputs behind the sortable set.statusOf(int)/getSlots()— classify any or every slot asOUT_OF_RANGE,ADMIN_LOCKED,USER_LOCKED, orSORTABLE.excludeItem(Material)/excludeItem(String)/getExcludedMaterials()/getExcludedItemNames()/matchesExcludedItem(ItemStack)— listener-contributed item exclusions, unioned with the admin blacklist.setCancelReason(Component)/getCancelReason()— an optional explanation shown to the player (rate-limited, since this event fires on every matching click) when a listener cancels the sort.
See the new Development → Events docs page for full details and an example listener.
PlayerPreferenceChangeEvent
A new cancellable event fired by PlayerSortingPrefs before any per-player preference changes. getChange() returns a sealed Change<T> (ValueChange<T> or LockedSlotChange, which adds a slot()) with typed oldValue()/newValue(); getChange(Preference<T>) gives a type-narrowed accessor for one specific preference (Preference.CLICK_MODE, .SORT_MODE, .ENABLED, etc. — see the new Preference<T> class). A cancelling listener may call setCancelReason(Component); ClickSorted's own commands show that reason, falling back to a generic preferenceChangeBlocked lang key when none is set. Only fires when the new value actually differs from the old one.
See the new Development → Events docs page for full details and an example listener.
PreferenceResult
PlayerSortingPrefs mutators now return a PreferenceResult record (APPLIED, UNCHANGED, or CANCELLED with an optional cancelReason()), letting ClickSorted's commands (and any third-party caller) distinguish a value that was already set from one a listener blocked, and report each correctly to the player.
Configurable update-check cadence
check_for_updates_interval_hours (default 24, minimum 1, lower values clamped up) controls how often UpdateChecker repeats its Modrinth check while the server keeps running, in addition to the existing startup/reload check. /clicksorted admin reload now restarts the full update-check schedule (previously it only fired a one-off check).
Development documentation
New docs/development/ section covering building & testing from source and the events ClickSorted publishes (InventorySortEvent, PlayerPreferenceChangeEvent), for plugin authors integrating with ClickSorted.
Bug Fixes
- Bundle-blacklist add/remove/clear couldn't distinguish "already in that state" from "a listener blocked it" — these now report a listener veto as blocked rather than silently falling through to the "already present"/"not present" message.
Other Improvements
ClickSortedCommandsdeduplicated:blockedAndReported/reportBlacklistResulthelpers own the cancel-report half of every preference-setting command once, instead of per call site.PlayerSortingPrefsdeduplicated: enum/boolean setters delegate to sharedsetEnumPref/setBoolPrefhelpers, and the set-valued blacklist mutators tomutateBlacklist, which own the fire→write→return sequence once; set-valued stores re-read PDC after the event fires so re-entrant listener mutations aren't clobbered.- A no-op preference setter call that matches the config default still pins the value in PDC (without firing the event), so an explicit choice survives a later default change.
ProtectedItems.blocks(ItemStack, String)overload added so callers that already resolved an item's display name (e.g.InventorySortEvent.matchesExcludedItem) avoid a second lookup.
Compatibility
✔️ Paper/Folia 1.21.5 – 26.1.x ✔️ Java 21+
Upgrading
- Stop your server.
- Replace the old jar in
plugins/with this release. - Start your server.
check_for_updates_interval_hoursis added toconfig.ymlautomatically (default24); no other config or stored-preference migration is required.
Note for plugin developers: if you call
PlayerSortingPrefssetters directly and use their return value, update to the newPreferenceResultreturn type (see Breaking Changes above).
What's Changed
- Add
InventorySortEventslot classification, item exclusions, and cancel reasons by @kccricket - Add configurable update-check cadence and cancellable preference events by @kccricket
- Introduce
PreferenceResultto distinguish no-op, applied, and cancelled preference changes by @kccricket - Fix: distinguish cancelled bundle-blacklist changes from no-ops; dedupe/cache event helpers by @kccricket
- Refactor: pin explicit preference no-ops to PDC and dedupe command/event helpers by @kccricket
- Refuse to sort cross-region-unsafe shared plugin inventories on Folia by @kccricket
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:8qsfB0IP:Iiuqvy0S"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:8qsfB0IP:Iiuqvy0S"
}

