Skip to content
RustPluginGenerator

OnGroupDeleted

OnGroupDeleted is called after an Oxide permission group has been removed from the groups data by Permission.RemoveGroup. It lets plugins react to the deletion, typically by cleaning up stored references to the group.

Signature
void OnGroupDeleted(string name)

Parameters

Hook parameters
ParameterTypeDescription
namestringThe name of the Oxide permission group that is being removed. Use it to look up saved metadata (titles, team links, audit records) before the group is gone.

Return value

The return value is ignored. Returning null or any other value has no effect on Permission.RemoveGroup; the method returns true whenever the group existed, regardless of what your hook returns.

How it works

The hook fires inside Permission.RemoveGroup(string groupName) after the group has already been removed from the group store and stripped from every user that belonged to it. Because it runs after the removal, GroupExists(name) is already false and permission.GetGroupPermissions(name) returns nothing useful — only the group name string is passed. That is enough to key any side data your plugin keeps: stored titles, per-group reward tables, team links, Discord role mappings, or WebAPI cache entries. Child groups whose parent was this group have already had their parent string cleared before the hook runs, so you cannot rely on ParentGroup lookups here. The hook fires once per successful removal; a call to RemoveGroup for a name that does not exist returns early and never reaches the hook. Removal triggered from the admin panel, another plugin calling permission.RemoveGroup, or RCON all go through the same method and therefore all fire the hook. The return value is ignored, so this is a notification-only hook.

Example plugin: Cascade-delete plugin data linked to a removed group

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

namespace Oxide.Plugins
{
    [Info("Group Cleanup", "RustPluginGenerator", "1.0.0")]
    [Description("Tracks administrator-marked permission groups and removes the stored record when the group is deleted.")]
    public class GroupCleanup : RustPlugin
    {
        private const string PermUse = "groupcleanup.use";
        private const string DataFileName = "GroupCleanup";

        private PluginConfig config;
        private StoredData data;

        private class PluginConfig
        {
            [JsonProperty("Broadcast removals to all players")]
            public bool BroadcastRemovals { get; set; } = false;
        }

        private class GroupRecord
        {
            [JsonProperty("Label")]
            public string Label { get; set; }

            [JsonProperty("Marked at (unix)")]
            public int MarkedAt { get; set; }
        }

        private class StoredData
        {
            [JsonProperty("Tracked groups")]
            public Dictionary<string, GroupRecord> Groups { get; set; } = new Dictionary<string, GroupRecord>();
        }

        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>
            {
                ["Marked"] = "Group '{0}' is now tracked with label '{1}'.",
                ["NotMarked"] = "Group '{0}' is not tracked.",
                ["Removed"] = "Group '{0}' was deleted; its tracked record has been removed.",
                ["ListHeader"] = "Tracked groups ({0}):",
                ["ListEntry"] = "  {0} - {1}",
                ["NoGroups"] = "No tracked groups.",
                ["NoPermission"] = "You do not have permission to use this command."
            }, 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(PermUse, this);
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(DataFileName) ?? new StoredData();
            if (data.Groups == null) data.Groups = new Dictionary<string, GroupRecord>();
        }

        private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(DataFileName, data);

        private void OnGroupDeleted(string name)
        {
            if (string.IsNullOrEmpty(name) || data == null) return;
            if (!data.Groups.Remove(name)) return;

            SaveData();

            if (config.BroadcastRemovals)
            {
                Server.Broadcast(Msg("Removed", null, name));
            }
            else
            {
                PrintWarning($"Tracked group '{name}' was deleted; record removed.");
            }
        }

        [ConsoleCommand("groupcleanup.mark")]
        private void CmdMark(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !permission.UserHasPermission(player.UserIDString, PermUse))
            {
                arg.ReplyWith(Msg("NoPermission", player.UserIDString));
                return;
            }

            string groupName = arg.GetString(0, string.Empty);
            if (string.IsNullOrEmpty(groupName) || !permission.GroupExists(groupName))
            {
                arg.ReplyWith($"Group '{groupName}' does not exist.");
                return;
            }

            string label = arg.GetString(1, groupName);
            data.Groups[groupName] = new GroupRecord { Label = label, MarkedAt = Facepunch.Math.Epoch.Current };
            SaveData();
            arg.ReplyWith(Msg("Marked", player?.UserIDString, groupName, label));
        }

        [ConsoleCommand("groupcleanup.list")]
        private void CmdList(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !permission.UserHasPermission(player.UserIDString, PermUse))
            {
                arg.ReplyWith(Msg("NoPermission", player.UserIDString));
                return;
            }

            if (data.Groups.Count == 0)
            {
                arg.ReplyWith(Msg("NoGroups", player?.UserIDString));
                return;
            }

            arg.ReplyWith(Msg("ListHeader", player?.UserIDString, data.Groups.Count));
            foreach (var entry in data.Groups)
            {
                arg.ReplyWith(Msg("ListEntry", player?.UserIDString, entry.Key, entry.Value.Label));
            }
        }

        private void Unload()
        {
            SaveData();
        }
    }
}

The plugin stores a small record for permission groups that its admins flag for cleanup: a display label and the time the group was registered through the console command groupcleanup.mark. Data lives in oxide/data/GroupCleanup.json. When OnGroupDeleted fires, the hook removes that record, writes the file only if something actually changed, and broadcasts a short notice so admins know the referenced data is gone. groupcleanup.mark <group> and groupcleanup.list are console commands available to admins; unload also saves the file.

Common use cases

  • Clean up per-group economy or reward tables

    Delete reward multipliers, salaries or shop discounts stored in your plugin data the moment an admin removes the group, so stale config never lingers.

  • Sync group removals to external systems

    Drop cached role mappings or cached permission snapshots keyed by group name, so a recreated group with the same name starts from scratch instead of inheriting old state.

  • Audit trail for permission changes

    Record who removed a group and when by pairing the hook with OnGroupCreated and OnUserGroupRemoved into a session log written to oxide/data.

Pitfalls

  • The group is already gone

    The hook fires after groupsData.Remove, so GroupExists(name), GetGroupPermissions(name) and parent-group lookups all fail. Capture anything you need from your own stored data before removal or in an earlier hook.

  • Only fires on successful removal

    Calling RemoveGroup with a name that does not exist returns early and never reaches the hook. Do not treat missing calls as proof the removal failed — check GroupExists before removing if you need certainty.

  • Easy to over-log or over-broadcast

    Bulk cleanup scripts can delete many groups in a short loop, producing a flood of chat messages or writes. Batch broadcast notices with a timer or only save data once at the end.

Build a plugin with OnGroupDeleted

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

Prompt: Clean up per-group economy or reward tables using the OnGroupDeleted 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 OnGroupDeleted

Generate