Skip to content
RustPluginGenerator

OnPlayerUnvanished

OnPlayerUnvanished is called on the server right after a player is switched back to visible with the debug.invis console command. It fires after collision and player-query registration have already been restored.

Signature
void OnPlayerUnvanished(BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer whose visibility was just restored. Safe to read position, identity and team data from it, but not guaranteed to be connected at the moment the hook fires.

Return value

The return value is ignored. The command always continues to reply to the caller and to send the invisibility UI update, whether or not the hook returned a value.

How it works

The hook fires only from the debug.invis admin console command when it runs the code path that turns invisibility off. The game first re-adds the player to server query and clears the AI ignore list, then calls OnPlayerUnvanished before it writes the Invis: false reply and triggers the debug.setinvis_ui command on the client. That means by the time your plugin runs, the player is already targetable and visible again. The single argument is the BasePlayer being revealed; you can use it for admin audit logging, restoring combat state, broadcasting a staff message, or re-applying any gameplay restrictions you lifted while the player was hidden. The parameter type is already the most specific useful one, so there is nothing to narrow. Because the hook is driven by a console command, it never fires for players who simply disconnect, sleep, or are killed while invisible, so pair it with OnPlayerVanish / OnPlayerUnvanish if you need full coverage.

Example plugin: Audit and announce admin vanish state changes

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

namespace Oxide.Plugins
{
    [Info("Vanish Audit", "RustPluginGenerator", "1.0.0")]
    [Description("Logs and optionally announces when a player becomes invisible or visible via debug.invis")]
    public class VanishAudit : RustPlugin
    {
        private const string PermAnnounce = "vanishaudit.announce";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Announce vanish state changes in chat")]
            public bool Announce { get; set; } = true;

            [JsonProperty("Broadcast to all players (false = only the affected player)")]
            public bool Broadcast { get; set; } = false;
        }

        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>
            {
                ["Vanished"] = "{0} is now hidden from players.",
                ["Unvanished"] = "{0} is now visible to players again.",
                ["NoPermission"] = "You do not have permission to announce vanish changes."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, userId), args);
        }

        private void Init()
        {
            permission.RegisterPermission(PermAnnounce, this);
        }

        private void OnPlayerVanished(BasePlayer player)
        {
            Handle(player, true);
        }

        private void OnPlayerUnvanished(BasePlayer player)
        {
            Handle(player, false);
        }

        private void Handle(BasePlayer player, bool vanished)
        {
            if (player == null) return;

            string state = vanished ? "vanished" : "unvanished";
            Puts(string.Format("{0} ({1}) is now {2}", player.displayName, player.UserIDString, state));

            if (!config.Announce) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAnnounce)) return;

            string key = vanished ? "Vanished" : "Unvanished";
            string text = Msg(key, null, player.displayName);

            if (config.Broadcast)
            {
                Server.Broadcast(text);
                return;
            }

            player.ChatMessage(text);
        }
    }
}

The plugin listens to both OnPlayerVanished and OnPlayerUnvanished so it can log the full cycle. Each event writes a timestamped line to the server console through Puts (only from these rare command events, never from a tick), and if the config flag is on it broadcasts a short chat message to everyone so other players know a staff member is currently hidden or back. A permission check gates the broadcast, so regular players using debug.invis on a permissive server do not spam chat. Config supplies the permission name and the on/off switch, messages live in the language file, and nothing is persisted to disk beyond the config.

Common use cases

  • Staff audit trail

    Record who toggled invisibility and when, so server owners can review admin behavior. Because the hook only fires on the reveal side, log both OnPlayerVanished and OnPlayerUnvanished for a complete pair.

  • Restore combat restrictions

    If you strip weapons or block damage while a player is hidden, re-enable them here so the player cannot carry vanish-only advantages into normal gameplay.

  • Notify the team

    Send a chat line to the player's team members (RelationshipManager.ServerInstance.FindTeam) that the staff member is visible again, without broadcasting to the whole server.

Pitfalls

  • Only fires from the console command

    A disconnect, death, or plugin-driven invisibility change does not run this code path, so the hook is not a reliable general-purpose "player is visible" signal. Track the state yourself for those cases.

  • Player may be disconnecting

    The command can be issued while the player's connection is closing. Always null-check the argument and treat player.IsConnected as optional before sending chat or UI.

  • Not the same as OnPlayerUnvanish

    OnPlayerUnvanish and OnPlayerUnvanished are separate hooks with different call sites. If your plugin depends on both, declare each explicitly rather than expecting one to cover the other.

Build a plugin with OnPlayerUnvanished

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

Prompt: Staff audit trail using the OnPlayerUnvanished 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 OnPlayerUnvanished

Generate