Aim statistics: a clue for your staff, never a sanction
Bastrel keeps, for every player, the last 50 weapon impacts the clients declare (weaponDamageEvent), per weapon category, and measures a few things about them: how often the head is hit, how far the targets are, how fast the player changes target, and how many fatal hits he declares per minute. When several of these look inhuman together, it writes one line, BAS-AIM-001, for your staff to look at.
It never bans and never kicks
Section titled “It never bans and never kicks”This is a rule of Bastrel, not a setting. On its own, the aim module produces a score and a staff alert, plus at most one screenshot sent with the alert (setting aim.screenshot), nothing else: it never issues a kick or a ban, whatever your configuration says (matrix, override, score threshold). A human reads the line and decides. One thing to know: its score adds to the player’s total like any other, so after about a dozen findings it can push the total past a kick threshold; that kick then stays pending and is applied by the next enforce detection of another module (spec section 12.3), never by aim. An aim statistic is a clue, not a proof: a very good player, a lucky streak or a sniper rifle can look like this.
Screenshot with the alert
Section titled “Screenshot with the alert”Once BAS-AIM-001 is lifted, a finding asks for a screenshot of the player together with the staff alert, so that a human has an image to read next to the numbers. This is the aim.screenshot key (true by default; false keeps the alert alone). It never adds a kick or a ban, and a pending kick threshold stays pending. The image is only taken if the screenshots module is enabled and within its own limits (delay per player, pending requests); a screenshot is personal data and you are responsible for it. It is not available as a per-rule override: the aim rules refuse overrides. While the rule is unverified, nothing is asked.
Status: journal only, for now
Section titled “Status: journal only, for now”BAS-AIM-001 ships as unverified: the distance reads the position of the victim, which nobody has yet read on a player who moves, and the “fatal hit” flag is a declaration by the client. So today:
- the line appears in the log and in the detections table, with
would_haveempty; - no alert, no Discord message, no score, no kick, no ban;
- it is lifted later, in a separate release, after a capture on a real server.
Off until you fill it in
Section titled “Off until you fill it in”Nothing is judged out of the box: aim.categories and aim.thresholds ship empty, because no value is a measure. Fill them from what you see in your own observe logs.
aim = { categories = { WEAPON_PISTOL = 'pistol', WEAPON_PUMPSHOTGUN = 'shotgun' }, -- weapon name or hash -> category thresholds = { pistol = { headRatio = 0.60, avgDistance = 60.0, minInterTargetMs = 250, killsPerMinute = 8.0 }, shotgun = { headRatio = 0.50, avgDistance = 40.0 }, -- a key you leave out switches its signal off },}A weapon that is not in categories (the melee WEAPON_UNARMED is refused, by name or by hash) and a category without an entry in thresholds are never judged. A category needs at least aim.minSignals (2) keys in its row, or it can never produce a finding: Bastrel warns at startup when it does not (a row with a single key is a dead row).
The four signals
Section titled “The four signals”On a full window (aim.minShots, 20 impacts or more), per category:
| Signal | True when |
|---|---|
head |
the share of impacts at a head component (gameevents.headComponents) is at least headRatio |
distance |
the mean distance shooter-victim is at least avgDistance metres |
switch |
at least aim.minFastSwitches (3) changes of target happen less than minInterTargetMs apart, but not less than aim.batchMs (50 ms) apart |
kills |
the impacts declared fatal (willKill) reach killsPerMinute (the minute is measured over at least aim.minSpanMs, 10 s) |
BAS-AIM-001 is written when at least aim.minSignals (2) signals are true together. One signal alone is too common. The line carries the category, the measures and the signal names. After a finding the window is emptied and the player gets no other line for aim.alertCooldownMs (5 min, all categories together).
What is counted
Section titled “What is counted”An impact counts only when the shooter is the attacker the server recognises, the event was not cancelled, the weapon is in categories, the damage declared is above 0, and the shot has exactly one victim other than the shooter’s own character, and that victim is a ped (a player or an NPC): a hit on a vehicle or an object is not counted. Nothing is counted while the module is inert.
Limits you should know
Section titled “Limits you should know”- A shot at a ped the shooter owns produces no event at all (a ped created by his own client): Bastrel does not see it. It sees shots at players and at peds owned by another client.
- Fast target switches are timed by the moment the events reach the server, not by the moment of the shot. Two impacts that arrive less than
aim.batchMsapart (a network batch after a lag, two pellets of a shotgun) are never a switch. The price: a real switch faster thanbatchMsis not seen. - The head, the target and the “fatal” flag come from the client’s declaration. A cheater who lies there lowers his own statistics; the module is a clue, and a human decides.
- The distance is read after the impact, from the current positions of the two characters, not from where they were at the instant of the shot. A victim who moves fast adds some error.
- The module does not use the graces of
graces(loading, routing bucket): a shot is not a state of the player.
Settings
Section titled “Settings”All in config/modules.lua, block aim. An invalid value takes its default with a warning at startup; Bastrel never stops for it.
| Key | Default | Meaning |
|---|---|---|
windowSize |
50 | impacts kept per player and category (10 to 200) |
minShots |
20 | no judgement before this many impacts (5 to windowSize) |
alertCooldownMs |
300000 | one finding per player in this window |
minSignals |
2 | signals that must hold together (1 to 4) |
minFastSwitches |
3 | fast changes of target needed for switch |
minSpanMs |
10000 | shortest span used to count kills per minute |
batchMs |
50 | impacts that arrive closer than this are never a target switch (0 to 1000) |
screenshot |
true |
true: a finding also asks for a screenshot of the player, in addition to the staff alert (never a kick or a ban); false: alert only. See above |
categories, thresholds |
empty | see above |