Tags
Creators
Details
2.3.1
Compatibility
Changes
š PrivateChest - Version 2.3.1 Release Notes
Release Date: 20/03/2026
[2.3.1] - 2026-03-20
Added
-
Granular Command Permissions
- New individual permission nodes for each player command:
privatechest.lockā Controls access to/lockchestprivatechest.unlockā Controls access to/unlockchestprivatechest.trustā Controls access to/trustprivatechest.untrustā Controls access to/untrustprivatechest.renameā Controls access to/renamecontainerprivatechest.signā Controls access to[Private]sign-based locking
privatechest.usenow acts as a parent permission that grants all of the above- Server admins can now deny specific commands per player or group without affecting the rest
- Sign protection (
privatechest.sign) is independent from the command (privatechest.lock), so blocking/lockchestdoes not block[Private]signs
- New individual permission nodes for each player command:
-
Shared Container Utility Class (
ContainerUtils)- Centralized double chest detection, container validation, and location serialization
- Eliminates code duplication across 6 files (listeners and commands)
- Single source of truth for container logic ā easier to maintain and less error-prone
Fixed
-
Explosion protection now covers both halves of double chests
- Previously, if only one half of a double chest had the lock record, the other half could be destroyed by TNT/creepers
- Both
EntityExplodeEventandBlockExplodeEventnow check all container parts
-
Sign protection now uses its own permission (
privatechest.sign)- Previously shared permission with
/lockchest, meaning blocking the command also blocked[Private]signs - Now uses a dedicated
privatechest.signpermission, independent fromprivatechest.lock
- Previously shared permission with
-
Null safety in
ChestLocker.serializeLocation()- Previously could throw
NullPointerExceptionif a world was unloaded - Now delegates to
ContainerUtils.serializeLocation()with proper null checks
- Previously could throw
-
Sign-based protection password is now cryptographically secure
- Previously used deterministic
hashCode()based on player UUID and location (predictable) - Now uses
SecureRandomto generate unpredictable 32-character hex passwords
- Previously used deterministic
Improved
-
Thread Safety
ChestLocker:chestOwnersandchestPasswordsmaps changed fromHashMaptoConcurrentHashMapTrustManager:trustRelationsmap changed fromHashMaptoConcurrentHashMap, inner sets useConcurrentHashMap.newKeySet()- Prevents
ConcurrentModificationExceptionwhen async cleanup runs alongside main thread operations
-
Timing-safe password comparison
PasswordManager.verifyPassword()now usesMessageDigest.isEqual()instead ofString.equals()- Prevents timing attacks that could leak password hash information
How Granular Permissions Work
- By default, nothing changes for existing servers.
privatechest.usestill grants all commands. - To block a specific command, negate its permission in your permissions plugin:
# LuckPerms example ā block only /lockchest but keep [Private] signs working permissions: - privatechest.lock: false - The remaining commands will continue working normally.
Migration Notes
- Fully backwards compatible ā no configuration changes required
- Existing permission setups using
privatechest.usewill continue to work identically - Thread safety improvements are transparent ā no action needed
Compatibility
- Minecraft 1.16.x ā 1.21.11
- Tested on Paper, Purpur, Pufferfish, and Spigot
Support & Feedback
- Report issues on GitHub: PrivateChest Issues
- SpigotMC Resource: https://www.spigotmc.org/resources/privatechest.124359/
Thank you for using PrivateChest!
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:TfUGyoNj:41QDAUsz"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:TfUGyoNj:41QDAUsz"
}

