Skip to content
RustPluginGenerator

OnPlayerVanished

OnPlayerVanished is called after an admin turns on invisibility with the server-side `invis` debug convar and the game has already marked the player invisible.

Signature
void OnPlayerVanished(BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer that just became invisible through the debug invis convar. Check IsConnected and IsAdmin before acting on it.

Return value

Returning a value has no effect. The hook is called after the visibility change has been applied and its return value is ignored, so the player stays invisible regardless of what you return.

How it works

The hook fires from ConVar.Debugging.invis, the console command that toggles admin invisibility. By the time it runs, the game has already set isInvisible to true, added the player to the invisible-player list, disabled their collider, added them to the AI ignore list, and removed them from the server player query. The single parameter is the affected BasePlayer. There is only one place the hook is called, so it fires once per vanish action, not per tick. It is a notification, not a gate: you cannot stop the vanish by returning a value. Use it to react to staff going invisible, for example to log the action, announce it to other staff, or flag the player for later cleanup. Vanish only happens through that admin convar, so ordinary players never trigger it. OnPlayerVanish runs before the state change, OnPlayerUnvanish and OnPlayerUnvanished handle the reverse transition.

Example plugin: Audit log for admin vanish

VanishAudit.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("VanishAudit", "RustPluginGenerator", "1.0.0")]
    [Description("Writes a persistent audit entry every time an admin vanishes with the invis convar.")]
    public class VanishAudit : RustPlugin
    {
        private const string PermSee = "vanishaudit.see";

        private PluginConfig config;
        private VanishLog logData;
        private Timer saveTimer;

        private class PluginConfig
        {
            [JsonProperty("Notify other admins")]
            public bool NotifyAdmins { get; set; } = true;

            [JsonProperty("Maximum log entries")]
            public int MaxEntries { get; set; } = 500;
        }

        private class VanishEntry
        {
            [JsonProperty("Time")]
            public string Time { get; set; }

            [JsonProperty("Player id")]
            public string UserId { get; set; }

            [JsonProperty("Player name")]
            public string Name { get; set; }
        }

        private class VanishLog
        {
            [JsonProperty("Entries")]
            public List<VanishEntry> Entries { get; set; } = new List<VanishEntry>();
        }

        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>
            {
                ["AdminNotice"] = "{0} ({1}) is now invisible.",
                ["SelfNotice"] = "Your vanish has been logged.",
                ["NoPermission"] = "You do not have permission to view the vanish log."
            }, 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(PermSee, this);
        }

        private void OnServerInitialized()
        {
            logData = Interface.Oxide.DataFileSystem.ReadObject<VanishLog>(Name) ?? new VanishLog();
        }

        private void OnPlayerVanished(BasePlayer player)
        {
            if (player == null || logData == null) return;

            logData.Entries.Add(new VanishEntry
            {
                Time = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
                UserId = player.UserIDString,
                Name = player.displayName
            });

            int over = logData.Entries.Count - config.MaxEntries;
            if (over > 0) logData.Entries.RemoveRange(0, over);

            ScheduleSave();

            if (!config.NotifyAdmins) return;

            foreach (BasePlayer other in BasePlayer.activePlayerList)
            {
                if (other == null || other == player || !other.IsConnected) continue;
                if (!other.IsAdmin && !permission.UserHasPermission(other.UserIDString, PermSee)) continue;
                other.ChatMessage(Msg("AdminNotice", other.UserIDString, player.displayName, player.UserIDString));
            }

            player.ChatMessage(Msg("SelfNotice", player.UserIDString));
        }

        private void ScheduleSave()
        {
            if (saveTimer != null) saveTimer.Destroy();
            saveTimer = timer.Once(5f, SaveLog);
        }

        private void SaveLog()
        {
            saveTimer = null;
            if (logData == null) return;
            Interface.Oxide.DataFileSystem.WriteObject(Name, logData);
        }

        private void Unload()
        {
            if (saveTimer != null)
            {
                saveTimer.Destroy();
                saveTimer = null;
            }
            SaveLog();
        }
    }
}

The plugin listens for OnPlayerVanished and records the event. Each vanish writes a timestamped line to a persistent log in oxide/data using Interface.Oxide.DataFileSystem, capped by the configurable maximum entry count so the file never grows without bound. If the config option is enabled, connected admins receive a chat notice through the language system, showing the vanished player's display name. The vanished player also gets a short confirmation so staff know the audit entry was created. Hard-coded strings are avoided; all player-facing text is registered in LoadDefaultMessages and formatted by a small helper. Unload saves the log and clears the notice timer.

Common use cases

  • Staff activity audit

    Write every vanish into a persistent file so server owners can see when admins go invisible during a raid or a player report.

  • Admin awareness

    Notify other connected admins in chat that a colleague just vanished, so overlapping staff actions are visible to the team.

  • Discord or logging plugin bridge

    Call into a separate logging plugin with the player's ID and name to push the vanish into an external admin log channel.

Pitfalls

  • Not a gate

    The hook fires after isInvisible is set and the collider is disabled. You cannot block the vanish here; use OnPlayerVanish if you need to decide before the change.

  • Do not re-enable the collider

    The game disables the player collider and removes them from the player query as part of vanishing. Manually reversing those steps from this hook breaks the intended invisibility behavior.

  • Fires only through the convar

    If another plugin sets isInvisible directly, this hook will not run. Handle those plugins through their own API or a periodic check if you need to cover them.

Build a plugin with OnPlayerVanished

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Staff activity audit using the OnPlayerVanished 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 OnPlayerVanished

Generate