Skip to content
RustPluginGenerator

OnServerUserRemove

OnServerUserRemove is called when a user entry is removed from the server's user registry via ServerUsers.Remove, before the entry is actually deleted.

Signature
void OnServerUserRemove(ulong uid)

Parameters

Hook parameters
ParameterTypeDescription
uidulongThe Steam64 ID of the user being removed from the server's user list (bans, whitelist entries, and similar ServerUsers records).

Return value

The return value is ignored. Returning anything (or nothing) does not change the fact that the user entry is removed immediately after the hook runs. This hook cannot veto a removal.

How it works

The hook fires inside ServerUsers.Remove(ulong uid) at the very start of the method, before the users dictionary drops the record. That method is the server-side path used to delete a stored user entry, such as lifting a ban or clearing a whitelist/moderator slot, typically reached through console commands like banid, unban, ownerid removal, or plugin code that manipulates ServerUsers directly.

The single uid argument is the Steam64 ID of the record being removed. Because the call happens before removal, a query against ServerUsers.Is(uid, ServerUsers.UserGroup.Banned) or similar still reflects the old state at the moment the hook runs. The hook carries no group or reason information, so if you need to know what kind of entry was removed you must capture that yourself (for example with OnServerUserSet, which fires when an entry is added) or look it up before calling Remove in your own code.

There is only one argument and it is always a ulong, so no overload narrowing is possible or useful here.

Example plugin: Log unbans and tell online staff

ServerUserRemoveLog.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("ServerUserRemoveLog", "RustPluginGenerator", "1.0.0")]
    [Description("Logs server user record removals and notifies online admins.")]
    public class ServerUserRemoveLog : RustPlugin
    {
        private const string PermSee = "serveruserremovelog.see";

        private PluginConfig config;
        private StoredData data;
        private readonly Dictionary<ulong, string> knownGroups = new Dictionary<ulong, string>();

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

            [JsonProperty("Keep last 200 log entries")]
            public int MaxEntries { get; set; } = 200;
        }

        private class LogEntry
        {
            [JsonProperty("Steam ID")]
            public string UserId { get; set; }

            [JsonProperty("Group")]
            public string Group { get; set; }

            [JsonProperty("Removed at (UTC)")]
            public string RemovedAt { get; set; }
        }

        private class StoredData
        {
            [JsonProperty("Removals")]
            public List<LogEntry> Removals { get; set; } = new List<LogEntry>();
        }

        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>
            {
                ["Removed"] = "User record removed: {0} (group: {1})",
                ["UnknownGroup"] = "unknown"
            }, this);
        }

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

        private void Loaded()
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
        }

        private string Msg(string key, string userId, params object[] args) =>
            args.Length == 0 ? lang.GetMessage(key, this, userId) : string.Format(lang.GetMessage(key, this, userId), args);

        private void OnServerUserSet(ulong uid, string name, ServerUsers.UserGroup group)
        {
            knownGroups[uid] = group.ToString();
        }

        private void OnServerUserRemove(ulong uid)
        {
            string group;
            if (!knownGroups.TryGetValue(uid, out group))
                group = Msg("UnknownGroup", null);
            knownGroups.Remove(uid);

            data.Removals.Add(new LogEntry
            {
                UserId = uid.ToString(),
                Group = group,
                RemovedAt = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")
            });

            while (data.Removals.Count > config.MaxEntries)
                data.Removals.RemoveAt(0);

            if (!config.Announce)
                return;

            string message = Msg("Removed", null, uid, group);
            foreach (var player in BasePlayer.activePlayerList)
            {
                if (player != null && permission.UserHasPermission(player.UserIDString, PermSee))
                    player.ChatMessage(message);
            }
        }

        private void Unload()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }
    }
}

The plugin keeps an in-memory snapshot of every user entry with a group added during this session via OnServerUserSet, mapping the Steam64 ID to a readable group name. When OnServerUserRemove fires, it looks up the snapshot, records a line into an Oxide data file with a timestamp, and notifies online admins in chat if the option is enabled. It also removes the stale snapshot entry so the dictionary does not grow. Because ServerUsers.Remove gives no reason or group, the snapshot is the only way to report what was actually removed. Messages go through the language file and the data file is written on unload.

Common use cases

  • Audit trail for bans and whitelist changes

    Append every removal to a data file with a timestamp so staff can review who lifted or deleted entries and when.

  • Notify staff in real time

    Send a chat line to online admins whenever a user record is removed, so unexpected unbans are noticed quickly.

  • Sync with an external roster file

    Keep an Oxide data file mirroring the current user list by removing the ID from your own stored table inside the hook.

Pitfalls

  • Fires for every user group

    The hook does not tell you whether the removed entry was a ban, owner, moderator, or whitelist slot. Track the group yourself or you will mislabel log entries.

  • Not the same as OnPlayerBanned or OnUserUnbanned

    Those hooks relate to player-level events; this one only means a ServerUsers record was deleted and can fire for offline IDs that never connected.

  • Runs before the removal completes

    Inside the hook the entry still exists. If you query ServerUsers for the ID you will get the old state, so do not use the hook to confirm the removal already happened.

Build a plugin with OnServerUserRemove

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

Prompt: Audit trail for bans and whitelist changes using the OnServerUserRemove 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 OnServerUserRemove

Generate