Sueta

BS-AntiCheat

1.1.2

Movement, combat, world, inventory and packet-level cheat detection that's actually tuned not to flag your legit players. Install it, and your builders, miners and PvPers keep playing , while cheaters

·Plugin
  1. 1.1.2latestBS-AntiCheat 1.1.22 days ago

    [1.1.2] - 2026-10-24

    Added
    - alerts.notify_min_vl: chat and Discord alerts only from this violation level on (default 1 = every alert); everything is still logged and listed in /movealerts.
    Fixed
    - Saving the whitelist, ore exclusions or silent mode no longer overwrites edits made to config.yml by hand since the last reload. - Placed ores are remembered in the chunk and survive restarts, so mining one's own ores is never counted as X-Ray. - PacketFlood/Crasher no longer log exempt players; no log entries are lost at shutdown while a database write is running. - /bsac test completes every check type. - Piston elevators that lift a player any height within one tick no longer raise TELEPORT. - Climbing on after an elytra glide ends in mid-air no longer raises FLY (free fall); hovering is still caught.
    14 downloads
  2. 1.1.1BS-AntiCheat 1.1.12 weeks ago

    [1.1.1] - 2026-10-09

    Fixed
    - Timer no longer flags a connection that slows down and then catches up, even when every tick is only slightly late: late ticks are owed back by the backlog, and a backlog draining at network speed keeps its catch-up window open. - AutoClicker no longer reads a held button as 26 CPS while a packet backlog drains. - Fly/Speed no longer flag a player whose flight ended in mid-air (leaving creative, /fly off, a world forcing survival on join) until they land, at most 30 seconds.
    640 downloads
  3. 1.1.0BS-AntiCheat 1.1.02 weeks ago

    [1.1.0] - 2026-10-07

    Added
    - Minecraft 26.1–26.3 support: new attack and punch packets (PacketEvents 2.14), spear reach, Lunge, wind charges, bouncy blocks and geysers, new mounts and physics attributes. - New checks: NoFall, Sprint (hunger/blindness/omni-sprint), Mace smash, CrystalAura/AnchorAura, PingSpoof and extended BadPackets.
    Fixed
    - Closed detection bypasses: ender pearl/chorus teleport grace, Timer pauses, packet bundling, oscillating or slowly sinking Fly/BoatFly, speed pulsing, dig-packet AutoClicker suppression, spoofed ground flag for InventoryMove, uncapped ping allowance. - Fewer false positives: Jesus at shores and piers, flight/gamemode changes, fast-moving reach targets, vein-miner bursts (Nuker/FastPlace), team-fight KillAura, X-Ray in overgrown caves and dense veins, knockback in cobwebs/powder snow, riptide while gliding. - Violation levels now survive a relog; punishment commands run in order on the correct Folia thread. - PerformanceAnalyzer migration no longer imports Discord or silent-player settings into fresh installs; foreign keys from earlier imports are removed. - Discord alerts share one rate limit per webhook and retry on HTTP 429; fallback log is flushed periodically.
    Changed
    - Timer check uses the client tick-end packet; lag detection uses a short window (per region on Folia). - Update notice shows clickable Modrinth and CurseForge links to operators on join. - Validation for all numeric config values; clear --db deletes by player UUID. - Console messages are English throughout; /acsilent category names come from the language files. - Various smaller thread-safety, memory and performance fixes.
    API
    - ViolationEvent now fires on the thread owning the player (region thread on Folia), one tick later than before.
    104 downloads
  4. 1.0.6BS-AntiCheat 1.0.6Sep 7, 2026

    [1.0.6] - 2026-09-07

    Live alerts from two production servers, worked case by case, and then a review pass over what that work left behind. Every false-positive entry below started as an alert somebody looked at and said "that is not what happened" — and in most of them the check was not merely mistuned but measuring the wrong quantity. Three of the fixes here replace a model rather than a threshold, and two proposals were built, measured against the servers' own history, and then thrown away because the data did not support them.
    Fixed — false positives
    - Water movement is judged by water physics. isSwimming() is only true in the horizontal swim pose. A player wading upright, surfacing, or carried by Dolphin's Grace is in water without ever entering it, fell through to WALKING, and was measured against the 0.4 dry-land cap — while the Depth Strider and Dolphin's Grace handling sat in a branch that was never reached. Live case: twelve SPEED alerts of 0.42–2.12 b/t, all labelled "walking", from a player with Depth Strider III and Dolphin's Grace. The fix applies the water cap as a floor on the on-foot cap, and deliberately does not reclassify the movement: the type gates far more than the speed limit — Step, Spider, GroundSpoof and the hover checks all require an on-foot type — so routing every in-water player to SWIMMING would have switched those off for anyone standing in a puddle. That was the first attempt, and it is the reason for the extra test that holds it shut. - Holding the drop key is not clicking. Every item dropped rides on a PLAYER_DIGGING packet and is accompanied by an arm swing, so holding Q emits one per tick and read as 26 CPS. Only the mining actions of that packet were excluded. The drop actions now suppress the swing too, through a deliberately short 200 ms window — mining's five-second safety cap would have turned one dropped item into a five-second free pass for a real clicker. - A thick vein counts as one find, not as its block count. The per-ore X-Ray threshold and the combined rare-ore count are block counts, and the hidden-ore test makes that worse rather than better: every block of a vein after the first is exposed by breaking its neighbour, so an entire deposit reads as hidden. The xray_min_veins gate was meant to prevent exactly this and could not, because the number it guarded stayed block-based. Live case: two fat deepslate veins produced 13 of 14 "hidden" diamonds and flagged an OP who had moved 3400 blocks of stone that hour. Each deposit now contributes at most xray_max_count_per_vein. - Strip mining is recognised by its shape. A new veto stands the counting checks down when the digging looks like a branch mine and the ore came out of the level it runs through. Calibrated against 4.5 months of this server's block log: honest strip miners hold one Y level to a standard deviation of 1.1–1.2, run 75–89 % of their digging as straight corridors, and take their ore from a 3.0–3.6 block band around them. The ore-band requirement is what keeps it from becoming a hiding place — without it a player could strip-mine honestly for ten minutes and have the veto cover the detours that were worth hiding. - The Timer check no longer punishes a bad connection. A link recovering from a stall delivers its backlog faster than one packet per tick but never all at once, so no single gap reaches PACKET_GAP_MS and the stall grace never engages while the balance climbs the whole way. Live case: a player at 1275–1444 ms round trips reached a balance of 779 ms and was flagged. Neither the height nor the duration of that excursion separates it from a hack — what does is that a catch-up drains and plateaus while a hack keeps gaining. The balance must now still be growing at the end of the excursion, and the required duration is extended by the measured round trip. The window is stretched, never the limit: a cheat that answers transaction pings late to fake latency then only delays its own flag instead of escaping it. - A ballistic arc is not hovering. The hover check counted any sample whose vertical speed stayed inside a ±0.08 band and never asked whether that speed was changing — but the apex of a slow arc sits inside the band for many samples in a row. Live case: dy ran +0.067 to −0.051 monotonically across all ten samples, with no potion, no gravity modifier and no teleport behind it (all three checked, not assumed). A hover holds its vertical speed; gravity keeps taking it away. checkSustainedAscent already judged motion this way — it asks whether a climb decays — and the hover check now does too. - Combat checks have a teleport and join grace. Every other check family had one: MovementChecker keeps recentTeleport and recentJoin, PacketChecker keeps graceUntil. Combat had none, so a hit landing while a teleport was still in flight was measured against a position no client involved had yet. It applies to both sides — being teleported yourself desyncs your view of everyone, and a target arriving next to you desyncs theirs. - The same swing is no longer counted several times. One hit does not always produce one event: item plugins that add their own damage fire EntityDamageByEntityEvent repeatedly with identical geometry. Measured here: of 327 hit groups, 83 arrived twice, 76 three times and 4 four times — same attacker, same victim, same aim angle to the degree. Every streak requirement in the combat checks was defeated by that, so a reach_violations of 3 was satisfied by a single over-reach hit. Deduplicated within one tick, before any streak is touched; weapon cooldowns put genuine hits hundreds of milliseconds apart. - Ranged item abilities are no longer judged as melee hits. This was the largest source of REACH alerts and it is not a threshold problem. MythicLib delivers ability damage as LivingEntity.damage(amount, player), which Bukkit reports as ENTITY_ATTACK with the player as damager — the exact shape of a melee hit, from wherever the ability reaches. Live case: a sword whose right-click fires a VOID_ZAPPER beam with length: 10.0; all 17 of that player's REACH alerts (4.14 to 9.37 blocks) fell inside that range, and the server had 21 such right-click abilities configured. A real melee hit is announced by the client with an INTERACT_ENTITY/ATTACK packet; ability damage has none, because the player never attacked anything. Damage without that packet is now skipped by all combat checks, not just reach: aim angle and multi-target are equally meaningless for a beam, which hits what it covers rather than what is aimed at, and which reaching three targets at once is exactly the "3 targets in 250 ms" that was being reported. - Reach latency is modelled as a distance, not a percentage. pingSlack scales a limit, which is right for a speed (blocks per tick × slack) and wrong for a distance: what latency costs here is however far the target travelled while the hit was in flight, and that is speed × time. Measured in a four-way PvP session at 373–1519 ms round trips — the old model allowed 4.7–5.5 blocks while a sprinting pair separates by 4.2 to 17.0 in one round trip, and the alerts ran to 14.61, a figure the multiplicative model cannot produce and ordinary play can. The allowance is now additive and capped, and above reach_max_ping_ms the check **stands down** rather than compensating further: past that the server cannot tell where either player was, and a hole that grows with latency is one a cheat can open by answering pings late. - Muted alert categories survive a disconnect. Persistent preferences are read from the config once, at startup, so anything dropped from memory on quit was gone for the rest of the server's uptime. cleanup() matched the stored entry with an exact-string test, which only
    348 downloads
  5. 1.0.5BS-AntiCheat 1.0.5Aug 20, 2026

    [1.0.5] - 2026-08-20

    A code review pass, then two days of live alert data with debug_mode on. 1.0.4 fixed the elytra speed check's assumption that one move event equals one tick; this release finds the same assumption in the two places it was left standing, and closes several holes of the same shape — a check whose evidence never expired, a guard that covered less ground than it claimed, state that was only maintained on the paths that reached the end of the method, and a rate that measured the network rather than the player.
    Fixed — false positives
    - A move event is no longer treated as one tick. The ground speed and vertical checks compared a per-event delta against a per-tick threshold (0.4 blocks walking, 3.5 vertical). A move event is not reliably one tick: a client on a poor connection delivers one event carrying several ticks of travel, and judging that as a single tick multiplies the apparent speed by however many ticks were bundled. This is exactly the mistake 1.0.4 removed from the elytra check, where live data caught it as a 110→100 b/s "over-speed" curve from a routine rocket flight; the ground checks carried it unchanged. The delta is now divided by the ticks it actually spans, clamped at one so a sub-tick event is never scaled up. - The catch-up move after a packet gap is no longer judged. Per-tick scaling handles the ordinary case but cannot rescue the pathological one: a stalled connection flushing seconds of backlog produces a single delta large enough for the TELEPORT threshold, which no division makes innocent. A gap over 400 ms now leaves that one move unjudged — the movement counterpart of the rule the Timer check has had since 1.0.4. - Vehicle speed is measured over real elapsed time. VehicleChecker derived blocks per second from one move event × 20, the same assumption again, and a boat crossing loading chunks on an ice highway is precisely the case that breaks it. Speed is now averaged over a 250 ms window, so distance and elapsed time grow together. - AutoClicker measures the click rate, not the packet arrival rate. There are two ways to read a rate off arm-swing packets, and both fail upwards — in different situations. Counting arrivals in a sliding second puts the network in charge of the answer: a connection that delivers a tick's swings in bundles makes the count read whatever the bundling lines up with. Taking the typical interval instead is immune to that, but an interval is not a rate: it spans the whole consistency window, so a short fast burst fills the window with burst intervals and reports the speed inside the burst as though it were sustained. Both were observed in the field. A player placing blocks was flagged at 26 CPS while the interval median sat at exactly 50.0 ms — one server tick, the held-button cadence — through the entire run-up; the MAD of 49 ms against that median describes a bimodal arrival pattern, half the intervals near 0 ms and half near 100 ms, which is bundling and which also broke the held-button exclusion, since that wants the spread tight. Separately, three alerts of 29–30 CPS had arrival counts for the same second of 5, 8 and 10 — a handful of quick clicks in a row, which is an ordinary thing to do. The rate is therefore the smaller of the two estimates. Neither can fall below the true rate, so the minimum is safe in both directions: bundling is capped by the median, a burst is capped by how many clicks actually arrived, and genuinely sustained fast clicking raises both and is still caught. Note that raising autoclicker_max_cps does not help against either artefact — the run-up went through 22, 23, 24 and 25 without pausing. - Rising is no longer counted as hovering. The sustained-hover check counted every sample that was not falling, which made a climb indistinguishable from hanging in the air. Live data: four alerts fired while the player was moving UP at 0.12–0.20 b/t in a Trial Chamber — a Breeze, a wind charge or a Wind Burst mace throws a player upwards for far longer than the 2 s knockback grace lasts, and the tail of that arc is a slow climb with nothing underneath. Hovering now means what the name says: vertical movement inside one tick of gravity (±0.08 b/t). Falling resets the counter as before; rising does too. - Nuker and FastPlace evidence now expires. Both ask for several one-second windows over the rate limit before flagging, so that one bundled window — a vein miner, a burst of place packets — is not evidence. But the counter could only be reset by flagging: a window that stays under the limit produces no event on that path at all. The count therefore never came down, and unrelated bursts spread across a session added up until the third one flagged. The windows must now fall within 10 s of each other to count as consecutive, which is what "the rate was held up" was supposed to mean. CombatChecker already solved this the same way; these two were the ones that had not. - The unloaded-chunk guard now covers the neighbourhood it reads. It checked the chunk the player stands in, but the checks behind it read the player's neighbours: the ground scan samples all four footprint corners, the fall-slowing and liquid exemptions look sideways, and Spider/Jesus look at the walls. On a chunk border those reach into the next chunk, where an unloaded result reads as "nothing below the player" — and forces a synchronous chunk load from inside a movement handler, which is what Folia forbids. The guard now covers one block of margin in every direction, and the vertical checks stand down (dropping their streaks rather than resuming them across the blind spot) when it is not satisfied.
    Added
    - Sustained ascent (opt-in, off by default). Narrowing the hover band leaves a slow steady climb unwatched, so anticheat.sustained_ascent_detection covers it — by the one thing that separates thrown from flown. A ballistic rise sheds about one tick of gravity of vertical speed every tick and ends within a second or two; a climb that does not decay is not one anything threw. It ships off because it is a heuristic with no live data behind it yet: turn on debug_mode, watch the [ASCENT-DEBUG] decay values on your own server, then enable it.
    Fixed — other
    - The plugin no longer overwrites a `config.yml` it did not change. The config was saved on every shutdown regardless, which made the plugin the last writer of a file it had not edited. An admin who edits a threshold while the server runs and does not run /bsac reload had that edit silently replaced by the stale in-memory value at the next stop — on a server with a scheduled restart twice a day, within hours, with nothing in the log to explain it. Saving is now tied to the plugin actually having something of its own to write: the whitelist and ore-exclusion commands, and the validator repairing an invalid value. Everything else on disk stays the admin's. /bsac reload is still what makes an edit take effect on a running server; it just no longer costs you the edit if you forget it. - Log rows carry the time of the violation. The ts column was left to CURRENT_TIMESTAMP, which stamps the moment the row is INSERTed. Entries are batched and flushed every 30 s, so every alert in a batch shared one timestamp, up to half a minute after the fact — visible in the data as clusters of rows on the same second. That is exactly the column needed to line an alert up against the server log. The detection time is now recorded with the entry and written explicitly, in the same UTC format the default produced, so existing rows and queries are unaffected. The fallback logger, which receives whole batches at once during a database outage, had the same problem and takes the same timestamp. - *Setback no longer teleports to a stale position.
    82 downloads
  6. 1.0.4BS-AntiCheat 1.0.4Aug 15, 2026

    [1.0.4] - 2026-08-15

    False-positive fixes derived from live alert data on the Rattenkolonie server. Over one hour of ordinary play, one player produced 54 alerts across six checks — every single one of them wrong. Each fix below is tied to the alert pattern that exposed it.
    Fixed — false positives
    - Towering up is no longer Fly. Placing a block under your own feet and landing on it produces exactly the hover signature the check hunts for: the fresh block catches the player before gravity shows, so the fall the check waits for never comes, and at the apex of each jump the previous solid block has already dropped out of the support scan. A block placed within 3 blocks below and 1.5 sideways of the player's feet now suppresses the hover counter for 1.5s. The vertical-burst and GroundSpoof checks stay armed — pillaring produces neither a 3.5-block jump nor a player genuinely high above ground. *Live data: 31 hover alerts climbing Y 91→302, against 752 clay blocks placed and removed over the same Y range in the same minutes.* - Cobwebs, powder snow and honey walls are no longer Fly. All three slow a descent below -0.08 blocks/tick, which the hover check does not count as falling, so the counter keeps climbing while the player is in fact sinking. isSupportive() does accept cobweb and powder snow as footing, but supportDepth() only ever scans DOWNWARD from the feet — a web holding the player at body height above a cave, or a honey block on the wall beside them, is never seen, and clearlyAirborne becomes true. Both halves of the hover signature, from standing in a mineshaft. Slow Falling was already exempt; these do the same thing physically and now are too. *Live data: two hover alerts at Y −14, each within a few blocks of a cobweb the player broke in the same second.* - Riptide momentum is no longer Speed. Vanilla clears isRiptiding() after the ~0.5s animation while the player is still travelling several blocks per tick, dropping them into the walking check at a 0.4 b/t cap. The post-glide grace is now 3s for riptide (elytra keeps 1.5s, where momentum drops fast). *Live data: SPEED alerts of 1.52 / 1.03 / 0.72 b/t — a decay curve — at the same second and coordinates as RIPTIDE alerts.* - Elytra/Riptide speed is measured over real elapsed time. The check derived b/s from a single move event × 20, assuming one event equals one tick. It does not: a packet gap while flying over loading chunks delivers one event carrying several ticks of travel, and multiplying it by 20 invents speed that was never flown. Speed is now averaged over a 250 ms window, so distance and elapsed time grow together and a gap is harmless. Live data: a tidy 110→100 b/s "over-speed" curve from a routine rocket flight. - Elytra and Riptide ceilings raised to what vanilla actually does. Riptide III launches at 3 blocks/tick = 60 b/s by design, but the ceiling was 50; rocket-assisted elytra dives legitimately reach 100–120 b/s against a ceiling of 100. Now 75 and 140. - Holding the mouse button is no longer AutoClicker. The held-button exclusion was keyed off the CPS count (accepted 18-22), and flagging began at 23 — two ranges meeting without overlap. But CPS is counted in a sliding window over packet ARRIVAL times: the client sends exactly one swing per tick (20/s), and network jitter bunches those arrivals so the window reads 21-23 while nothing about the clicking changed. Held swings then fell into the gap between "recognised as held" and "flagged". The exclusion now identifies a held button by the rate its swings arrive at — the median interval sits at one tick (50 ms) however much individual arrivals jitter — instead of by how many land in a window. A hand clicking at 23 CPS has a ~43.5 ms interval and is still not excluded, so closing the false positive did not open a hiding place. Median and MAD are used rather than mean and standard deviation, because a single pause between swings shifts a mean and explodes a deviation while leaving the median where it is. *Live data: four alerts, all reading exactly "23 CPS (Max: 22)", with no block broken at the time — swinging with nothing in reach, which the START_DIGGING-based mining exclusion never covered.* - `autoclicker_max_cps` raised from 22 to 25. Butterfly clicking reaches 20-25 CPS by hand, so the old cap sat inside human range. - Pistons no longer read as Speed, Fly, GroundSpoof or InventoryMove. A piston displaces a player without applying velocity — it fires no PlayerVelocityEvent — so the knockback immunity every other check relies on never engaged. The player is simply somewhere else next tick, which reads as movement they made themselves. Piston elevators, flying machines and door mechanisms all produce it. A new PistonTracker records where pistons fired into a bounded ring buffer; the checks consult it only on their would-flag paths, so a clock circuit costs nothing but an append. - Timer no longer fires on a stalled connection. A gap in the packet stream means the client stopped sending; what follows is it flushing the backlog, and every queued packet credits a full tick with no real time attached, so the balance climbs by the whole backlog at once. The existing sustained-excursion rule does not help — an eight-second backlog is not paid off in a few hundred milliseconds either. A gap over 400 ms now discards the balance and leaves the catch-up unjudged for 3 s. *Live data: two TIMER alerts, one of 8284 ms, inside the minutes a player was timing out and reconnecting.* - InventoryMove no longer fires on momentum. Vanilla friction needs several ticks to bring a sprint below the 0.15 threshold, and the player steers none of them. Judging now waits 1 s after the container opens, and skips airborne players entirely — walking off a ledge carries horizontal speed the whole way down with no key input behind it. Live data: three alerts measuring 0.150 / 0.165 / 0.278 against a 0.15 threshold. - Slime launchers no longer read as Speed. The slime grace was computed inside the vertical block, so only the fly checks ever saw it — but a slime launcher throws a player sideways just as readily, and the bounce needs no key input either. - Dismounting at speed is no longer Speed. Leaving a galloping horse or an ice boat hands the player its momentum; the grace that covers elytra landings now covers dismounts too. - Nuker and FastPlace require repeated windows. Both flagged on a single one-second window over the limit. Plugins that break several blocks per action (vein miners, custom tools) fire a burst of events inside one tick, and instamining with Efficiency V and Haste reaches ~20 blocks/s by hand — close enough to the cap that one bundled window crosses it. A cheat holds the rate up across windows; a burst does not. - KillAura multi-target requires a streak. Reach and aim angle each needed three suspicious hits; hitting several targets did not, so one burst flagged outright — and a crowded team fight legitimately puts three players within reach inside 250 ms. - AimSnap now requires the flick to be fast in time, not just in packet order. The check compares three consecutive rotation packets, which should span ~2 ticks, but nothing bound how far apart they actually arrived. A packet gap handed it three rotations seconds apart — and turning to look at something and back is ordinary over a second. Capped at 150 ms. - FastUse reads the item's own use time. The 600 ms floor assumed vanilla eat times; custom consumables that are legitimately quicker were flagged for being what they are. The floor is now the lower of the configured value and what the item actually needs. - Mining outside the overworld is accounted for. STONE_TYPES — the spoil that forms the ratio's denominator and decides whether a
    113 downloads
  7. 1.0.3BS-AntiCheat 1.0.3Aug 10, 2026

    [1.0.3] - 2026-08-05

    Full code-review release: correctness fixes found by auditing every source file against its own documentation, plus hot-path performance work and dead-code removal. No behaviour change for the default configuration except where a check was previously too weak.
    Fixed — false positives
    Fixed — movement
    - Standing on entities is no longer GroundSpoof/Fly. The ground scan only ever looked at blocks, so a player standing on a boat, a minecart, a horse or another player's head had "nothing below them" and was flagged. An entity check now runs — but only when the block scan came up empty, so it costs nothing during normal play. - Lily pads are no longer Jesus. The water-surface test accepted any non-water block at foot level, so walking across lily pads (and standing on a boat on water) read as walking on water. The feet block must now be air specifically. - Jump Boost is exempt from the fly checks. It raises both jump height and time spent rising, which fed the hover counter. The Step check had always excluded it. - Depth Strider is compensated. Without it a player with Depth Strider III swims at roughly walking speed and pushes against the swimming cap.
    Fixed — inventory
    - InventoryMove had no knockback grace. Any server-applied velocity (arrow or trident hit, wind charge, explosion, jump pad) moves a player with a GUI open without any key input. MovementChecker has always had this grace; this check was missing it. - InventoryMove ignored plugin-granted flight. A player flying via EssentialsX /fly is in survival gamemode, so the creative exemption never applied — they drifted with a GUI open and were flagged. Now exempt, as is the Velocity check for the same reason. - ChestStealer had no lag guard. After a lag spike the queued clicks all arrive in one tick and look like 0 ms intervals — an instant flag. Every other check already backed off under lag.
    Fixed — combat and vehicles
    - Reach honours the `entity_interaction_range` attribute. Item plugins grant long-reach weapons by raising it; judging those hits against the flat config value flagged players for using their own gear. - Vehicle teleports are not speed violations. VehicleMoveEvent carries no teleport flag, so a Multiverse portal or a plugin repositioning a ridden horse produced one enormous "movement". Implausible single-tick jumps now reset the streak instead. - A Speed potion on a mount raises its ceiling. A fast horse under Speed II otherwise blew past the flat per-type limit.
    Changed
    - `autoclicker_min_signals` now defaults to 3 (was 2). Markers 1 and 2 both measure jitter and rise together, so a human clicking steadily trips both — 2 was not the safe middle it appeared to be. Marker 3 (does the player ever pause?) is the independent signal. Documented in config.yml.
    Fixed — effects, items and attributes
    - Checks now read the game's own attributes instead of hand-rolled multipliers. The Speed potion was compensated by a hard-coded formula, so every other legitimate way a player gets faster was invisible: attribute modifiers from custom gear and item plugins, datapacks, mount/armour buffs — and EssentialsX /speed, which bypasses the attribute system entirely via setWalkSpeed(). The speed checks now derive their ceiling from movement_speed and getWalkSpeed(). Verified numerically identical for potions (Speed I → 1.2x, Speed II → 1.4x, exactly as before), so nothing was loosened for vanilla play. Vanilla applies the sprint boost as a movement_speed modifier, which is divided back out so the separate sprint threshold is not inflated by 30%. - Sneaking uses the `sneaking_speed` attribute — what Swift Sneak actually modifies — instead of a fixed 0.3, so any item or plugin granting faster crouching is respected. The enchantment is still read as a floor. - Jump strength scales the vertical fly allowance, step height is taken from the step_height attribute, and reduced `gravity` exempts the hover check — hanging in the air without descending is exactly what that check flags, and low gravity makes it legitimate. - Body `scale` is respected: a resized player has a proportionally wider hitbox (the ground scan now scans that width) and reaches proportionally further (reach ceiling). - Swimming reads `water_movement_efficiency`, the attribute vanilla maps Depth Strider onto, so items and plugins granting the same effect without the enchantment are covered. The enchantment lookup remains as a floor.
    Changed — Criticals is now opt-in
    - `criticals_detection` now defaults to `false`. Checking the vanilla rules against the implementation showed the condition ("critical and on the ground and fall distance zero") is a contradiction: the server only awards a critical when the player is airborne and falling, and isCritical() reports that same server-side decision. It can therefore never fire on legitimate vanilla combat — only on damage events synthesised by other plugins, i.e. it flags players for using custom gear. It also does not catch the real Criticals cheat, which fakes micro-falls so the crit is computed while the player is reported airborne. A working implementation needs per-tick vertical movement from the packet layer; until then the check is off rather than silently wrong. Consistent with the 30 days of production data available, where it never fired once.
    Fixed — the plugin now really is optional-dependency safe
    - BSAntiCheat did not load at all without PacketEvents, despite plugin.yml listing it only as a softdepend and the README promising the packet checks would simply switch off. The main class named PacketEvents types directly (a PacketListenerCommon field, and passing PacketChecker where a PacketListener is expected), so the JVM had to resolve them while linking the main class — long before the runtime guard in onEnable could run. Anyone installing the plugin without PacketEvents got a startup error instead of the documented degradation. All PacketEvents references now live in a single PacketIntegration class; loading that is what fails on such a server, inside the caller's try/catch(Throwable). Verified by loading the main class against a classpath with and without PacketEvents.
    Fixed — correctness
    - AutoClicker consistency analysis now actually exists. config.yml documented seven options for it (autoclicker_consistency, _min_samples, _min_cps, _max_deviation_ms, _max_cv, _max_outlier_ratio, _min_signals) and the alert text was translated in both languages — but no code ever read them, so enabling it did nothing. Implemented as documented: three robotic markers (absolute jitter, jitter relative to click rate, share of human pauses), flagged when at least min_signals hold. Still opt-in; calibrate with debug_mode, which now logs sd/cv/outlier values. - XRay ore/stone ratio was measured against a stale stone count. The stone deque was only trimmed to the time window when stone was broken or by the 5-minute cleanup task, so a player who mined stone and then switched to pure ore mining kept an inflated denominator for minutes — silently weakening the check. It is now trimmed at read time. - `/movealerts clear <player> --db` deleted only 3 of ~30 log types (speed, fly, teleport), leaving every other check's rows behind while reporting success. It now deletes everything that is not XRay-owned, derived from a single shared constant so adding a check can no longer make it stale again. - Log deletion could hit the wrong player. The queries built LIKE patterns from raw names, and _ is a single-character wildcard in SQL — clearing alerts for A_B also matched AxB. Patterns are now escaped (ESCAPE '\'). - **KillAura multi-target raised
    46 downloads
  8. 1.0.2BS-AntiCheat 1.0.2Jul 16, 2026

    [1.0.2] - 2026-07-10

    XRay calibration release: live data showed legitimate beacon (Haste II) + Efficiency V deepslate branch mining still tripping the per-ore thresholds and the ore/stone ratio (10.45% vs. the 10% limit). - Per-ore thresholds raised for the deepslate-level ores: gold 10 → 15, redstone 10 → 20, lapis 8 → 15, diamond 6 → 10, emerald 4 → 6. - Ore/stone ratio 0.10 → 0.15 — cave/deepslate miners legitimately exceed 10%. - Combined rare-ore threshold 8 → 12 and now configurable (xray_rare_combined_threshold). - New `xray_exempt_worlds` option: skip XRay entirely in listed worlds (resource/farm worlds that get reset). Prefer raised thresholds over full exemption — cheaters mine in exactly those worlds. - Threshold alerts now name the exceeded ores (e.g. [DIAMOND_ORE x11 (max 10)]) in the alert and DB log — previously a logged alert couldn't be diagnosed or tuned against. ---

    [1.0.1] - 2026-07-09

    False-positive elimination release, driven by live alert data from a production server (503 GroundSpoof / 314 Timer / 150 Speed / 62 Fly alerts — all confirmed false positives). Verified in production: deployed 2026-07-09; the prior baseline of ~200 false alerts per day (~1000 over 5 days) dropped to zero in the first 24 hours of normal play.
    Movement
    - GroundSpoof/Fly ground detection rewritten: all ground tests now check the four hitbox-footprint corners (not just the centre column — sneaking over an edge no longer flags), include the block at foot level itself (standing on trapdoors, slabs, carpets, snow layers), and use real collision (isPassable) instead of isSolid, plus explicit support for powder snow, scaffolding tops, cobwebs, climbables and liquids. - Fly at ladders fixed: climbing state now uses the game's own isClimbing() logic, and climbable blocks below the feet count as support (exiting the top of a ladder no longer accumulates hover violations). - Speed on ice fixed: the ice multiplier now survives sprint-jumps (3-block down-scan plus a 2.5s ice-momentum memory) — ice-road running no longer flags. - Swift Sneak supported: the sneak speed cap scales with the enchantment level (level 3 = 0.75x walking) instead of the fixed vanilla 0.3x. - Server-applied velocity grants knockback immunity (PlayerVelocityEvent): projectile knockback (punch bows, snowballs), wind charges, fishing-rod pulls and jump-pad plugins no longer trigger Speed/Fly. - Slime-bounce grace: high bounces are exempt during the whole rise (3s launch memory), not only while slime is within 2 blocks below.
    Combat
    - Reach and KillAura (angle) now require 3 suspicious hits within a 10s window (configurable via reach_violations/killaura_angle_violations) instead of flagging a single hit, and Reach is ping-compensated (same sqrt scaling as movement, fed by the transaction-latency system). - AutoBlock default off: vanilla allows attacking while an offhand shield is raised, so the check flags ordinary sword+shield play. Opt-in only.
    Packet & world
    - Timer clock-drift leak: real time is counted at 101%, absorbing benign client clock drift (~0.5% fast clocks accumulated ~10ms/s and periodically crossed the 200ms limit). - **FastBreak measures the expected dig time at dig start AND end and uses the lenient one** — landing from a jump or a haste beacon kicking in mid-dig no longer flags. - Scaffold angle measured to the clicked block (nearest hitbox point) instead of the placed block's centre — placing a block at your own feet (95–113° off aim in live data) no longer counts. - Boat ice momentum: the boat speed ceiling and Boat-Fly keep the ice multiplier for 3s after ice contact — bumps/gaps/ramp launches on ice roads no longer flag. - InventoryMove exempts momentum and shoves: sliding on ice with a GUI open and being pushed by nearby entities (mob crowds) no longer count.
    Infrastructure & calibration
    - Lag detection reacts to spikes: isLagging now also checks the 5-second MSPT average (getAverageTickTime) — the 1-minute TPS average barely moves during short spikes, which are exactly what distorts movement deltas. - Calibration from live data: autoclicker_max_cps 20 → 22 (legit butterfly clicking peaked at 21), nuker_max_breaks_per_second 15 → 25 (instamine reaches ~20/s), XRay coal 20 → 30 / iron 15 → 25 / copper 15 → 40 (1.18+ giant veins), XRay ratio check needs 60 mined stone (was 20) so cave miners aren't judged on tiny samples. Note for existing installations: config values already present in your config.yml are kept on update — apply the new calibration defaults (autoclicker_max_cps, nuker_max_breaks_per_second, xray_thresholds, autoblock_detection) manually or delete the keys so the new defaults merge in.
    47 downloads
  9. 1.0.0BS-AntiCheat 1.0.0Jul 3, 2026

    [1.0.0] - 2026-07-02

    First public release. Live-tested and false-positive-calibrated on a real server, verified on both Paper and Folia.
    Detections
    - Movement: Speed, Fly (vertical burst + sustained hover), Teleport, GroundSpoof, Elytra/Riptide speed ceiling. - Combat: Reach (measured to the hitbox surface), KillAura (aim angle + multi-target), AimSnap (robotic snap-back rotation), Criticals, AutoBlock. - World: Nuker, FastPlace, Scaffold, FastBreak (per-block dig time vs. Block#getBreakSpeed). - Vehicle: Boat-Fly and per-type vehicle speed via VehicleMoveEvent (movement checks don't fire while riding, so this closed a real gap). - Packet-level (needs PacketEvents): AutoClicker, BadPackets, Timer, crash protection (oversized book/sign packets are cancelled), packet-flood — the last two stay active even under server lag. - XRay: per-ore thresholds, ore/stone ratio, combined-rare-ore, restricted-world zones, player-placed-ore exclusion. - Inventory (opt-in): InventoryMove, ChestStealer, FastUse, BowSpam, AutoTotem.
    Accuracy & internals
    - No sampling: every movement is checked (not every 10th), so a cheat can't hide between samples. - Server-authoritative ground check (bounding-box vs. the world) instead of the spoofable client flag — hardens Fly/hover against NoFall/Fly spoofing. - Transaction-latency system: a per-tick ping/pong measures true round-trip latency; movement, vehicle and elytra lag compensation use it instead of the coarse getPing(). - Grace windows for teleport, knockback, join/respawn/world-change and elytra landings (residual glide momentum no longer flags as Speed). - AutoClicker excludes mining swings; Reach measured to the hitbox surface; XRay resets its evidence after each flag (no VL spirals); Creative/Spectator exempt.
    Platform & integrations
    - Folia support via a Paper/Folia scheduler abstraction — verified against a real Folia 1.21.11 server with zero scheduler exceptions; behaviour on Paper is unchanged. - Bedrock exemption (exempt_bedrock_players) via Geyser/Floodgate detection. - Legacy-client exemption (exempt_legacy_clients, opt-in) via ViaVersion. - LuckPerms group whitelist, PlaceholderAPI (%bsanticheat_total%, %bsanticheat_vl_<check>%), Discord webhook alerts, bStats, update checker.
    Enforcement & operations
    - Violation-level system with decay and configurable punishment tiers (console commands), optional setback; report-only by default. - 25+ calibration thresholds in config.yml, all tunable live via /bsac reload. - SQLite logging with a plaintext fallback logger; silent mode; bilingual (EN/DE); auto-migrating config.
    Defaults & notes
    - The false-positive-prone movement micro-heuristics — NoSlow, Jesus, Spider, Step — and Velocity/AntiKnockback ship off by default (opt-in). They need further hardening; the reliable checks are enabled out of the box. - autoclicker_max_cps defaults to 20 (skilled humans reach ~17–20; autoclickers exceed it).
    Notable fixes since the internal extraction
    - Correct shading: sqlite-jdbc/slf4j are provided by Paper (jar shrank from ~11 MB to ~355 KB). - Thorns damage no longer flags the victim as KillAura; sweep hits no longer cause bogus reach/angle flags. - DatabaseManager: no silent log loss (batch overflow, failed inserts, shutdown drains fully). - Thread-safety fixes (config/language published atomically); non-blocking command player lookups; Discord webhook timeouts. ---

    Origin

    Extracted from the PerformanceAnalyzer plugin (v2.3.4) as a standalone anti-cheat: movement and XRay detection with lag compensation, LuckPerms whitelist, Discord webhooks, SQLite logging, silent mode and bilingual support. Everything above builds on that base.
    27 downloads

Similar projects

Plugin →