Configuration
ShadowBlocker
An anticheat that shows its work instead of shouting.
Where the files are
plugins/ShadowBlocker/ The plugin creates them on first start and updates them by itself on new versions, keeping your changes.
Applying changes
/shadowblocker reload Reloads the files without restarting the server.
No setting found
Try another word, or search every plugin.
Settings
The files that decide how the plugin behaves. Start here.
config.yml
General settings 94 settings
config.ymlplugins/ShadowBlocker/config.yml File notes
This file decides HOW the plugin behaves. The thresholds of the individual checks live in checks.yml instead, one by one, with what they detect and what can produce a false positive written next to them.
Every value here already has the right number for a normal server: you can copy the jar in and never open this file. When you do need to change something, the comment above each key says WHAT it does and, above all, WHY it is the way it is.
A wrong value never stops the plugin: it is replaced with the default one and the console gets the path and what was expected there.
On update your files are moved to the new version automatically: every value you changed (and every threshold you tuned in checks.yml) is carried over, new options arrive with their comments, the old files are kept in backup/<date>/, and if something needs your attention you will find PORTING-<version>.txt in the plugin folder.
Durations are written in plain text: 500ms, 20t (ticks), 30s, 5m, 2h30m, 7d. A number with no unit means seconds.
config-version1How ShadowBlocker decides
Every check follows the same ladder, and each step is harder to reach than the one below it:
Keep reading
1. OVERSHOOT. A check measures something (a speed, a distance, a
time) and compares it with what Minecraft allows in that exact situation, plus margins for latency. Going over is NOT a violation yet. Nobody sees it, except a staff member who is watching that player with /shadowblocker debug <player>.
2. VIOLATION. The overshoots have repeated more than the check's
'buffer' (checks.yml). The violation is written to the evidence log with everything around it, and adds 'weight' points to that check. The points drop by 'decay' every minute.
3. ALERT. Only when the points of that check reach its
'alert-threshold' does the staff get a message in chat. One odd moment - a lag spike, a bugged block - never reaches this step.
4. SETBACK. For the movement checks marked 'setback: true', at the
same moment the player is moved back to the last spot where they were standing, clean. It undoes the impossible movement and is not a punishment (see setbacks below).
5. PUNISHMENT. Only with punishments.enabled: true (off by default),
and only with the points of checks that reached their own 'punish-threshold'. The checks that only corroborate (clicks, aim, walls, packet order...) have punish-threshold 0: on their own they never punish anybody.
Before step 1 even happens, a check does not run at all while an exemption window is open: teleports (any plugin), respawns, world changes, knockback and velocity from plugins, explosions, pistons, vehicles, elytra, riptide, server lag, connection hiccups, blocks refused by a protection plugin, chunks the client has not received. Every window is lengthened by the player's ping. See exemptions.
A false positive? do this
1. Look at what was measured:
/shadowblocker violations <player> the latest evidence
/shadowblocker evidence <number> one of them in full:
value vs limit, blocks
around, effects, ping
/shadowblocker player <player> points of every check
and the alert thresholds2. Watch it happen again, live:
/shadowblocker debug <player> every overshoot, with
value, limit and buffer
(same command to stop;
from the console it goes
to the log)3. Stop it while you investigate:
/shadowblocker exempt <player> 10m movement
suspends a family (or 'all') for a
while, and closes on its own
/shadowblocker check <id> off one check, until restart
exemptions.disabled-worlds every check, one world
disabled-worlds in checks.yml one check, one world
integrations.shadowregions.suspend one region
integrations.shadowgames.suspend one arena
/shadowblocker clear <player> wipes their points (the
evidence stays)4. Fix it for good: raise that check's 'buffer' or
'alert-threshold' in checks.yml (not the limit), or set engine.sensitivity to relaxed.
5. Report it to us with the evidence file: /shadowblocker export
<player> writes every piece of evidence to one file, which is exactly what we need to reproduce it.
The most important thing in the whole file
Freshly installed, ShadowBlocker PUNISHES NOBODY. It detects everything, alerts the staff, writes the evidence, and stops there.
Keep reading
That is not timidity: it is the only honest thing to do. An anticheat that has just been switched on does not know your server yet - it does not know you have a grappling hook plugin, that your arena has double jump, that half of your players connect from 250 ms away. A plugin that starts banning before it knows those things bans the wrong people, and a single video of a false positive costs more than ten sales.
THE RIGHT WAY:
1. install it and leave it alone for a week; 2. now and then look at /shadowblocker violations and ask yourself whether what you see makes sense; 3. where it does not, raise that check's buffer in checks.yml (the buffer matters far more than the threshold); 4. ONLY THEN set punishments.enabled to true.
In the meantime the staff punishes by hand from the buttons in the alerts, which work from the first minute.
punishments3 keysenabledfalsefalse = observation mode. See the paragraph above.
jail-cell""The ShadowEssentials cell used by the JAIL action. Empty = the first available one. If you do not have ShadowEssentials, the JAIL action does nothing and the console says why.
punishments.ladder4 keysThe ladder: when the score reaches this much, do that.
Keep reading
Every step fires ONCE per play session. The order you write them in does not matter: they are sorted by score on their own.
Available actions:
alert only an alert to the staff log a line in the console, more visible ticket opens a case in the ShadowControl panel with the evidence kick kicks from the server mute mutes (needs ShadowEssentials) jail sends to jail (needs ShadowEssentials) ban bans (needs ShadowEssentials) command runs a command from the console, with %player% and %reason%
The scores below are POINTS THAT COUNT FOR PUNISHMENTS: only the points of checks that have reached their own punish-threshold in checks.yml. With the default values, 60 points are about eight violations of a movement check close together; the checks that only corroborate (clicks, aim, walls, packet order) never count. Points drop by each check's 'decay' every minute.
MIND the permanent ban: there is none here, and that is deliberate. A ban given automatically must be undoable by a staff member who spots the mistake, and a permanent one handed out at night with nobody watching is the fastest way to lose an honest player forever.
punishments.ladder.first2 keysscore60actionlogpunishments.ladder.second2 keysscore120actionticketpunishments.ladder.third3 keysscore200actionkickreason"Disallowed behaviour"punishments.ladder.fourth4 keysscore350actionbanduration"7d"reason"Disallowed behaviour detected"The engine
engine5 keyssensitivity"balanced"How strict the whole plugin is, in one word:
Keep reading
relaxed every buffer and every alert/punish threshold x1.5.
Slower to alert, even fewer false positives. For
servers with many high-ping or Bedrock players, or
with plugins that move players in unusual ways.
balanced checks.yml exactly as written. The default.
strict x0.75: alerts sooner. Only after a week of alerts that
you have looked at and found correct.It multiplies the values of checks.yml at load time, so you can keep your own tuning there and still switch preset.
decay-per-minute10.0How many points a player's score drops by every minute, for the checks that do not set their own 'decay' in checks.yml (every shipped check does) and for reports sent by other plugins.
Keep reading
The score has to come down, otherwise it does not describe a player: it describes their career. Someone who has played for two years and picked up four violations from as many hiccups of the network would end up, on some ordinary day, above a ban threshold - because of four moments spread over two years. The score answers one question, and the right one: IS THIS PERSON CHEATING NOW?
The decay is linear and not exponential, on purpose: "it drops by ten a minute" can be worked out in your head, and a parameter nobody can predict is a parameter nobody tunes.
With 10 a minute and the default scores, someone who reaches 120 points is clean again in twelve minutes. 0 = the score never drops. Do not do that.
buffer-decay0.25How much a check's buffer drops by when a sample is fine.
The buffer is how many times in a row a check has to go over before it really counts. It goes up by 1 on every overshoot and down by this value on every clean sample.
It MUST be less than 1, and it must not be 0. If one clean tick reset the buffer, cheating intermittently - half a second on, half a second off - would be enough never to cross any threshold. That is literally how "pulsing" cheats are built.
snapshot-period-ticks1How many ticks apart the world around a player is photographed.
Keep reading
1 = every tick, and that is what it takes to measure well. The snapshot is taken ONLY for players who have moved since last time: somebody standing still in a base costs one comparison and no more.
Raising it to 2 halves the cost and makes the measurements less precise: the exemptions for teleports and knockback arrive one tick later, so a few more false positives. Only touch this if you have measured a problem, not "just in case".
engine.packets1 keyenabledtrueRead the packets where they arrive, instead of where the server uses them.
Keep reading
It is the difference between an anticheat that measures the player's time and one that measures how hard the server was working. It is needed for:
- the packet checks (order, rate, fields); - the precise measurement of how long a dig took; - not costing the server any ticks.
If you switch it off, movement is read from the game's events: it costs more, it measures worse, and the packet checks stay off. The plugin tells you so in the console.
It switches itself off, saying so, on a server where it cannot attach: that is not an error to report, it is a different server.
Alerts to the staff
alerts3 keyscooldown"3s"How long to wait before repeating the alert of the same check for the same player.
A cheat produces violations in bursts: with no brake, one alert for each of them fills every staff member's chat and makes them switch alerts off forever - which makes the plugin useless exactly when it is working. In the meantime the score carries on rising in silence.
to-consoletrueWrite the alerts in the console as well.
The console is the only staff member who is always there, and it is the one read by whoever opens the logs the next day. If you have hundreds of violations a day and your logs are getting heavy, switch it off: the evidence stays in the log all the same.
log-quiet-violationstrueAlso write one "[flag]" line in the console for the violations that have NOT reached their check's alert-threshold yet (nobody in chat sees them). It is how you see what the plugin is thinking without disturbing the staff, and it is what to read when tuning. Switch it off if your console is busy.
Setbacks
For the movement checks with 'setback: true' in checks.yml (fly, speed, jump, climb, phase): when a violation reaches the check's alert-threshold, the player is moved back to the last spot where they were standing on real ground, outside blocks, with no movement violation in the last second.
Keep reading
It is the cheapest answer when we are wrong: an honest player ends up a few blocks back and keeps playing. A cheater loses what the cheat gave them. It is NOT a punishment and works in observation mode too.
setbacks4 keysenabledtruefalse = never move anybody; the alerts still arrive.
max-distance48.0A spot further than this (in blocks) is not a correction any more, it would be a teleport: nothing is done.
max-age"30s"A spot older than this is stale: nothing is done.
cooldown"1s"At most one setback per player this often. The teleport opens its own exemption window anyway.
The evidence log
Every violation is saved with everything that was needed to judge it around it: position, velocity, blocks around, what was in hand, effects, ping, open exemptions. That is what lets a staff member DECIDE instead of trusting.
evidence3 keysmemory-cap500How many pieces of evidence to keep in memory, ready for /shadowblocker violations.
In the free version the real cap is 50 and this value does not count. With a licence the evidence also goes to the database, and this number is only the fast window.
keep-for"30d"How long to keep the evidence on the database.
A month is long enough to answer "had we already told them?", which is the question that makes a history useful. Beyond that it is weight. Zero = forever.
write-filestrueAlso write a text file for every serious violation, inside evidence/, organised by day.
It is there to attach to an appeal, and to let the panel read it without the panel having to know anything about our database.
The cases
This is the feature this plugin exists for: a serious violation becomes, on its own, something somebody has to look at, with the evidence attached and a summary in readable words.
requests3 keysscore120From which score upwards a case is opened.
0 = never. Raise it if your staff receives too many cases: three real ones a day are better than thirty of which nobody looks at any.
cooldown"5m"At most one case per player how often.
It is there to keep the staff list readable: twenty cases for the same person cancel out the value of the feature.
ai-summarytrueAsk the ShadowControl assistant for a two-line summary.
Optional twice over: it needs ShadowControl with the assistant set up, and it needs our licence. The summary we write ourselves ALWAYS arrives, and arrives immediately; this one is added afterwards, when it comes back. The case never waits for a call out to the internet.
The exemptions
The most important paragraph after the one about punishments.
Keep reading
Latency and teleports are the number one cause of false positives in every anticheat ever written. Each of these windows suspends the checks for a moment after an event that makes it impossible to measure anything sensible.
The default values are GENEROUS on purpose. Lowering them does not catch you more cheaters - somebody cheating does it all evening, not for the two seconds after a teleport - it only catches you more innocent people.
To find out whether a window is staying open too long on somebody:
/shadowblocker exempt <player>
exemptions7 keyslag-tps18.0Below how many ticks per second (one-minute average) the server is considered to be struggling: the movement, combat and packet checks are suspended for everybody while it lasts.
When the ticks take twice as long, every time-based measurement doubles. 18 means "more than 10% slow". Lower it (15-16) only if your server lives permanently around 18-19 TPS and you still want detection there.
lag-spike"200ms"A single server tick longer than this opens the lag window for everybody at once. The one-minute average above does not notice a one-second freeze (a world save, a garbage collection, a chunk being generated); this does. A normal tick is 50ms. 0 = off.
connection-gap"300ms"A silence of a player's connection longer than this, followed by a burst of everything that was queued, opens the "connection" window for that player (movement, combat and packet checks). A client in game sends a packet every 50ms, always. 0 = off.
ping-extension1.0Every window below is lengthened by the player's ping (the highest of the last seconds) multiplied by this. A two-second window is generous at 50ms and tight at 900ms: this is what makes it fair for everybody. 0 = off.
max-ping-extension"2s"The most a window can be lengthened by the ping.
disabled-worlds[ ]Worlds where NO check runs at all (creative build worlds, lobbies with parkour plugins...). To switch off one check in one world use disabled-worlds inside that check in checks.yml.
exemptions.windows20 keysjoin"6s"Just joined: the client is loading chunks and sending inconsistent positions.
teleport"3s"THE NUMBER ONE CAUSE. Between the moment the server moves somebody and the moment the client confirms it, movements arrive that start from the old place: standing still they look like flight, running they look like two hundred blocks a second.
world-change"6s"Like a teleport, plus the chunk loading.
portal"6s"respawn"4s"velocity"2s"The server sent a velocity to the client: knockback, an arrow, a wind charge, a plugin's spring plate. For a few ticks the player moves faster than any formula, and it has nothing to do with what they pressed.
explosion"3s"piston"2s"vehicle"2500ms"Got on or off a vehicle: the physics change mid-movement.
gliding"2500ms"riptide"4s"bounce"2s"gamemode"3s"flight"3s"effect"1500ms"An effect that changes movement has just arrived or just ended.
lag"4s"The server struggled. This one applies to everybody at once.
chunk"3s"A chunk not loaded yet under their feet.
plugin"2s"Another plugin moved or changed the player. It is also the window used by the public API when a minigame asks for the checks to be suspended.
connection"3s"The player's connection stopped and then delivered everything at once (see connection-gap above). The silence itself is added to this window.
ghost-block"1500ms"A block placement or break was cancelled by another plugin (a protection, an arena): for a moment the client sees a block the server does not have, or the other way round.
Bedrock players (Geyser / Floodgate)
A Bedrock player does not run the Java client: the bridge translates their movement, hits and digs into Java packets, and the translation does not follow the Java client's physics and timing. The checks written for Java would flag them for playing from a phone.
bedrock4 keysdetecttrueRecognise Bedrock players: Floodgate's or Geyser's API when they are installed, otherwise the client name. false = treat everybody as Java.
detect-by-uuidtrueAlso recognise them by the Floodgate UUID (first half all zeros). Leave it on unless you use a UUID scheme of your own.
exempt-families[movement, combat, packet]The families of checks that do NOT run on Bedrock players. Families: movement, combat, interaction, packet
exempt-checks[fastbreak, visibility]Single checks that do not run on them either (from the other families). Dig timing and line of sight are translated too loosely by the bridges to be judged.
The integrations
All optional. The plugin starts and protects a server where nothing else of the suite is installed; when a piece switches off because another plugin is missing, the console says which one and what went off with it.
integrations4 keysintegrations.shadowcontrol2 keysreportstrueWrite requests.yml, which the panel reads.
report-every"1m"integrations.shadowgames1 keysuspend[ ]Suspend certain checks inside arenas.
A minigame with double jump, grappling hooks or launch pads has nothing to do with Minecraft's physics, and insisting in there flags exactly the people who are enjoying themselves.
It is written: "game:arena = family,family"
or: "game:* = family" for every arena of a game
Families: movement, combat, interaction, packet
Examples (commented out: switch them on if you have those games):
suspend: - "bedwars:* = movement" - "parkour:* = movement,packet" - "skywars:arena3 = movement"
integrations.shadowregions1 keysuspend[ ]As above, but by region. It is there for built events, where a staff member fires people into the air for fun.
It is written: "regionname = family,family"
suspend: - "event_arena = movement,combat"
integrations.other-anticheat2 keysraise-buffersfalseWhat to do if the server already has another anticheat.
Keep reading
Two anticheats together do not protect more: they get in each other's way. Each one corrects movement in its own way and the other reads those corrections as if they were the player, and the result is false positives on both sides - with the blame always seeming to fall on the newer one.
The right thing is to keep only one. If you are migrating and have two of them for a week, set this to true: it raises the buffers of every check, detection becomes slower and false positives far fewer.
extra-buffer6Where the evidence ends up
Out of the box: an H2 file inside the plugin folder. Nothing to install, no user to create, no password.
The database is ONLY for the archive: detection, the score, the alerts and the latest evidence in memory all work without it. If it cannot be opened, the plugin starts anyway - an anticheat that does not start is an unprotected server.
storage5 keystable-prefix"sb_"The table prefix. Change it if several plugins share the same database.
storage.mysql7 keysenabledfalseShared MySQL is part of the Premium version.
It is needed on a network: without it the score and the evidence start from zero on every server switch, which is also the first trick somebody cheating on a network learns.
host"127.0.0.1"port3306database"shadowblocker"username"root"password""parameters"useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=utf8"storagefallback-to-localtrueIf MySQL does not answer, fall back to the local file instead of not starting. Leave it true: the evidence matters, but not as much as having a protected server.
storage.h21 keyauto-serverfalseLets another process open the same H2 file. Only needed if you want to look at the database while the server is running.
storage.pool5 keysmaximum-pool-size4Small on purpose: no queries are made inside the game here. Evidence is written asynchronously and read when a staff member opens a list.
minimum-idle1connection-timeout-ms10000idle-timeout-ms600000max-lifetime-ms1800000Licence
ShadowBlocker is a single jar with two versions inside it.
Keep reading
With no key it runs the FREE version, and the free one has ALL THE DETECTION: every movement, combat, interaction and packet check, the score, the adjustable thresholds, the explanation of every violation, observation mode, the alerts, the exemptions, both languages. No check is behind a payment, and that is not generosity: an anticheat that protects half the players has a hole, and the hole is found first by whoever is cheating.
What you pay for is everything AROUND the detection:
- automatic punishments (the free one alerts, the staff decides); - the evidence log on the database, searchable and with no expiry; - the cases on ShadowControl with the assistant's summary; - suspension by arena and by region; - shared MySQL across a network; - exporting the evidence for an appeal.
The plugin NEVER switches itself off over a licence problem. The worst case is going back to free, writing in the console why. In an anticheat that counts twice over: switching off would mean leaving the server unprotected exactly when nobody notices.
Usually nothing here needs touching: the key is activated in game with
/shadowblocker license claim <purchase-code> /shadowblocker license activate <key>
and is written to license.yml, which lives outside this file on purpose: a config gets sent to support, a licence does not.
license4 keysTHE KEY LIVES IN license.yml (same folder as this file), or in the central plugins/ShadowControl/licenses.yml for the whole suite. The 'key' below is only a fallback for deploy systems that generate config.yml: leave it empty.
The key does NOT live here: it lives in license.yml, in this folder, written at first start (or in plugins/ShadowControl/licenses.yml for the whole suite). Only if you would rather keep it here instead of in license.yml. The SHADOWBLOCKER_LICENSE environment variable and the -Dshadowblocker.license=... system property work too.
grace-days7Days of grace before going back to free when a subscription expires or when the server address does not match the licence. A renewal has almost always already started, and an address that does not match is far more often a NAT than a thief.
server-ip""The address this server is known by, if you know it. Behind a proxy a machine never sees its own domain.
resolve-public-iptrueOne single call, at startup, to the licence service (activation-url + /ip) to find out what public address this server has: behind NAT it is the only way to recognise it. The service answers with the address it sees, and nothing about the server is sent. Only when a licence is installed: a server without a key asks nobody. false = never ask.
activation-url'https://shadow-licences.riccardobotins.workers.dev'Activation service. This is the BASE address: the plugin appends /claim and /check itself. Empty = completely offline verification.
Messages and interface
messages3 keysdefault-locale"en_US"The language used when the player's own one is not there. The plugin ships en_US and it_IT; a file you add to lang/ is picked up too.
Everything a person reads goes through lang/*.yml. Everything else - command names, the keys in this file, the units written to the database, the console - is English everywhere and does not change with this setting.
follow-client-localetrueFollow the language set in each player's client.
auto-updatetrueMove lang/*.yml to the new version when the plugin is updated: new texts arrive, the texts you changed stay yours, and the previous file is kept in backup/<date>/. With false the files in lang/ are never rewritten, and a text missing from them is read from the copy inside the jar.
Keep reading
This applies ONLY to the languages. config.yml, checks.yml and menus/ are always moved to the new version the same way: what you changed is carried over, and what cannot be carried is listed in PORTING-<version>.txt.
gui1 keyclick-delay"150ms"How long to wait between two clicks in the same window. It is there against accidental double clicks, not against cheats.
compatibility1 keysection-color-codestrueAccept the section character as well as the ampersand in colours. Useful if you paste in text coming from an older plugin.
Diagnostics
logging1 keylog-sqlfalseWrite every SQL statement in the console. Useful for an afternoon, then switch it back off.
metrics1 keyenabledtruebStats: how many servers use this plugin and which version they are on.
Keep reading
No data of any player leaves: no name, no UUID, no address, no coordinate, no violation. What is sent, every 30 minutes: a random server id (serverUuid, not derived from anything), the plugin version, the Minecraft/Paper and Java versions, the operating system and core count, the online mode, how many players are online (a number only) and one chart, the licence tier (free/premium/creator). Nothing is sent while the plugin waits for a licence.
It serves one question only: which Minecraft version can stop being supported. Without that number the answer is an opinion.
false switches off this plugin only. enabled: false in plugins/bStats/config.yml switches off every bStats plugin on this server at once.
checks.yml
The anti-cheat checks 211 settings
checks.ymlplugins/ShadowBlocker/checks.yml File notes
One check per section. Each one switches on, switches off and tunes on its own, and each one has WHAT IT DETECTS and WHAT CAN PRODUCE A FALSE POSITIVE written above it. Those are the same two sentences you see in game with /shadowblocker check <name> and in the checks screen: one source, written next to the code it describes.
The keys they all have
enabled whether the check runs.
Keep reading
weight how many points ONE violation adds to this
check (and to the player's score).buffer HOW MANY TIMES IN A ROW the check has to go over
its limit before it counts as a violation at all.alert-threshold how many points of THIS check are needed before
the staff gets an alert. Below it, the violation
is only written to the evidence log (and, if
alerts.log-quiet-violations is on, one line in
the console): nobody is bothered for one odd
moment. With weight 8 and alert-threshold 16 it
takes two violations close together.punish-threshold from how many points of THIS check upwards its
points count for AUTOMATIC punishments (the
ladder in config.yml, off by default).
0 = NEVER. The checks that only corroborate
(click rhythm, packet order, walls, aim...) are
at 0: on their own they never punish anybody.decay how many points this check loses per minute.
One violation of fly (8 points) is forgotten in
one minute at 8 per minute.setback (movement checks only) when the alert threshold
is reached, move the player back to the last spot
where they were standing, clean. It is not a
punishment: the impossible movement is simply
undone. See setbacks in config.yml.The global "sensitivity" in config.yml (relaxed / balanced / strict) multiplies every buffer and every threshold at once, so you rarely need to touch these numbers one by one.
The buffer is the parameter that matters, and almost nobody looks at it
If you have a false positive, nine times out of ten the answer is RAISE THE BUFFER (or the alert-threshold), not raise the limit.
Keep reading
The limit says "what is impossible". The buffer says "how many times the impossible has to happen before we believe it". Raising the limit makes the check blind to real cheating too; raising the buffer only makes it slower, and somebody cheating keeps doing it for many ticks in a row.
The buffer drops by engine.buffer-decay (0.25) on every clean sample and rises by 1 on every overshoot: somebody cheating intermittently still accumulates.
To see what a check sees, live, while a player does something:
/shadowblocker debug <player>
Every overshoot is printed with the measured value, the limit and the buffer - before it becomes a violation.
disabled-worlds
The list of worlds where that check does not run. It is there for the build world, for the minigame world, for the one where a plugin moves people its own way. Better than switching a check off everywhere because of a single world. (To switch EVERY check off in a world, use exemptions.disabled-worlds in config.yml.)
config-version1On update this file is moved to the new version automatically: every threshold you tuned is carried over, new checks arrive with their comments, the old file is kept in backup/<date>/, and anything that needs your attention is listed in PORTING-<version>.txt.
checks19 keysMovement
Every movement check measures the CLIENT'S own ticks (the client closes each tick with a packet since 1.21.2), ignores the moves that arrive between a teleport and the client's confirmation (any teleport: /tp, /rtp, elevators, portals, plugins), opens a window when the server pushes the player (knockback, explosions, wind charges, launch pads, velocity set by plugins) and reads the player's REAL attributes (gravity, jump strength, movement speed, step height, safe fall distance, scale) instead of the vanilla constants. The vertical checks only judge a move when the world snapshot describes exactly the point the move starts from.
checks.fly14 keysDETECTS: every tick's upward motion compared with what the game's gravity allows, and players who hang in mid-air without rising or falling. The formula is the game's real one, with the player's gravity and air-drag attributes: next_dy = (current_dy - gravity) * drag
Keep reading
FALSE POSITIVES: anything that changes the physics - water, ladders,
cobwebs, slime, honey (also on its side), powder snow, bubble columns, scaffolding, elytra, riptide, levitation, slow falling, vehicles, pistons, wind charges, knockback, teleports, chunks the client has not received yet. They are ALL already excluded. What is left are plugins that move players their own way without a teleport or a velocity (grappling hooks, lifts): if you have one, either raise 'tolerance' or switch the check off in the worlds where it runs.
enabledtrueweight8buffer4disabled-worlds[ ]alert-threshold16punish-threshold4040 = five violations close together before it can count for an automatic punishment: flying is never a one-tick event.
decay8setbacktruetolerance0.03How much is allowed beyond the prediction, in blocks per tick. 0.03 is a tenth of the difference between a jump and a slow flight.
ping-allowance-per-100ms0.01How much to widen the tolerance every 100 ms of latency, and the ceiling.
max-ping-allowance0.06jump-grace-ticks2For how many ticks after leaving the ground a jump's push (0.42 blocks, or the player's jump strength) is allowed anyway.
Keep reading
It is needed because the world snapshot is one tick behind: there is an instant where we see "not on the ground any more" while the previous movement was still zero, and without this margin EVERY JUMP of EVERY PLAYER would be a violation.
Do NOT raise it. Every extra tick is a tick in which a flight rising by 0.42 is indistinguishable from a jump: at 20 it would become invisible.
hover-tolerance0.005Below how much vertical movement counts as "still in the air".
hover-ticks14For how many ticks in a row. In mid-air it is impossible by construction: even at terminal velocity you drop 3.92 per tick.
checks.speed17 keysDETECTS: the AVERAGE horizontal speed over a short window, and separately the instant displacements that have no explanation. The average is what is looked at because a single fast tick is normal (the tick of a sprint-jump moves 0.6 blocks), while a speed cheat stays on. The limit is multiplied by the player's REAL movement_speed attribute (which already contains Speed, Soul Speed and anything a plugin added), by 'ice-multiplier' on ice, slime or slippery ground (and for 2 seconds after leaving them), and by Depth Strider in water.
Keep reading
FALSE POSITIVES: plugins that push players without a velocity or a
teleport (grappling hooks, dashes done by moving the player directly) and custom mounts. Speed effects and attributes, ice, slime, dolphins, knockback, teleports, elytra, riptide, horses and boats are already accounted for. If a plugin grants speed in some other way, raise 'base' instead of switching the check off: switched off it does not even see somebody moving at ten blocks per tick.
enabledtrueweight6buffer5disabled-worlds[ ]alert-threshold18punish-threshold36decay8setbacktruebase0.45The honest maximum AVERAGE on normal ground, in blocks per tick. Walking 0.215 - sprinting 0.28 - sprint-jumping on flat ground averages 0.365, and the worst honest case is sprint-jumping under a ceiling 2.5 blocks high (the jump is cut short and the sprint push comes every six ticks): 0.42. Simulated with the game's own 26.2 movement code. 0.45 stays above that on purpose. Values below 0.43 flag honest players who sprint-jump in low tunnels.
speed-effect-per-level0.20How much each level of Speed adds, as a fraction. Only used when the movement_speed attribute cannot be read: the attribute already includes the effect.
ice-multiplier2.6Ice, slime and slippery ground (friction attribute) are the worst case of all: on blue ice sprint-jumping gets close to 0.48 per tick. 2.6 is generous on purpose; it also covers the momentum carried for two seconds after leaving the ice.
depth-strider-per-level0.15soul-speed-multiplier1.30ping-allowance-per-100ms0.02max-ping-allowance0.10instant-multiplier5.0How many times over the threshold a single tick has to be to be flagged straight away, without waiting for the average. It is there for jumps through space, not for speed.
min-samples10How many samples are needed before an average means anything.
checks.jump11 keysDETECTS: how far somebody has risen since the normal physics last started again (the ground, or the moment they left water, a ladder, a bubble column, an elytra flight...). It sees the cheats that respect gravity tick by tick but restart it: double jump, high jump, mid-air bounce. A jump with no effects rises 1.2522 blocks; the allowed height uses the player's jump strength and gravity, and the upward speed they had when the normal physics started.
Keep reading
FALSE POSITIVES: slime, beds, bubble columns, wind charges, pistons,
horses, knockback and teleports are already excluded. A plugin that applies an upward push WITHOUT going through Bukkit's velocity API is not: in that case switch the check off in that world, or raise 'tolerance' until it covers the highest push that plugin gives.
enabledtrueweight8buffer3disabled-worlds[ ]alert-threshold16punish-threshold40decay8setbacktruebase-height1.35The maximum height of a jump with no effects, with a little margin. The same margin (base-height minus 1.2522) is added on top of the height computed from the player's real jump strength and gravity.
per-jump-level0.80How much each level of Jump Boost adds.
tolerance0.10checks.climb10 keysDETECTS: players who keep rising against a wall long after a jump's ascent would be over, with no ladder or vine anywhere. It is the cheat known out there as "spider".
Keep reading
FALSE POSITIVES: a climbable block invented by a plugin, which
Minecraft does not consider climbable. Real ladders, vines, scaffolding and powder snow are already excluded, and a jump with Jump Boost (or a stronger jump attribute) gets the extra ticks of ascent it really has. If you have blocks like that, switch the check off in that world.
enabledtrueweight7buffer6disabled-worlds[ ]alert-threshold21punish-threshold42decay8setbacktrueair-ticks8From how many ticks in the air onwards it looks, at least. A jump's ascent lasts six ticks; with Jump Boost or a stronger jump the real number of rising ticks plus two is used when it is higher.
min-rise0.02Below how much rise it does not look.
checks.phase10 keysDETECTS: players whose box overlaps the real collision shape of a solid block for many ticks in a row WHILE STILL MOVING, which is how a wall gets walked through.
Keep reading
FALSE POSITIVES: the most delicate check in the plugin. Doors and
trapdoors closing on someone, pistons, blocks placed on their head, chunks the client has not received yet, blocks refused by a protection plugin, vehicles inside walls. That is why it takes MANY ticks in a row AND movement in progress: someone who ends up in there by accident stands still and is pushed out at once. Walking on soul sand, mud or farmland is NOT a problem any more: the check compares with the block's real shape. Raise it further and you will see nobody; lower it and you will see everybody.
enabledtrueweight5buffer8disabled-worlds[ ]alert-threshold15punish-threshold00 = never on its own: it corroborates the other checks.
decay10setbacktrueMoving the player back out of the wall is the right answer here.
ticks12How many ticks inside the block before saying anything.
min-movement0.02Below how much movement it counts as "stuck" rather than "walking through". You get pulled out of being stuck, you do not walk through it.
checks.nofall10 keysDETECTS: the "I am on the ground" flag sent by the client, checked against what the server sees at that exact spot (blocks, or entities you can stand on such as boats and shulkers) after a fall we measured ourselves. In Minecraft fall damage is worked out by the CLIENT: a modified client that keeps that flag always true never takes damage.
Keep reading
FALSE POSITIVES: a block the client sees but the server refused (a
placement cancelled by a protection plugin opens a short window), chunks the client has not received yet, and plugins that reset the fall their own way (lifts, feathers, parachutes). The claim is verified with the world snapshot taken AFTER the landing, never with the one from the tick before.
NOTE: this check only works with the packet layer switched on. Without it, the client's flag never reaches us and the check has nothing to compare.
enabledtrueweight10buffer4disabled-worlds[ ]alert-threshold20punish-threshold50decay6setbackfalseA setback would not undo anything useful here: the fall is over.
min-fall-distance3.0From how many blocks of fall onwards. Below three the damage is zero anyway, so there is nothing to gain by lying. The player's safe_fall_distance attribute is used when it is higher.
margin1.0Blocks added on top of that, for the rounding of the fall we measure ourselves.
Combat
Detects: the distance between the attacker's eye and the target's
HITBOX. The range is the player's entity_interaction_range
attribute (3.0), or the weapon's own range (spears reach 4.5). Latency is compensated by looking at where a player target ACTUALLY was inside the attacker's ping window plus the client's interpolation, taking the position most favourable to the accused; for mobs, by how fast they were moving.
Keep reading
FALSE POSITIVES: latency, already compensated (the ping used is the
highest of the last seconds, not the average). Riding on either side gets an extra margin; the ender dragon is not measured. What is left are plugins that teleport during a fight. If you must change something, raise 'tolerance' one tenth at a time and watch what stops being flagged: if everything stops, it was too high.
checks.reach15 keysenabledtrueweight7buffer5disabled-worlds[ ]alert-threshold21punish-threshold42decay8max-distance3.0The game's range in survival. The player's attribute or the weapon range are used instead when they are longer.
tolerance0.40The fixed margin: it covers the widening of the hitboxes and the floating-point rounding.
ping-allowance-per-100ms0.15max-ping-allowance0.60compensation-window-ms1000How far back to look in the trail of the target's positions, at most. The real window is the attacker's ping + interpolation-ms + one tick; this is only the ceiling, so that somebody with five seconds of latency does not get five seconds of positions to pick the most convenient one from.
interpolation-ms150How long the client takes to show an entity at its new position (it slides it over a few ticks). Part of the compensation window.
max-velocity-compensation1.5For mobs (which have no position trail): at most how many blocks of their own movement during the delay are given as margin.
mounted-tolerance1.0Extra margin when the attacker or the target is riding something.
Detects: three things. Clicks per second (the least useful), how
REGULAR the intervals are (the most useful), and how many intervals
are exactly identical. Only real clicks count: the arm swings of mining and of right-click actions (placing blocks, throwing, using tools, feeding animals) are recognised and left out.
Keep reading
FALSE POSITIVES: drag clicking and butterfly clicking go past twenty
clicks per second WITH no program at all - that is why 'max-cps' is high and counts for little. A mouse with a faulty double click can generate identical pairs: if a player is flagged only here and never anywhere else, look at their mouse before their intentions. This check alerts the staff and never punishes on its own.
checks.clicks12 keysenabledtrueweight5buffer4disabled-worlds[ ]alert-threshold15punish-threshold00 = never on its own: the click rhythm is the weakest clue there is.
decay10max-cps30The absurd number. High on purpose: a low threshold here catches the good players (drag clicking goes past 25), not the cheats.
window-seconds8Only the clicks of the last this-many seconds are analysed: a judgement given at a hit looks at how the player clicks NOW.
min-samples40Below how many samples nothing is ever said. On five clicks everything looks regular.
min-deviation-ms8.0The minimum standard deviation of the intervals, in milliseconds. A person stays in the tens of milliseconds even at full speed.
checks.angle9 keysDETECTS: the angle between where they were looking and the NEAREST point of the target's box at the moment of the hit. Minecraft's client does not even let you try to hit somebody you are not looking at: it is not "unlikely", it is something a normal client cannot send.
Keep reading
FALSE POSITIVES: tight melee, where one tick of delay is worth tens
of degrees - below 'min-distance' nothing is looked at. Big mobs hit on a corner are fine (the nearest point of the box is used), non-living targets and the ender dragon are skipped. What is left are plugins that deal damage on behalf of a player without them having hit anything (traps, area abilities, summons).
enabledtrueweight9buffer3disabled-worlds[ ]alert-threshold18punish-threshold45decay8max-angle85.0Beyond how many degrees. 85 is almost sideways: an honest client always stays under twenty.
min-distance1.2Below how much distance the angle means nothing.
checks.wallhit8 keysDETECTS: a hit landed on somebody who had a block in the way from every point of view we try.
Keep reading
FALSE POSITIVES: corners, thin pillars and half-open doors, because
the ray is traced now while the hit was decided one ping ago. Rays go to several points of the target's box (and, for players, to where they were inside the ping window) with both the collision and the outline shapes of the blocks, so a cow hit over a fence or a mob seen through a gap is fine. It still takes several hits in a row. Watch out for transparent blocks the game treats as full.
enabledtrueweight6buffer6disabled-worlds[ ]alert-threshold18punish-threshold00 = never on its own.
decay10min-distance1.0Detects: two very different things
'snap' the head snapping around at the instant of the hit,
starting from a head that was still the tick before. A
hand aims over several ticks, accelerating and braking;
a program gets there in one.
'pattern' the step of the rotations. A real mouse ALWAYS produces
multiples of the same number, because it counts whole
steps. What is measured is what FRACTION of the rotations
sits on that grid: a mouse is near 1, a program well
below. It is the strongest signal there is.Keep reading
FALSE POSITIVES:
snap: players with very high sensitivity who aim with a flick of
the wrist (that is why it needs a still head before the
snap). Rotations forced by the server (teleports, plugins)
are ignored.
pattern: ANYONE PLAYING FROM BEDROCK THROUGH A BRIDGE, ON A
CONTROLLER, OR WITH A MOD THAT SMOOTHS THE CAMERA. All of
them sit off the grid and would ALL be flagged.That is why 'pattern' is OFF by default. Only switch it on if you know that on your server people join from Java with a mouse and nothing else.
checks.aim14 keysenabledtrueweight6buffer5disabled-worlds[ ]alert-threshold18punish-threshold00 = never on its own.
decay10snaptruesnap-window-ms120max-snap-degrees45.0snap-still-before-degrees5.0The head must have turned by at most this much in the tick BEFORE the snap. A human flick is already moving the tick before.
patternfalseRead the paragraph above before touching this.
pattern-min-samples20Interaction
checks.fastbreak9 keysDETECTS: how long a dig took compared with the minimum that block requires, taking into account the tool, Efficiency, Haste, Mining Fatigue, water and whether the player was in mid-air (which divides the speed by five, and almost nobody remembers it). The dig is measured twice: in milliseconds AND in the client's own ticks, which the network cannot shorten; both must be too short.
Keep reading
FALSE POSITIVES: a plugin breaking blocks on behalf of the player
and blocks destroyed by an explosion (no dig was seen: nothing is said). Jumping, water, Haste and Mining Fatigue changing during the dig are taken the way that favours the player. If you have custom tools that dig faster than the real ones, lower 'max-speed-ratio'.
enabledtrueweight9buffer3disabled-worlds[ ]alert-threshold18punish-threshold45decay8max-speed-ratio0.65Below what fraction of the minimum time it flags. 0.65 = "they took less than two thirds of the minimum possible". No connection gives away a third of the time; a cheat zeroes it.
min-expected-ms150Below what expected time it does not look: blocks that break almost instantly have nothing to measure.
Detects: how many block breaks arrive inside the same server tick
The game allows ONE: to break the next block you have to start over.
With the packet layer on, the digs must also have been sent inside one single CLIENT tick, so server lag never counts.
Keep reading
FALSE POSITIVES: other plugins (explosions, area abilities, tree
fellers, WorldEdit run on behalf of a player) send no dig packets and are therefore not flagged when the packet layer is on. Without it, raise 'max-per-tick' if you have plugins like that.
checks.nuker8 keysenabledtrueweight12buffer1disabled-worlds[ ]alert-threshold24punish-threshold48decay8max-per-tick3checks.interactreach12 keysDETECTS: the distance between the eye and the NEAREST point of the block they acted on. The range is the player's block_interaction_range attribute (4.5 in survival, 5 in creative) or the value below, whichever is longer.
FALSE POSITIVES: the movement of whoever is clicking during the
connection delay, already covered by the margin. And plugins that make a player break or place blocks from far away.
enabledtrueweight6buffer4disabled-worlds[ ]alert-threshold18punish-threshold36decay8survival-distance4.5creative-distance6.0tolerance0.60The fixed margin, in blocks, on top of the range. The distance is measured to the nearest point of the block (it used to be the centre, with 0.9 of margin to cover that).
ping-allowance-per-100ms0.08max-ping-allowance0.35checks.visibility12 keysDETECTS: a block broken or touched behind a wall, and a block acted on while facing the other way. The game allows a normal client neither of those.
Keep reading
FALSE POSITIVES: corners, doors, blocks placed by others in the same
instant and chunks the client sees differently. Rays are traced to fifteen points of the block with both collision and outline shapes, and blocks with no collision (grass, flowers, crops, buttons, levers) are never judged on the wall test. It still takes several cases in a row. Watch out for transparent blocks the game treats as full, and for plugins that make a player act on blocks they are not looking at.
enabledtrueweight5buffer6disabled-worlds[ ]alert-threshold15punish-threshold00 = never on its own.
decay10check-line-of-sighttruemin-distance1.5check-angletruemax-angle90.0angle-min-distance2.0checks.place11 keysDETECTS: a block placed against NOTHING, or placed without looking at it. Those are the two signatures of "scaffold" (the bridge built under your feet while walking over the void) and of "tower".
Keep reading
FALSE POSITIVES: plugins that place blocks on behalf of a player
(rollbacks, regenerations, build tools): in those cases there is no clicked face and the check goes quiet on its own. What is left are blocks that lean on things which are not full blocks: if you see those flagged, set 'require-support' to false and keep only the angle test.
enabledtrueweight7buffer4disabled-worlds[ ]alert-threshold21punish-threshold00 = never on its own.
decay10require-supporttruecheck-angletruemax-angle100.0angle-min-distance1.5Packets
MIND THIS: these three checks have a LOW weight and a HIGH buffer, and that is not an oversight. An odd packet rhythm is not proof on its own.
Keep reading
They are there to CORROBORATE - to explain why a player who was being flagged elsewhere was being flagged elsewhere - not to decide on their own. Raising their weight is the fastest way to ban somebody because of their wifi.
They only work with the packet layer switched on (config.yml, engine.packets.enabled).
checks.rate10 keysDETECTS: a sped-up client clock ("timer"). The client closes every
tick with a packet: each one adds 50 ms to a balance, each real
millisecond takes one away. An honest client stays around zero; a
timer at 25 ticks a second gains 250 ms every second. A connection
that stops and then delivers everything at once is forgiven by the
balance itself: the silence becomes credit that the burst spends.
It also counts floods of packets of any kind.Keep reading
FALSE POSITIVES: very few. /tick rate is followed automatically.
Floods are not counted right after a silence of the connection.
enabledtrueweight3buffer10disabled-worlds[ ]alert-threshold9punish-threshold00 = never on its own: it corroborates speed and fastbreak.
decay10max-ahead-ms500.0How far ahead of real time the client's clock may run, in milliseconds, before it counts. 500 = ten ticks: normal network jitter moves the balance by one or two.
max-lag-credit-ms2000.0How much credit a silence of the connection can build up, in milliseconds. It is what forgives the burst that follows a lag spike. Larger silences are covered by the "connection" window in config.yml anyway; this cap only stops somebody from saving up minutes of ticks to spend later.
max-total-per-second500.0checks.order9 keysDETECTS: packet sequences the client does not produce. TCP reorders nothing, so two packets that arrived in one order were SENT in that order: it is not a hint, it is a fact. What is looked at: actions without the client's game loop running, interactions long after death, digs that end without having started.
Keep reading
FALSE POSITIVES: few. An attack sent a moment before dying arrives
after death with the ping in between: only interactions well after death count. What is left are alternative clients that batch actions differently (Bedrock through a bridge, excluded by default) and plugins that send actions on behalf of a player.
enabledtrueweight4buffer8disabled-worlds[ ]alert-threshold12punish-threshold00 = never on its own.
decay10min-actions8Below how many actions in the window it does not look: a player who has just joined has half-empty windows by definition.
dead-grace-ms1500How long after death an interaction still counts as "sent while alive", in milliseconds; twice the player's ping is added.
Detects: values inside packets that the game never produces
NaN or infinite coordinates, pitch outside the -90/+90 range,
impossible position jumps, positions outside the world. It is the check with the fewest false positives in the whole plugin: nothing is estimated and nothing is tuned here.
Keep reading
FALSE POSITIVES: none known. If you see them on a player, before
thinking of a cheat consider a bridge between different versions or a proxy that rewrites the packets: the result is the same, but the person has nothing to do with it.
checks.field9 keysenabledtrueweight15buffer1disabled-worlds[ ]alert-threshold15punish-threshold30decay5max-jump-blocks100.0world-border-blocks30000000.0Languages
The texts the plugin shows to players, one file per language.
lang/
Texts and messages 2 files
lang/plugins/ShadowBlocker/lang/ File notes
This file holds EVERY word a person reads: chat messages, screen titles, item names and their descriptions. Nothing else. Command names, configuration keys, units written to the database and console lines are English everywhere and are NOT in here - they are data, and data that changes name with the language is not data.
Colours are written with &: &a &c &l &r, and &#FF8800 for an exact colour. The only exception are gradients, which & cannot express:
<gradient:#7E57C2:#4FC3F7>text</gradient>
Placeholders are written <like-this>. The one you will meet most often is <label>: it is filled with the command the server ACTUALLY granted us. Never write "/sbl" by hand: if that command already belonged to another plugin, ours is called "/shadowblocker:shadowblocker", and a hardcoded message would send a staff member straight into somebody else's command.
A message can be a plain string or a map:
key: text: "&cText" channel: ACTION_BAR # CHAT | ACTION_BAR | TITLE | SUBTITLE | NONE sound: block.note_block.bass cooldown: "2s" # per-player anti-spam
channel: NONE silences a message without deleting the key. A key missing here is read from the copy of this language inside the plugin jar, then from English, one key at a time.
Every message the plugin shows to players, one file per language. The keys are not listed one by one: here are the files, how many texts they hold and the placeholders they use.
| File | Language | Texts |
|---|---|---|
lang/en_US.yml reference | English | 419 |
lang/it_IT.yml | Italiano | 419 |
Most used placeholders
The plugin replaces them with the right value: leave them written as they are.
<prefix><label><player><id><reason><action><check><family><text><name><count><value><state><measured>
Message groups
prefix1common10command4unit25exempt20check76alert23punish2narrator28evidence32admin115gui36license43locked4