Pokeball lets you catch a mob in a ball and throw it back out, just like in Pokémon. It's designed to be straightforward, reliable, and easy to configure - great for survival or adventure servers where players need to move mobs without leads or boats.
Built against Paper API 1.21.8 (Minecraft 1.21.x)
- Spigot resource: https://www.spigotmc.org/resources/pokeball.128981/
- GitHub releases: https://github.com/stdNullPtr/Pokeball/releases
- Hangar: https://hangar.papermc.io/stdNullPtr/Pokeball
- Catch mobs by throwing a Pokeball at them.
- If the mob is allowed (see
config.ymlallowlist), it's captured. - If not allowed (or it's a player), the ball is refunded with a message.
- If the mob is allowed (see
- Release mobs by throwing a filled Pokeball.
- The mob appears where the ball lands.
- Wall hits are adjusted so the mob spawns just outside the wall.
- Admins give balls to players; players just use them - no extra permissions needed for normal use.
Demonstration.Pokeball.mp4
- Paper 1.21.x
- Java 21 (LTS)
- Folia: not currently supported (
folia-supported: falseinpaper-plugin.yml:9)
- Download the latest release JAR and drop it into your server's
plugins/folder. - Start the server once to generate
plugins/Pokeball/config.yml. - Edit
config.ymlto your liking, then run/pokeball reloadin‑game or from console.
- Ask an admin for Pokeballs.
- Throw an empty ball at an allowed mob to capture it.
- Throw a filled ball where you want the mob to appear.
- If capture is not allowed, you get the ball back with a message.
- Help:
/pokeballor/pokeball help(alias/pb). - Version:
/pokeball version. - Give balls:
/pokeball give <player> [amount]. - Reload config:
/pokeball reload. - Storage management:
/pokeball admin list|tp|clean|cap. - Refund mode:
/pokeball admin refund [GIVE|DROP]. - Capture allowlist:
/pokeball admin capture list|allow <entity>|remove <entity>.
| Command | Description | Permission | Notes |
|---|---|---|---|
/pokeball |
Show contextual help | - | Alias: /pb |
/pokeball help |
Show help | - | |
/pokeball version |
Show plugin version | - | Reads plugin meta |
/pokeball reload |
Reload configuration | pokeball.admin.reload |
|
/pokeball give <player> [amount] |
Give Pokeballs | pokeball.admin.give |
Player selector; amount 1-64; tab-suggested amounts |
/pokeball admin list |
List stasis entries (first 20) | pokeball.admin |
Shows id prefix + type |
/pokeball admin tp <id> |
Teleport to stasis location | pokeball.admin |
Players only; tab-suggests IDs |
/pokeball admin clean <id|all> |
Remove one or all entries | pokeball.admin |
Tab-suggests IDs and all |
/pokeball admin cap [maxTotal] |
Show/set storage cap | pokeball.admin |
0 = unlimited; tab-suggests common limits |
/pokeball admin refund [mode] |
Show/set refund mode | pokeball.admin |
Modes: GIVE, DROP; tab-suggested |
/pokeball admin capture list |
List allowed capture types | pokeball.admin |
Tab-completion shows current values |
/pokeball admin capture allow <entity> |
Add a mob type to the allow-list | pokeball.admin |
Suggests missing types; updates config |
/pokeball admin capture remove <entity> |
Remove a mob type from the allow-list | pokeball.admin |
Suggests allowed types; prevents removing all |
- Allowed mobs are configured by name in
config.yml(strict allowlist: anything not listed is blocked). - Capture/release effects and flight trail are configurable:
- Enable/disable specific effects.
- Tweak sizes, counts, intensity.
- Turn the white streak (trail) off if you prefer a cleaner view.
- Release positioning is smart:
- Hitting a wall spawns the mob just outside it (not inside).
- You can tune how far from the wall and how many blocks to probe.
- Config key:
refund.mode: GIVE|DROPGIVE: return balls to player inventory; if full, drop at the impact location.DROP: always drop balls at the impact location.
- Live control:
/pokeball admin refund [mode].
pokeball.admin- access admin subcommands (list,tp,clean,cap,refund,capture …).pokeball.admin.give- allow/pokeball give.pokeball.admin.reload- allow/pokeball reload.pokeball.capture.any- bypass capture allowlist (default op).- Players do not need extra permissions to use balls; they just need the item.
- Paper 1.21.x (api-version
1.21). - Folia: not currently supported (
folia-supported: false). - No NMS; uses modern Paper APIs and Adventure Components.
- Throw empty ball to capture: if the hit mob is allowed and the world is allowed, the entity is parked in stasis and you receive a filled ball linked to it; otherwise you get an empty ball back.
- Throw filled ball to release: the ball's ID rides the projectile; on impact the plugin finds a safe spot just outside walls and teleports the stored mob there, then unfreezes it a tick later; optionally refunds an empty ball.
- Stasis = real, hidden mob: captured entities are frozen, invisible, and moved to a stash location; a small record
{ballId -> world, entityUUID, chunkX/Z, type}is saved and cleaned up on startup/death. - Projectiles are consumed on launch; capture projectiles never deal damage. A configurable flight trail can be shown.
- Config drives allowlist, worlds (capture and release), effects, refund mode, and whether release consumes the ball; admin commands manage storage cap, capture allowlist, and refund mode.
How it works (Detailed)
-
Item identity and state
- Every Pokeball is an
ItemStackwith a uniqueball_idstored in its PDC, making it non‑stackable (seesrc/main/java/com/stdnullptr/pokeball/item/PokeballItemFactory.java). - Empty vs filled is tracked by the presence of
captured_typein the item's PDC. No raw NBT is stored on the item. - Display is driven by MiniMessage: name/lore from config; lore shows Contents: Empty or the captured type (see
src/main/resources/config.yml).
- Every Pokeball is an
-
Throw detection (projectiles)
- On
ProjectileLaunchEvent, if the player is holding a Pokeball (seesrc/main/java/com/stdnullptr/pokeball/listener/ProjectileListeners.java):- If filled, the projectile is tagged with the ball's
ball_id(release throw). - If empty, the projectile is tagged as a Pokeball capture throw.
- The item is consumed immediately on launch (prevents dupes across gamemodes, e.g., creative).
- A small flight trail (glow, dust, end‑rod streak) is rendered if enabled (see
src/main/java/com/stdnullptr/pokeball/config/sections/EffectsConfig.java).
- If filled, the projectile is tagged with the ball's
- On
-
Impact handling
- On
ProjectileHitEvent(seesrc/main/java/com/stdnullptr/pokeball/listener/ProjectileListeners.java):- Release projectile: compute a safe spawn position at impact and call stasis
release(ballId, at)(seesrc/main/java/com/stdnullptr/pokeball/service/StasisService.java). - Capture projectile hitting an entity: validate rules and call stasis
park(entity, newBallId); a filled ball linked to that entity is given back (seesrc/main/java/com/stdnullptr/pokeball/config/sections/CaptureConfig.java). - Block hit or miss (capture): an empty ball is refunded per the configured refund mode (see
src/main/java/com/stdnullptr/pokeball/config/sections/EffectsConfig.java). - Pokeball capture projectiles never deal damage; damage is cancelled for marked projectiles.
- Release projectile: compute a safe spawn position at impact and call stasis
- On
-
Capture flow (high level)
- World gate: if the world is not allowed, fail and refund (see
src/main/resources/config.ymlcompat.worlds). - Type gate: strict allowlist from config; players cannot be captured. A special permission can bypass the
allowlist (optionally annotated on the ball's lore) (see
src/main/java/com/stdnullptr/pokeball/config/sections/CaptureConfig.java). - On success: create a new ball (with new
ball_id), put the target in stasis with that id, mark the ball as filled with the captured type, play capture effects, and return the filled ball (seesrc/main/java/com/stdnullptr/pokeball/listener/ProjectileListeners.javaandsrc/main/java/com/stdnullptr/pokeball/service/StasisService.java).
- World gate: if the world is not allowed, fail and refund (see
-
Release flow (high level)
- The filled ball's
ball_idtravels on the projectile. On impact, we select a spawn point outside walls with passable head/feet space. - Stasis
releaseteleports the stored entity to the target, plays effects, then refunds an empty ball unlessconsume-on-releaseis true (seesrc/main/java/com/stdnullptr/pokeball/service/StasisService.javaandsrc/main/resources/config.yml).
- The filled ball's
-
Release placement
- When a wall is hit, the code probes outward along the impacted face for a few blocks to find “feet and head
passable” space (see
resolveImpactSpawninsrc/main/java/com/stdnullptr/pokeball/listener/ProjectileListeners.java). - A configurable offset is applied outward from the face and slightly up to avoid clipping (see
src/main/resources/config.ymlrelease.*). - Wider mobs (spiders) get an extra lateral clearance check; otherwise universal rules apply (see
isSafeSpawninsrc/main/java/com/stdnullptr/pokeball/listener/ProjectileListeners.java). - If probing fails, it falls back to “just outside the block”; entity/air hits use the projectile location.
- When a wall is hit, the code probes outward along the impacted face for a few blocks to find “feet and head
passable” space (see
-
Stasis (what it is and why)
- Stasis parks the real entity safely away from players immediately on capture, instead of serializing complex data
into the item (see
src/main/java/com/stdnullptr/pokeball/service/StasisService.java). - On capture (
park):- Capacity check (configurable max; 0 = unlimited). If full, capture is refused and the ball is refunded (see
src/main/resources/config.ymlstasis.cap.*). - For living entities, the plugin freezes and hides them: AI off, collidable off, invisible, invulnerable, silent, gravity off, not removed when far.
- The entity is teleported to a configured “stash” location (world/x/y/z; default high Y), so it won't collide
or be seen (see
src/main/java/com/stdnullptr/pokeball/config/sections/StasisConfig.java). - A lightweight mapping is written to
plugins/Pokeball/stasis.yml:{ball_id -> world, entity_uuid, chunkX, chunkZ, type}.
- Capacity check (configurable max; 0 = unlimited). If full, capture is refused and the ball is refunded (see
- On release (
release):- Ensures the source chunk is loaded, finds the entity by UUID, teleports it while invisible and with zero velocity/fall distance, plays effects, then restores normal state a tick later.
- The stasis entry is removed from
stasis.yml.
- Housekeeping:
- On startup, an async cleaner walks
stasis.ymlin small batches per tick, removing entries whose world/entity is missing (seecleanupInvalidAsyncinsrc/main/java/com/stdnullptr/pokeball/service/StasisService.java). - If a stasis entity somehow dies, a listener removes the stale entry (see
src/main/java/com/stdnullptr/pokeball/listener/StasisCleanupListener.java).
- On startup, an async cleaner walks
- Stasis parks the real entity safely away from players immediately on capture, instead of serializing complex data
into the item (see
-
Refunds and permissions
- Refunds after misses/failures and post‑release are handled by GIVE (to inventory, drop on overflow) or DROP (
always on ground) (see
src/main/resources/config.ymlrefund.modeandsrc/main/java/com/stdnullptr/pokeball/config/sections/EffectsConfig.java). - A special capture permission can bypass the allowlist; admins can live‑tune storage cap, capture allowlist, and
refund mode via
/pokeball admin.
- Refunds after misses/failures and post‑release are handled by GIVE (to inventory, drop on overflow) or DROP (
always on ground) (see
-
Notes for command setup
- Commands are registered using Paper's Brigadier API with typed arguments and tab‑completion (no YAML commands).
- Player arguments use native selectors and client‑side suggestions; integers/booleans are validated client‑side.
- Build:
mvn -q -DskipTests package - Output:
target/Pokeball-<version>.jar
This project is licensed under the AGPL‑3.0. See LICENSE for details.
Found a bug or have a feature request? Open an issue on the project's GitHub repository.