Tags
Creators
Details
Licensed ARR
Published 9 months ago
Updated last week
All versions
1.7.0
Release
XpBottle 1.7.02 weeks ago 50
Compatibility
Minecraft: Java Edition
26.3
26.2
26.1.x
1.21.x
1.20.x
1.19.x
1.18.x
1.17.x
1.16.5
Platforms
Folia
Paper
Purpur
Spigot
Changes
This is a major bugfix release. Updating is strongly recommended as it fixes an XP duplication exploit.
Bug Fixes
Critical
- XP duplication exploit — Negative amounts were accepted by the withdraw command (for example
/xpbottle -1000 1), which gave players free XP instead of removing it. All amounts must now be strictly positive. - Cooldown never enforced — The withdraw cooldown was saved but never checked, so it had no effect. It is now applied before every withdrawal, and the waiting message shows the remaining time.
- MySQL settings ignored — The plugin read
database.mysql.host,database.mysql.port, etc. while the config file definesdatabase.host,database.port,database.name,database.useranddatabase.password. MySQL connections therefore always used the default values. The correct config keys are now used.
Major
- Silent data loss in SQL databases — Saving a player ran a plain
UPDATEthat silently failed when the row did not exist yet. Saves now use an upsert (ON DUPLICATE KEY UPDATEfor MySQL,ON CONFLICT DO UPDATEfor SQLite), so no player data is lost anymore. - Tab completion did not work — The
TabCompleterwas never registered, so/xpbottlehad no tab completion. It is now registered (and re-registered on reload) for subcommands, suggested amounts and player names. min-expandmax-expwere never applied — These config options existed but were never checked. They are now enforced, with dedicated error messages.
Minor
- Integer overflow — The total cost (
amount x bottles) is now computed in 64-bit and rejected if it exceeds theintrange, preventing corrupted bottles. Redeeming a full stack while sneaking is also computed in 64-bit. - Invalid sound name — An invalid sound name in the config no longer causes an error on redeem; the default sound is used as a fallback.
- ItemsAdder fallback — If the configured
itemsadder_iditem cannot be found, the plugin now falls back cleanly to a vanilla bottle instead of failing. - Update checker — A local version newer than the published one is no longer reported as "update available".
- Permissions — The children of
xpbottle.adminpointed to non-existent permissions (xp.admin.*). They now point to the plugin's real admin permissions (xpbottle.give,xpbottle.take,xpbottle.reset,xpbottle.reload).
Optimizations
- Bottle generation — Bottles are now created once and distributed as stacks instead of rebuilding the same item N times. This significantly reduces NBT serialization for large withdrawals.
- NBT key caching — The
NamespacedKeyused to identify XP bottles is cached in the listener instead of being recreated on every interaction and craft check. - Thread-safe SQL access — The shared JDBC connection is now protected against simultaneous access from asynchronous tasks, eliminating a corruption risk on SQLite and MySQL.
- Safer reload —
/xpbottle reloadno longer clears running cooldowns (they can no longer be bypassed by asking for a reload) and keeps the tab completer registered. - Withdraw sound — The previously unused
sound.withdrawconfig option is now played on withdrawal.
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:U2dtoQ7Z:yfcfBnIf"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:U2dtoQ7Z:yfcfBnIf"
}

