OnPlayerViolation
OnPlayerViolation is called from AntiHack.AddViolation right before the server records an anti-cheat violation for a player, letting you suppress, log, or act on it.
object OnPlayerViolation(BasePlayer player, AntiHackType type, float amount, UnityEngine.GameObject gameObject)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer the anti-cheat recorded the violation for. Never null at the call site, but check IsConnected when acting on them. |
type | AntiHackType | The AntiHackType enum value describing what the anti-cheat suspected (movement, projectile, melee, eye position, and similar categories). |
amount | float | The severity float the anti-cheat attached to this specific violation. Higher values mean the detected deviation was larger; the scale differs per AntiHackType. |
gameObject | UnityEngine.GameObject | Optional GameObject involved in the violation (for example a weapon or projectile entity). Can be null, so always null-check before using it. |
Return value
Returning null (or letting the method fall through) lets the server record the violation normally. Returning any non-null value skips the remainder of AntiHack.AddViolation, so that violation is not counted or acted on. The return value itself is never used as data.
How it works
The hook fires inside AntiHack.AddViolation, which the server calls whenever a fail-counting anti-hack check (movement, projectile, melee, eye position and the other AntiHackType categories) decides a player deserves a violation record. Because it sits at the very start of that method, it fires for every violation the anti-cheat would otherwise log, including the ones that would push a player toward a kick or ban.
player is the suspect, type tells you which check fired, and amount is the severity the check computed — values are not comparable across different AntiHackType values, so use thresholds per type rather than a single global number. gameObject is sometimes null, so guard it.
The return value controls whether the violation is recorded. Returning non-null skips the rest of AddViolation for that call, so the server neither counts the violation nor applies its consequences for it. That makes this hook useful for server-side exceptions (admins testing, custom movement like vehicles or teleport plugins) as well as for mirroring violations into your own logs, Discord feeds, or warnings. Because it can fire very frequently on busy servers, keep the hook body cheap and never print to the console from it.
Example plugin: Warn and log anti-cheat violations instead of silently ignoring them
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Violation Watcher", "RustPluginGenerator", "1.0.0")]
[Description("Logs anti-cheat violations and warns players who trip checks repeatedly.")]
public class ViolationWatcher : RustPlugin
{
private const string PermExempt = "violationwatcher.exempt";
private PluginConfig config;
private readonly Dictionary<ulong, Queue<float>> recent = new Dictionary<ulong, Queue<float>>();
private class PluginConfig
{
[JsonProperty("Warn threshold within window")]
public int WarnThreshold { get; set; } = 5;
[JsonProperty("Window seconds")]
public float WindowSeconds { get; set; } = 60f;
[JsonProperty("Exempt permission")]
public string ExemptPermission { get; set; } = PermExempt;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Warning"] = "Anti-cheat check triggered ({0}). Please make sure your connection is stable."
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(config.ExemptPermission, this);
}
private object OnPlayerViolation(BasePlayer player, AntiHackType type, float amount, GameObject gameObject)
{
if (player == null || !player.IsConnected) return null;
if (player.IsAdmin || permission.UserHasPermission(player.UserIDString, config.ExemptPermission))
return null;
Interface.Oxide.LogInfo("[Violation] {0} ({1}) type={2} amount={3:0.##}",
player.displayName, player.UserIDString, type, amount);
if (!recent.TryGetValue(player.userID, out var window))
{
window = new Queue<float>();
recent[player.userID] = window;
}
float now = Time.realtimeSinceStartup;
window.Enqueue(now);
while (window.Count > 0 && now - window.Peek() > config.WindowSeconds)
window.Dequeue();
if (window.Count == config.WarnThreshold)
player.ChatMessage(Msg("Warning", player.UserIDString, type));
return null;
}
private void Unload()
{
recent.Clear();
}
}
}The plugin listens to OnPlayerViolation and records each violation with the player name and Steam ID, the AntiHackType name and the severity amount. It writes to oxide/logs/ViolationWatcher through Interface.Oxide.LogInfo, so admins can audit anti-cheat activity without scrolling the console. A second list tracks players who exceed a configurable number of violations within a rolling window; once over the limit they receive a chat warning built from the language file. The hook always returns null so the vanilla anti-cheat continues to work exactly as before — this plugin observes, it does not suppress. Amounts are only compared per type via a simple count threshold so mismatched scales don't cause false alarms.
Common use cases
Audit trail for staff
Log every violation with player, type and amount to a file so admins can review suspicious activity after the fact without watching the console.
Progressive in-game warnings
Send a chat warning the first few times a player trips a check, before the server's own kick logic kicks in.
Exempt trusted players from noisy checks
Return non-null for admins or members of a permission group during known server events (custom movement, tournaments) so their violations are not counted.
Pitfalls
The callback fires a lot
On populated servers with projectile and movement checks, this hook can fire many times per second. Never call Puts, PrintWarning or write to disc synchronously inside it.
amount is not comparable across types
A projectile amount of 3 and a movement amount of 3 mean very different things. Base thresholds on the AntiHackType or count violations rather than using a single amount cutoff.
Returning non-null disables the check
Any non-null return suppresses the violation record for that call. Do not return a value just to indicate 'handled' — if you want the anti-cheat to keep working, return null.
Build a plugin with OnPlayerViolation
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPlayerViolation
Generate