Virtual sparring players for testing combat plugins. Spawn a bot that is a real player to your server — real player-list entry, real inventory, real movement packets, real knockback — then tune its ping, aim, click speed, reach, and movement to spar against. Built for Paper 1.17.1 → 26.x.
Testing a combat plugin (knockback tuning, anti-cheat, OldCombatMechanics modesets, Mental profiles) usually means rounding up real people. SimpleBoxer gives you opponents on demand.
A boxer is indistinguishable from a real player to the server. Whatever shapes combat on your server shapes the boxer identically, because the boxer rides the same wire:
- Knockback arrives as the same velocity packet a real client receives, and is integrated through the vanilla client's motion math — so a boxer flies back exactly like a player with the same ping would.
- Movement, sprinting, attacks, and swings leave as genuine player packets —
same server validation, same Bukkit events (
PlayerMoveEvent,PlayerToggleSprintEvent, …). - Vanilla commands (
/tp,/effect,/give) and other plugins target boxers like any player. They're hidden from the tab list but stay tab-completable.
Use case: arena-style flat/simple terrain. Boxers sprint, strafe, and jump single-block steps — they don't path through mazes.
- 🥊 Real-player bots — no NPC shortcuts; the server sees a
ServerPlayer. - 🎚️ Difficulty ladder —
dummy→easy→medium→hard→expert→aimbot, each a full behaviour bundle. - 🛰️ Simulated ping — symmetric RTT that delays both perception and action, like a real laggy client.
- 🎯 Spring-damper aim — presets
locked/sharp/smooth/sloppy, or tune stiffness, damping, and max turn speed by hand. - 🖱️ Configurable clicking — CPS with jitter, reach and aim-cone gating.
- 🏃 Movement styles —
rush,strafe-circle,strafe-weave,stand, with w-tap rhythm and stop-distance rings. - ⚙️ Live tuning — change any knob at runtime with
/boxer set. - 🧩 Skins & targets — wear any account's skin; pre-bind a follow target.
- 🛡️ Tireless fixtures — invincible (full hit + restore) and well-fed by default, so a test never ends because the bot starved or died.
| Server | Paper (or a Paper fork) 1.17.1 through 26.x |
| Java | 17+ for MC 1.17–1.20.4 · 21+ for MC 1.20.5 and newer |
SimpleBoxer reaches into server internals (NMS) to make boxers real players. It targets Paper; Spigot/CraftBukkit are untested. If your exact version is unsupported the plugin disables itself cleanly and logs why — it never half-loads.
- Download the latest
SimpleBoxer-x.y.z.jarfrom the Releases page. - Drop it into your server's
plugins/folder. - Restart the server (a reload won't load a new plugin cleanly).
- Verify — you should see
SimpleBoxer x.y.z enabledin the console, and aplugins/SimpleBoxer/config.ymlwill be created. - Spawn one in-game:
/boxer spawn Rival hard target:YourName
That's it. By default only operators can use the commands (see Permissions).
/boxer spawn Bot # an unhandicapped partner at your feet
/boxer spawn Bot hard # pick a difficulty from the ladder
/boxer spawn Bot expert skin:Notch target:Steve
/boxer target Bot Steve # sic an existing boxer onto a player
/boxer set Bot ping 150 # tune anything, live
/boxer pause Bot # freeze its brain (it still takes hits)
/boxer info Bot # inspect its current settings
/boxer remove all # clean up
Alias: /sb works anywhere /boxer does.
All commands are under /boxer (alias /sb).
| Command | Description |
|---|---|
/boxer spawn <name> [preset] [skin:<player>] [target:<player>] [at <x> <y> <z>] |
Spawn a boxer. With no preset it uses your defaults. at is required from console. |
/boxer remove <name|all> |
Remove one boxer, or every boxer. |
/boxer list |
List live boxers with their target, ping, and CPS. |
/boxer info <name> |
Full settings dump for one boxer. |
/boxer target <name> <player|none> |
Set or clear a boxer's follow/attack target. |
/boxer pause <name|all> |
Freeze a boxer's brain (it still receives knockback). |
/boxer resume <name|all> |
Un-freeze. |
/boxer set <name> <key> <value> |
Tune one setting at runtime (see below). |
/boxer reload |
Re-read config.yml. |
| Key | Value | Example |
|---|---|---|
ping |
whole number of ms (0–2000) | /boxer set Bot ping 150 |
cps |
clicks per second (0–50; 0 = never attacks) | /boxer set Bot cps 12 |
reach |
blocks (0.5–6) | /boxer set Bot reach 3.2 |
aim |
locked / sharp / smooth / sloppy |
/boxer set Bot aim smooth |
wtap |
true / false |
/boxer set Bot wtap true |
movement |
rush / strafe-circle / strafe-weave / stand |
/boxer set Bot movement strafe-circle |
preset |
any preset name | /boxer set Bot preset expert |
invincible |
true / false |
/boxer set Bot invincible false |
Invalid input is answered in plain language — e.g. set Bot ping abc replies
ping expects a whole number, not 'abc'. rather than throwing.
Each preset bundles ping, CPS, aim, reach discipline, w-tap, and movement into
a named tier. Every component stays individually overridable at spawn or with
/boxer set.
| Preset | Feel | Ping | CPS | Aim |
|---|---|---|---|---|
dummy |
Stands still, never attacks — a punching bag | 0 | 0 | smooth |
easy |
High ping, slow sloppy clicks, walks | 120 | 4 | sloppy |
medium |
An ordinary player | 60 | 7 | smooth |
hard |
A practiced PvPer, disciplined w-taps | 35 | 10 | sharp |
expert |
Tournament-grade, circles its target | 15 | 13 | tight |
aimbot |
The calibrator: zero ping, locked aim | 0 | 16 | locked |
You can also define your own presets in config.yml as sparse overlays — an
entry there with a built-in's name overrides it.
Everything defaults to operators only. Grant these nodes to delegate.
| Node | Grants | Default |
|---|---|---|
simpleboxer.command.use |
Run /boxer, plus help, list, info |
op |
simpleboxer.command.spawn |
spawn and remove |
op |
simpleboxer.command.control |
target, pause, resume |
op |
simpleboxer.command.tune |
set |
op |
simpleboxer.command.reload |
reload |
op |
simpleboxer.* |
All of the above | op |
A documented config.yml is written to plugins/SimpleBoxer/ on first start.
The two top-level blocks are defaults (applied to any spawn that names no
preset, and the base every preset overlays) and presets (your own named
overlays). Every key inherits a sensible default when omitted, and a malformed
value warns in the console and keeps the inherited value — a typo can never
break a spawn.
# Keep boxers out of the tab list (still tab-completable in commands).
hide-from-tab: true
defaults:
ping-ms: 0 # simulated RTT, 0–2000 (split: half perception, half action)
cps: 8.0 # clicks per second, 0–50 (0 = never attacks)
click-jitter: 0.3 # per-click interval wobble, 0–0.9
aim:
preset: sharp # locked / sharp / smooth / sloppy
# stiffness: 0.55 # optional granular overrides on top of the preset
# damping: 0.30
# max-velocity: 60.0
reach: 3.0 # attack range in blocks, 0.5–6
aim-tolerance-degrees: 10.0 # a click only attacks within this cone
w-tap:
enabled: false
delay-ticks: 1 # ticks after a hit before forward releases (0–20)
release-ticks: 2 # ticks forward stays released (1–20)
movement:
style: rush # rush / strafe-circle / strafe-weave / stand
stop-distance: 0.0 # 0 = hold W through the target (true rusher)
sprint: true
invincible: true # take the full hit, then restore health
feed-hunger: true # pin hunger full so sprint stays legal
# Your own presets (sparse overlays over `defaults`):
presets:
# laggy-spammer:
# ping-ms: 180
# cps: 14
# aim:
# preset: smoothAfter editing, run /boxer reload.
Other plugins can spawn and control boxers. The api module exposes
BoxerService, obtainable from Bukkit's ServicesManager:
BoxerService boxers = Bukkit.getServicesManager().load(BoxerService.class);
boxers.spawn(new BoxerSpawnRequest(
"Rival",
player.getLocation(),
DifficultyPresets.HARD,
"Notch", // skin owner, or null
player.getName() // target, or null
)).thenAccept(boxer -> boxer.setTarget(player));BoxerSpawnEvent and BoxerRemoveEvent fire on the Bukkit event bus.
Requires a JDK 21+ (the build provisions a Java 25 toolchain automatically).
git clone https://github.com/owengregson/SimpleBoxer.git
cd SimpleBoxer
./gradlew build # compiles + runs unit tests; jar in core/build/libs/The shaded plugin jar is core/build/libs/SimpleBoxer-<version>.jar.
./gradlew build # unit tests (physics, aim, latency, settings)
./gradlew integrationTest # boots real Paper servers (floor + ceiling) and runs the in-server suite
./gradlew integrationTestMatrix # every version listed in gradle.propertiesIntegration results land in
run/<version>/plugins/SimpleBoxerTester/test-results.txt.
See ARCHITECTURE.md for the module map, the
captured-connection design, threading rules, and the honest boundaries
(in-process packets are invisible to packet-sniffing plugins; Folia and
pathfinding are deferred).
- In-process boxers have no socket, so their outbound packets don't traverse the netty pipeline — ProtocolLib/PacketEvents listeners won't see boxer traffic. The interesting direction (a real player attacking a boxer) works fully, because the attacker's own connection carries the packets.
- Folia isn't supported yet (the scheduling seam is ready; placement and cross-region brains aren't).
- Boxers are ephemeral — they're never written to player data and are despawned cleanly on shutdown.
Built by owengregson to pair with Mental and OldCombatMechanics. Issues and pull requests welcome on GitHub.