OnEyePosValidate
OnEyePosValidate is called from AttackEntity.ValidateEyePos when the server checks that a player's reported eye position is not too far from where the entity is expected to be, before allowing an attack.
object OnEyePosValidate(AttackEntity attackEntity, BasePlayer player, UnityEngine.Vector3 eyePos, bool checkLineOfSight)Parameters
| Parameter | Type | Description |
|---|---|---|
attackEntity | AttackEntity | The AttackEntity whose eye position is being validated, typically the weapon or tool held by the player. Compare its net.ID or ShortPrefabName to target specific weapon types. |
player | BasePlayer | The BasePlayer the validation is for. May be null in edge cases, so null-check before reading userID or position. |
eyePos | UnityEngine.Vector3 | The eye position being checked by the server. Useful to compare against the player's actual eyes.position if you want to apply tolerance rules. |
checkLineOfSight | bool | Whether the game will also run a line-of-sight trace in addition to the distance check. Read-only context; you cannot change this from the hook. |
Return value
Return true to accept the eye position without the game's distance and line-of-sight checks, false to reject it and block the action. Return null (or any non-bool value) to let the original ValidateEyePos logic run and decide.
How it works
Oxide fires this hook at the start of AttackEntity.ValidateEyePos, which the game runs whenever it needs to confirm a player's eye position is plausible for the given attack entity. The original method would compare the supplied eyePos against the entity's expected position and, when checkLineOfSight is true, run a line-of-sight trace. The hook receives the AttackEntity being validated, the owning BasePlayer, the raw eyePos the server is checking, and the checkLineOfSight flag.
Returning a bool overrides the entire original check, so true accepts the position without any distance or LOS trace and false rejects it outright. Returning null (or any non-bool) lets the vanilla validation run unchanged.
Because the argument is AttackEntity, you can declare the parameter as a specific subtype such as BaseMelee or BaseProjectile to narrow the hook to that family of weapons; Oxide skips calls where the argument is a different type. This hook is a low-level anti-cheat surface, so keep your logic cheap: it can run for every attack on a busy server.
Example plugin: Reject eye positions that differ too far from the player's real eyes
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Eye Pos Guard", "RustPluginGenerator", "1.0.0")]
[Description("Rejects attacks whose reported eye position is too far from the player's real eyes.")]
public class EyePosGuard : RustPlugin
{
private const string PermAdmin = "eyeposguard.admin";
private PluginConfig config;
public class PluginConfig
{
[JsonProperty("Enabled")]
public bool Enabled = true;
[JsonProperty("Max Allowed Distance (meters)")]
public float MaxDistance = 2f;
}
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>
{
["Rejected"] = "[EyePosGuard] Rejected attack from {0}: eye offset {1:F2}m",
["ToggleOn"] = "EyePosGuard enabled.",
["ToggleOff"] = "EyePosGuard disabled.",
}, 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(PermAdmin, this);
}
private bool IsEnabled => config != null && config.Enabled;
private object OnEyePosValidate(AttackEntity attackEntity, BasePlayer player, Vector3 eyePos, bool checkLineOfSight)
{
if (!IsEnabled || player == null || attackEntity == null) return null;
if (player.IsAdmin || player.IsDead()) return null;
float offset = Vector3.Distance(player.eyes.position, eyePos);
if (offset <= config.MaxDistance) return null;
NotifyAdmins(player, offset);
return false;
}
private void NotifyAdmins(BasePlayer offender, float offset)
{
foreach (var admin in BasePlayer.activePlayerList)
{
if (admin == null || !admin.IsConnected) continue;
if (!admin.IsAdmin && !permission.UserHasPermission(admin.UserIDString, PermAdmin)) continue;
admin.ChatMessage(Msg("Rejected", admin.UserIDString, offender.displayName, offset));
}
}
[ChatCommand("eyeposguard")]
private void CmdEyePosGuard(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAdmin))
{
return;
}
config.Enabled = !config.Enabled;
SaveConfig();
player.ChatMessage(Msg(config.Enabled ? "ToggleOn" : "ToggleOff", player.UserIDString));
}
}
}The plugin compares the eyePos passed to the hook against the player's current eyes.position. If the reported position is further than the configured tolerance (default 2 meters), it returns false so the attack is rejected, and records the discrepancy to a data file. Admins with the eyeposguard.admin permission get a live chat warning when a rejection happens so they can spot suspicious clients. Player-facing text goes through the language file, and the config lets an admin disable the guard at runtime and adjust the tolerance without recompiling. The check stays cheap because it is a single Vector3.Distance call per invocation.
Common use cases
Detect spoofed eye positions
Reject attacks where the reported eye position is far from the player's actual eye position, which can indicate an aim or position manipulation cheat.
Relax validation for laggy clients
For known high-ping players or a tolerant server, accept the check by returning
truewhen the discrepancy is within a larger custom margin so legitimate latency is not punished.Whitelist vehicles or turret-mounted weapons
Declare the parameter as a specific AttackEntity subtype such as
BaseProjectileand returntruefor mounted weapon cases where the expected eye position is naturally offset.
Pitfalls
Returning true disables the entire check
A
truereturn skips both the distance test and the line-of-sight trace, so use it for narrow cases rather than as a blanket accept.Not every callback is a cheat
Vehicles, mounted weapons and unusual game states cause legitimate offsets. Take logs and tune tolerance before auto-kicking anyone based on this hook.
Hot code path
This fires on every attack validation. Avoid allocations, string formatting and logging inside the hook unless an outlier is detected.
Build a plugin with OnEyePosValidate
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 OnEyePosValidate
Generate