OnGroupTitleSet
OnGroupTitleSet is called by Oxide's permission system after a group's title has already been changed through Permission.SetGroupTitle. The title value stored in the group data is final when the hook fires.
void OnGroupTitleSet(string name, string title)Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The group name whose title was changed. This is the group identifier, not the readable title. |
title | string | The new title string that was just written to the group and is now stored in the permission data. |
Return value
The return value is ignored. The original method has already assigned the title and returns true regardless of what the hook returns, so returning a value has no effect on the permission system.
How it works
This hook fires inside Permission.SetGroupTitle after the group has been looked up, after the new title has been written to the group data, and only when the new title differs from the stored one. Calling SetGroupTitle with a title identical to the current one returns early and never reaches the hook, so duplicates are not reported. The two arguments are the group name and the fresh title, which is enough to mirror the change into your own storage, a Discord relay, or a naming convention check.
Because the hook comes from the permission layer rather than a patched game method, it fires for every caller: Oxide commands such as oxide.group set, admin plugins calling permission.SetGroupTitle directly, and Carbon's compatible permission API when it loads Oxide plugins. It does not fire when a group is created (see OnGroupCreated) or when a group's rank or parent changes.
There is no extra narrowing available: the signature is fixed as two strings. The hook is informational only and runs after the change, so it cannot veto the new title. Keep the handler light and avoid calling SetGroupTitle from inside it with a different value, which would re-enter the hook.
Example plugin: Snapshot group titles to a data file and report changes
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Group Title Audit", "RustPluginGenerator", "1.0.0")]
[Description("Snapshots Oxide group titles to a data file and reports title changes")]
public class GroupTitleAudit : RustPlugin
{
private const string PermView = "grouptitleaudit.view";
private PluginConfig config;
private StoredTitles data;
private Timer flushTimer;
private bool dirty;
private class PluginConfig
{
[JsonProperty("Flush interval in seconds")]
public float FlushInterval { get; set; } = 10f;
[JsonProperty("Log title changes to server console")]
public bool LogChanges { get; set; } = true;
}
private class StoredTitles
{
[JsonProperty("Group titles")]
public Dictionary<string, string> Titles { get; set; } = new Dictionary<string, string>();
[JsonProperty("Last updated (UTC)")]
public string Updated { get; set; } = string.Empty;
}
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>
{
["TitleChanged"] = "Group '{0}' title set to '{1}'",
["Snapshot"] = "Stored {0} group titles. Last update: {1}",
["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(PermView, this);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredTitles>(Name) ?? new StoredTitles();
}
private void OnServerInitialized()
{
flushTimer = timer.Every(config.FlushInterval, Flush);
}
private void OnGroupTitleSet(string name, string title)
{
if (string.IsNullOrEmpty(name)) return;
string previous;
data.Titles.TryGetValue(name, out previous);
data.Titles[name] = title;
dirty = true;
if (config.LogChanges && previous != title)
Puts(Msg("TitleChanged", null, name, title));
}
private void Flush()
{
if (!dirty) return;
dirty = false;
data.Updated = DateTime.UtcNow.ToString("u");
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
[ChatCommand("grouptitles")]
private void CmdGroupTitles(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermView))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
player.ChatMessage(Msg("Snapshot", player.UserIDString, data.Titles.Count, data.Updated));
}
private void Unload()
{
if (flushTimer != null)
{
flushTimer.Destroy();
flushTimer = null;
}
Flush();
}
}
}The plugin keeps a record of every group title it has seen in oxide/data/GroupTitleAudit, loaded on startup. When OnGroupTitleSet fires it compares the incoming title against the stored one, writes the new value, and marks the data dirty. Because the hook can fire during bulk admin work, the plugin batches writes with a repeating timer instead of hitting the data file on every call; the pending flag is set in the hook and flushed every few seconds. A chat command lets permitted admins print the current snapshot, which is useful for spotting groups renamed by hand. The hook itself only does dictionary work, so it stays cheap even if many groups are re-titled in sequence.
Common use cases
Mirror titles into external state
Store group titles in a data file or send them to a chat bridge so other tools display the same labels the permission system uses.
Enforce a naming convention
Warn staff in console when a new title does not match your scheme, for example a prefix that groups by server or team.
Audit trail for permission changes
Record the group name, old title and new title with a timestamp so you can trace who renamed a group and when.
Pitfalls
Hook does not fire on identical titles
SetGroupTitlereturns early when the new title equals the stored one, so a repeated rename to the same value produces no hook call and no audit entry.Fires after the change, not before
The title is already written when the hook runs. You cannot reject or modify the new title from here; validate earlier in whatever command or plugin performs the rename.
Beware re-entrancy
Calling
permission.SetGroupTitlefrom inside the hook with a different value triggers the hook again. If you sanitise titles, guard against loops or do the work on a timer.
Build a plugin with OnGroupTitleSet
Describe what should happen and get a compiled plugin that uses this hook.
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 OnGroupTitleSet
Generate