Skip to content
RustPluginGenerator

OnUserBanned

OnUserBanned is called by Oxide after a ban entry is written to the server's user table, giving plugins the banned player's name, Steam ID, last IP, reason and expiry time.

Signature
void OnUserBanned(string playerName, string steamId, string Address, string reason, long expiry)

Parameters

Hook parameters
ParameterTypeDescription
playerNamestringThe display name stored in the ban entry when the ban was recorded. Can be null or empty for bans issued without a known name.
steamIdstringThe banned player's Steam ID as a string (not a ulong), matching the key in the server's user table.
AddressstringThe player's last known IP address from the connected player cache, or the literal string "0" when the player is not currently tracked by the server.
reasonstringThe reason text passed to the ban command. Empty when no reason was supplied.
expirylongUnix timestamp (seconds) when the ban expires, or 0 for a permanent ban. Compare against Facepunch.Math.Epoch.Current.

Return value

The return value is ignored. Whatever you return (or don't return) has no effect on the ban, the player, or the server's user table.

How it works

The hook fires from Oxide's own user-table handler, not from a patched game method. When the server records a user as ServerUsers.UserGroup.Banned and the server has finished initializing, Oxide resolves the player through Covalence and then calls both OnPlayerBanned and OnUserBanned. The only real difference is the second argument: this hook passes the Steam ID as a string, so you can use it directly as a key for data files, permissions and economy calls.

Address comes from the connected-player cache, so it is valid for online players and falls back to "0" for bans issued on an offline ID or via banid. expiry is a raw Unix timestamp; 0 means permanent. Because the hook is called after the entry is written, you should not try to cancel the ban here. There is no return value that changes anything.

The hook also fires for bans applied through the console, RCON or another plugin, so it is a reliable single place to mirror ban state elsewhere. Declaring the parameters with the exact types above is required; there is no overload with a ulong id.

Example plugin: Record bans to a data file and notify staff

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

namespace Oxide.Plugins
{
    [Info("Ban Audit Log", "RustPluginGenerator", "1.0.0")]
    [Description("Records every ban to a data file and reports it to staff in chat")]
    public class BanAuditLog : RustPlugin
    {
        private const string PermStaff = "banauditlog.staff";

        private PluginConfig config;
        private BanData data;

        private class PluginConfig
        {
            [JsonProperty("Maximum stored entries")]
            public int MaxEntries = 500;

            [JsonProperty("Notify staff in chat")]
            public bool NotifyStaff = true;
        }

        private class BanEntry
        {
            [JsonProperty("Player name")]
            public string PlayerName;

            [JsonProperty("Steam ID")]
            public string SteamId;

            [JsonProperty("IP address")]
            public string Address;

            [JsonProperty("Reason")]
            public string Reason;

            [JsonProperty("Expiry (unix, 0 = permanent)")]
            public long Expiry;

            [JsonProperty("Banned at (unix)")]
            public long BannedAt;
        }

        private class BanData
        {
            [JsonProperty("Bans")]
            public List<BanEntry> Bans = new List<BanEntry>();
        }

        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>
            {
                ["BanReported"] = "BAN: {0} ({1}) - {2} - {3}",
                ["Permanent"] = "permanent",
                ["Expires"] = "expires {0}"
            }, 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(PermStaff, this);
            data = Interface.Oxide.DataFileSystem.ReadObject<BanData>(Name) ?? new BanData();
        }

        private void OnUserBanned(string playerName, string steamId, string Address, string reason, long expiry)
        {
            var entry = new BanEntry
            {
                PlayerName = playerName,
                SteamId = steamId,
                Address = Address,
                Reason = reason,
                Expiry = expiry,
                BannedAt = Facepunch.Math.Epoch.Current
            };

            data.Bans.Add(entry);
            if (data.Bans.Count > config.MaxEntries)
                data.Bans.RemoveRange(0, data.Bans.Count - config.MaxEntries);

            if (!config.NotifyStaff) return;

            string when = expiry == 0
                ? Msg("Permanent")
                : Msg("Expires", null, DateTimeOffset.FromUnixTimeSeconds(expiry).UtcDateTime.ToString("yyyy-MM-dd HH:mm 'UTC'"));
            string line = Msg("BanReported", null, playerName ?? "unknown", steamId, reason ?? "no reason", when);

            foreach (var player in BasePlayer.activePlayerList)
            {
                if (player != null && permission.UserHasPermission(player.UserIDString, PermStaff))
                    player.ChatMessage(line);
            }
        }

        private void OnPlayerUnbanned(string playerName, ulong playerId, string reason)
        {
            data.Bans.RemoveAll(b => b.SteamId == playerId.ToString());
        }

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

The plugin listens for OnUserBanned and appends an entry to oxide/data/BanAuditLog.json holding the player name, Steam ID, IP, reason, expiry and the current time. It prints a chat line to every online player who has the banauditlog.staff permission, formatting the expiry as permanent or a date via DateTimeOffset.FromUnixTimeSeconds. Ban history is kept to the configured maximum, oldest entries dropped first. OnPlayerUnbanned is also implemented to clear any matching IP entry from the running report, and the data file is written in Unload so restarts do not lose recent bans.

Common use cases

  • External ban sync

    Push every ban to a shared file or in-memory list that a companion plugin reads, so aliases and reasons stay consistent across the server network.

  • Staff audit trail

    Keep a timestamped record of who was banned, by what reason and until when, without relying on a database plugin.

  • Link bans to player data

    Use the string Steam ID to look up stored player profiles and mark the account as banned for future reference.

Pitfalls

  • Address is not always a real IP

    Offline ID bans and console banid produce the literal string "0", so do not treat it as a valid address when comparing or geolocating.

  • This is after the fact

    The user table entry already exists when the hook fires, so kicking, removing or cancelling the ban from inside the hook is not supported here.

  • Expiry is a Unix timestamp, not a duration

    0 means permanent. Passing it straight to a timespan or date formatter without checking will show 1970.

Build a plugin with OnUserBanned

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

Prompt: External ban sync using the OnUserBanned 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 OnUserBanned

Generate