OnClanMemberKicked
OnClanMemberKicked is called when a member is removed from a clan by a kick rather than leaving voluntarily, after the clan backend has processed the membership change but before it is persisted.
void OnClanMemberKicked(LocalClan localClan, ulong steamId, ulong bySteamId)Parameters
| Parameter | Type | Description |
|---|---|---|
localClan | LocalClan | The clan object the kicked player belonged to. Use it to read ClanId, the member list, or the clan name before the kick is reflected in your own data. |
steamId | ulong | Steam ID of the player who was removed from the clan. This is the only reliable identifier for the kicked member. |
bySteamId | ulong | Steam ID of the player who performed the kick. Compare it with localClan.Leader or simply log it to audit clan leadership actions. |
Return value
Returning a value has no effect. The patched site ignores whatever your hook returns and the Kick coroutine continues writing the kick log entry, updating membership and completing the removal.
How it works
The hook fires inside LocalClan.Kick after the game decides the removal was a kick and not a voluntary leave, immediately before the clan backend writes a "kick" entry to its database log and calls MembershipChanged on the removed player. That ordering matters: by the time the hook runs, the sender's intent (kick vs leave) is known, but the backend persistence and the removed player's membership cleanup have not happened yet.
localClan gives you the clan context (ClanId, name, leader, member list) so you can record who was affected. steamId identifies the removed member and bySteamId the actor who issued the kick. Neither carries a reason, so anything you log beyond the IDs comes from your own detection logic.
Because the server invokes the hook with concrete types, declaring LocalClan for the first parameter is required to receive calls; there is no broader base type that would match. Returning nothing is expected since the underlying method returns void.
The hook is not called for players who leave on their own; that path fires OnClanMemberLeft instead. Treat it as an audit/reaction point, not a veto point.
Example plugin: Track clan kicks and flag leadership abuse
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Clan Kick Audit", "RustPluginGenerator", "1.0.0")]
[Description("Records clan kicks, notifies the removed player and alerts on kick abuse")]
public class ClanKickAudit : RustPlugin
{
private const string PermAlerts = "clankickaudit.alerts";
private PluginConfig config;
private KickLog data = new KickLog();
private class PluginConfig
{
[JsonProperty("Notify kicked player in chat")]
public bool NotifyPlayer { get; set; } = true;
[JsonProperty("Max kicks by one actor before an alert is sent")]
public int AlertThreshold { get; set; } = 3;
[JsonProperty("Alert window in hours")]
public double AlertWindowHours { get; set; } = 24.0;
}
private class KickEntry
{
[JsonProperty("clanId")] public ulong ClanId { get; set; }
[JsonProperty("kickedSteamId")] public ulong Kicked { get; set; }
[JsonProperty("bySteamId")] public ulong By { get; set; }
[JsonProperty("utc")] public string Utc { get; set; }
}
private class KickLog
{
[JsonProperty("kicks")] public List<KickEntry> Kicks { get; set; } = new List<KickEntry>();
}
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>
{
["Kicked"] = "You were removed from clan {0} by a clan leader.",
["Alert"] = "{0} has kicked {1} clan members in the last {2} hours."
}, 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(PermAlerts, this);
}
private void OnServerInitialized()
{
data = Interface.Oxide.DataFileSystem.ReadObject<KickLog>(Name) ?? new KickLog();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void OnClanMemberKicked(LocalClan localClan, ulong steamId, ulong bySteamId)
{
if (localClan == null || steamId == 0 || bySteamId == 0) return;
data.Kicks.Add(new KickEntry
{
ClanId = (ulong)localClan.ClanId,
Kicked = steamId,
By = bySteamId,
Utc = DateTime.UtcNow.ToString("o")
});
if (config.NotifyPlayer)
{
var player = BasePlayer.FindByID(steamId);
if (player != null && player.IsConnected)
player.ChatMessage(Msg("Kicked", player.UserIDString, localClan.ClanId));
}
CheckThreshold(bySteamId);
}
private void CheckThreshold(ulong bySteamId)
{
if (config.AlertThreshold <= 0) return;
var cutoff = DateTime.UtcNow.AddHours(-Math.Abs(config.AlertWindowHours));
int count = 0;
for (int i = data.Kicks.Count - 1; i >= 0; i--)
{
var entry = data.Kicks[i];
if (entry.By != bySteamId) continue;
DateTime stamp;
if (!DateTime.TryParse(entry.Utc, out stamp) || stamp < cutoff) continue;
count++;
}
if (count < config.AlertThreshold) return;
var actor = BasePlayer.FindByID(bySteamId);
string actorName = actor != null ? actor.displayName : bySteamId.ToString();
string alert = Msg("Alert", null, actorName, count, config.AlertWindowHours);
foreach (var player in BasePlayer.activePlayerList)
{
if (player != null && permission.UserHasPermission(player.UserIDString, PermAlerts))
player.ChatMessage(alert);
}
PrintWarning(alert);
}
}
}The plugin records every clan kick into an in-memory list and persists it to oxide/data/ClanKickAudit.json on unload. Each entry stores the clan ID, the kicked Steam ID, the actor's Steam ID, and a UTC timestamp. By default the kicked player receives a chat message when connected; this can be turned off in the config. A threshold in the config raises an alert when one actor reaches the configured number of kicks inside the configured window; the alert goes to chat for holders of clankickaudit.alerts and to the console via PrintWarning.
Common use cases
Kick audit trail for staff
Store who kicked whom and when so admins can investigate claims of leaders abusing kick rights. Persist the list with
Interface.Oxide.DataFileSystem.Notify the removed player
Look up the kicked Steam ID with
BasePlayer.FindByIDand send a chat message so the player knows they were removed by a leader rather than by a bug.Sync with a Discord or in-game roster plugin
Forward the clan ID and Steam ID through
[PluginReference]to a roster or tagging plugin so member lists stay correct after a kick.
Pitfalls
Not a veto point
The hook runs after the kick decision inside the coroutine and ignores return values. You cannot cancel the removal from here; use
localClan.Leaderchecks earlier in your own command flow if you need approval logic.Do not assume the kicked player is online
steamIdis a plain ulong and may not map to a connected player. Always null-check the result ofBasePlayer.FindByIDbefore messaging.Backend may fail afterwards
The database log write and membership change happen after the hook. If the backend rejects the change your recorded audit entry may describe a kick that never completed, so treat records as intent, not confirmation.
Build a plugin with OnClanMemberKicked
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 OnClanMemberKicked
Generate