
BackPackSSS
Custom tiered backpacks with progression crafting, upgrade system and persistent storage — designed for survival servers.
[1.9] - 2026-05-24
Fixed
-
🔴 Critical — Race condition: async save after connection close
saveSession()dispatches asynchronous write tasks. If one of those tasks was still in flight whensyncSaveAll()returned andDatabaseManager.closeConnection()was called (during shutdown or/bp reload), the task would attempt to write to an already-closed connection, silently losing data. Fixed by introducing avolatile boolean shuttingDownflag inBackpackManager:syncSaveAll()sets the flag before entering, which causessaveSession()to no-op for any new attempts.syncSaveAll()then spin-waits (up to 5 seconds) for any already-running async tasks to land before proceeding with its own synchronous pass, ensuring the connection is only closed after all writes have completed. -
🔴 Critical — Memory leak:
sessionsmap never evicted after closecloseSession()removed a backpack fromactiveBackpacksandplayerToBackpackbut never calledsessions.remove(). Every unique backpack UUID opened since the last restart accumulated indefinitely, causing unbounded memory growth on long-running servers. Fixed by addingsessions.remove(backpackUUID)in bothcloseSession()andclosePlayerSession(). -
🟡 Medium —
DatabaseManager.init()could fail silently on first runcreateNewFile()throwsIOExceptionwhen its parent directory does not exist. On a fresh install, ifsaveDefaultConfig()had not yet been called, the database file could not be created, leaving the connection null and causing NPEs on any subsequent load. Fixed by callingplugin.getDataFolder().mkdirs()before attempting to create the file, and added a return-early path whencreateNewFile()returnsfalse. -
🟡 Medium —
BackpackSession.playerUUIDwas alwaysnullgetBackpackSession()always passednullas the first constructor argument, makingBackpackSession.getPlayerUUID()permanently useless. Fixed by threading thePlayer's UUID fromopenBackpack()through togetBackpackSession(), which now acceptsUUID playerUUIDas its first parameter. -
🟡 Medium —
isForbiddenItem()usedgetMaxStackSize() == 1, which was too broad The heuristic blocked music discs, name tags, saddles, empty buckets, and any item from third-party plugins that happens to have a stack size of 1 for unrelated reasons. Replaced with an explicit category check usingMaterial.name()suffixes (armor, swords, axes, pickaxes, shovels, hoes) and individual constants for bows, crossbows, tridents, shields, elytras, shears, flint-and-steel, enchanted books, and totems of undying — exactly the items documented inconfig.yml. -
🟡 Medium —
onClick()marked session dirty before restriction checkssession.setDirty(true)was called at the top ofonClick(), before the nesting and item-filter checks that might cancel the event. A cancelled click does not modify the inventory, so marking it dirty caused unnecessary async saves. Fixed by movingsetDirty(true)to execute only after all checks pass. Same fix applied toonDrag(). -
🟡 Medium —
PlayerInteractEventfired for both hands, processing the backpack twice Without anEquipmentSlot.HANDguard, right-clicking with a backpack in the main hand generated twoPlayerInteractEventcalls per click. The opening cooldown absorbed the second call, but both calls enteredonInteract()and evaluated the PDC. Addedif (event.getHand() != EquipmentSlot.HAND) return;as an early exit. -
🟡 Medium —
InventorySerializerdid not close streams on exception BothtoBase64()andfromBase64()closed theirBukkitObject*Streamonly on the happy path. If serialization or deserialization threw mid-loop, the stream leaked. Replaced both methods with try-with-resources blocks. -
🟡 Medium —
PrepareItemCraftEventiterated player inventory unnecessarilycountBackpacks()was called on everyPrepareItemCraftEventtick even whenmax-backpackswas0(unlimited). Added an early-return guard so the inventory scan only runs when a real limit is configured. -
🟢 Minor —
BackpackCommandlacked explicit tier range validation A tier number outside[1, 3]was silently forwarded tocreateBackpack(), which returnednull, producing a generic "invalid tier" message through an indirect path. Added upfrontInteger.parseInt()+ range check againstTIER_MIN/TIER_MAXconstants, returning themessages.invalid-tierconfig message immediately.
-
[1.8] - 2026-04-21
Fixed
-
🔴 Critical —
onDropnever fired:getSessions()returns aMap<BackpackUUID, Session>but the old code checkedgetSessions().containsKey(playerUUID). Because the key is a backpack UUID, the check was always false — players could freely drop items while a backpack was open. Fixed by addingBackpackManager.isPlayerHasOpenBackpack(playerUUID)which queries the correctplayerToBackpackmap. -
🔴 Critical —
isBackpack()missed freshly crafted items:isBackpack()previously checked forBP_ID_KEY, which is only assigned on the first right-click of a backpack item. Brand-new crafted backpacks had no ID yet, so they were invisible to the pickup/max-backpack limit check. Fixed by checkingBP_TIER_KEYinstead, which is always present fromcreateBackpack(). -
🔴 Critical — Data loss race condition in
saveAll(): The oldsaveAll()dispatched async save tasks and then immediately calledsessions.clear(). The async lambdas could still be running when the map was cleared, leading to NPE or lost saves on server shutdown/reload. Replaced with a newsyncSaveAll()that blocks on the main thread during disable/reload, ensuring all data is written before the connection is closed. -
🟡 Task leak on
/bp reload:reloadPlugin()created a newBackpackManager(and calledstartAutoSaveTask()on it) without ever cancelling the previous manager's repeating task. Each reload stacked another permanent 2-minute timer. Fixed by callingcancelAutoSaveTask()before tearing down the old manager. -
🟡 Auto-save not restarted after reload:
reloadPlugin()built a newBackpackManagerbut forgot to callstartAutoSaveTask()on it — the replacement manager ran with no auto-save at all. Fixed. -
🟡 Hardcoded version string in startup log:
onEnable()printed"version 1.0"regardless of the actual version. Changed togetDescription().getVersion(). -
🟢 Config typo — "Week Backpack": Tier-1 display name was
"Week Backpack"(a day of the week) instead of"Weak Backpack". Fixed inconfig.yml.
Improved
-
BackpackCommandnow usesbackpack.adminpermission instead ofplayer.isOp(), allowing server admins to grant give/reload access via any permissions plugin (LuckPerms, etc.). -
/backpack give <tier> [player]: Admin can now give a backpack directly to another online player:/bp give 2 Steve. Tab-completion lists online player names. -
Recipe deduplication on reload:
registerRecipes()now removes old recipes before re-adding them, preventing "duplicate recipe" warnings in the console on each/bp reload. -
Safe inventory size mismatch handling: If a backpack's stored item count exceeds the configured
size(e.g. after an admin reduces the tier size), the plugin now logs a warning and truncates gracefully instead of throwing anArrayIndexOutOfBoundsException. -
plugin.yml: Addedbackpack.adminpermission entry withdefault: op. Updated description and usage string for thebackpackcommand.
-
[1.7] - 2025-03-28
Fixed
- Memory Leak: Fixed
recipeDiscoveryCachenot clearing when players quit - UUIDs now properly removed onPlayerQuitEvent - Deprecated API: Replaced deprecated
org.yaml.snakeyaml.external.biz.base64Coder.Base64Coderwith nativejava.util.Base64 - Code Quality: Improved serialization stability using Java 8+ standard Base64 encoder/decoder
- Memory Leak: Fixed
Added a null check in BackpackListener.java to prevent the crash.
Нет описания изменений
Stability Update
Reworked backpack saving system to prevent lag, duplication and rollback. Backpacks now use session caching, async safe saving and autosave protection.
Improved shutdown handling and fixed rare issue where a backpack could stop saving after a disk error.
Safe for long-term public servers.
Нет описания изменений
🛡️ Industrial-Grade Stability Thread-Safe Snapshot Saving: Implemented inventory snapshotting. Inventory contents are now cloned on the Main Thread before being passed to asynchronous SQLite tasks, completely eliminating data corruption or item loss during high-activity movement. BackpackHolder (Custom Identifier): Introduced a custom InventoryHolder specifically for backpacks. This ensures 100% accurate identification of backpack inventories, preventing any accidental interaction with Chests, Barrels, or other GUI menus. Force-Save on Disconnect: Added a fallback mechanism to trigger an immediate save during PlayerQuitEvent (Quit, Kick, or Timeout), ensuring player data is never rolled back even during erratic disconnects. ⚡ Performance & Anti-Exploit SQLite Database Integration: Migrated from slow YAML file storage to a professional SQLite engine. All I/O operations are performed asynchronously, maintaining a stable 20 TPS even with a high player count. Anti-Dupe Opening Lock: Integrated a logic lock and a 0.1s cooldown for opening backpacks to prevent common duplication glitches caused by rapid interaction spam. Shift + Click Support: Fully enabled Shift-Click functionality, allowing users to quickly deposit or withdraw items without tedious drag-and-drop operations. 🚫 Custom Restrictions & Rules (Configurable) Nesting Prevention: Strictly blocks players from placing a backpack inside another backpack to prevent infinite storage loops. Carriage Limits: Added a configurable max-backpacks limit (default: 1) to control the number of backpacks a single player can carry. Intelligent Item Filtering: Introduced a toggleable filter to block Armor, Weapons, Tools, and Books from being stored in backpacks, ensuring balanced gameplay and reduced NBT complexity. 🎨 Compatibility & Graphics 1.21.x Hybrid Compatibility: Optimized the file structure to support both legacy 1.21 rendering and the new 1.21.4+ Item Components system. Namespace Standardization: Unified all assets under the backpacks namespace, resolving "black & purple" texture errors and ensuring smooth 3D model rendering. Tiered Crafting Progression: Refined recipes to require the previous tier's backpack as the core ingredient, creating a more rewarding upgrade path.
BackPackSSS Industrial Stabilization Update Comprehensive stability, performance, and security overhaul for production environments.
🛡️ Industrial-Grade Stability Thread-Safe Snapshot Saving: Implemented inventory snapshotting. Contents are cloned on the main thread before being passed to asynchronous SQLite tasks, completely eliminating data corruption or loss during item movement. Reliable Identification (BackpackHolder): Introduced a custom InventoryHolder to uniquely identify backpack inventories. This prevents the plugin from accidentally interacting with or overwriting data from standard chests or other GUIs. Force-Save on Disconnect: Added a fallback save mechanism for PlayerQuitEvent. Whether a player quits, is kicked, or times out, their backpack contents are instantly saved to the database. ⚡ Performance & Anti-Exploit Logic Anti-Dupe Opening Lock: Integrated a 2-tick (0.1s) cooldown and a logic lock for opening backpacks. This eliminates race conditions and duplication glitches caused by rapid interaction spam. State Cleanup: Integrated mandatory closeInventory() calls before every new backpack session to ensure clean transitions and prevent persistent inventory UI bugs. Memory Optimization: Improved memory management by purging player session locks and temporary caches immediately upon logout. 🎨 High-Fidelity Resource Pack (v1.21.x) Hybrid Version Support: Fully optimized structure supporting both Minecraft 1.21-1.21.3 (via models) and 1.21.4+ (via new items range_dispatch syntax). Universal Namespace: Standardized all assets under the backpacks namespace for consistent texture mapping and professional organization. 3D Model Integration: Verified and synced high-quality 3D backpack models (Brown, Yellow, Red) with correct CustomModelData mapping.
Нет описания изменений

