Skip to content
RustPluginGenerator

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.

Signature
object OnEyePosValidate(AttackEntity attackEntity, BasePlayer player, UnityEngine.Vector3 eyePos, bool checkLineOfSight)

Parameters

Hook parameters
ParameterTypeDescription
attackEntityAttackEntityThe 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.
playerBasePlayerThe BasePlayer the validation is for. May be null in edge cases, so null-check before reading userID or position.
eyePosUnityEngine.Vector3The eye position being checked by the server. Useful to compare against the player's actual eyes.position if you want to apply tolerance rules.
checkLineOfSightboolWhether 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

EyePosGuard.cs
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 true when 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 BaseProjectile and return true for mounted weapon cases where the expected eye position is naturally offset.

Pitfalls

  • Returning true disables the entire check

    A true return 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.

Prompt: Detect spoofed eye positions using the OnEyePosValidate hook, with a config file and a permission
Generate this plugin

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