OnClientDisconnect
OnClientDisconnect is called when the server reads a disconnect reason packet from a leaving client, before the reason is written to the server log. It fires once per client that sends such a packet.
void OnClientDisconnect(Network.Connection connection, string text)Parameters
| Parameter | Type | Description |
|---|---|---|
connection | Network.Connection | The Network.Connection of the client that is leaving, carrying SteamID/IP and the string form used in server logs. May be null in unusual disconnect paths, so null-check before reading members. |
text | string | The human-readable disconnect reason string the client sent in the disconnect packet (for example a kick message or network timeout text). It is not guaranteed to be non-empty. |
Return value
The game method that triggers this hook returns void, so the hook is purely observational. Any value your method returns is ignored and cannot block, modify, or delay the disconnect.
How it works
Oxide fires this hook inside ServerMgr.ReadDisconnectReason, right after the server parses the disconnect packet from a client and before it writes the combined identity and reason line to the debug log. It does not fire for every player who leaves: the surrounding game code only calls the hook when both the packet's connection string form and the reason text are non-empty, so silent drops or clients that never send a reason bypass it.
Use connection to identify who is leaving and text to inspect or react to the stated reason (kick message, timeout text, plugin-supplied reason). This runs early in the disconnect sequence, before OnPlayerDisconnected and before the player entity is removed, so it is a good place to stamp or record the reason.
Declaring Network.Connection is required; there is no more specific player type available as a parameter, so look up the player with connection.userid when you need the BasePlayer. Keep the handler fast: there is no return value and no way to cancel the disconnect from here.
Example plugin: Log disconnect reasons with player names
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Disconnect Reason Logger", "RustPluginGenerator", "1.0.0")]
[Description("Records client disconnect reasons and flushes them to the console in batches with player names")]
public class DisconnectReasonLogger : RustPlugin
{
private const string PermView = "disconnectreasonlogger.view";
private class PluginConfig
{
[JsonProperty("Log Disconnect Reasons")]
public bool LogReasons { get; set; } = true;
[JsonProperty("Flush Interval (Seconds)")]
public float FlushInterval { get; set; } = 10f;
}
private PluginConfig config;
private Timer flushTimer;
private readonly List<string> pending = new List<string>();
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);
private void Init()
{
permission.RegisterPermission(PermView, this);
}
private void OnServerInitialized()
{
StartFlushTimer();
}
private void StartFlushTimer()
{
if (flushTimer != null) return;
float interval = Mathf.Max(1f, config.FlushInterval);
flushTimer = timer.Every(interval, FlushPending);
}
private void OnClientDisconnect(Network.Connection connection, string text)
{
if (!config.LogReasons || connection == null) return;
string reason = string.IsNullOrEmpty(text) ? "no reason given" : text;
string id = connection.userid.ToString();
string name = "unknown";
var player = BasePlayer.FindByID(connection.userid);
if (player != null && !string.IsNullOrEmpty(player.displayName)) name = player.displayName;
pending.Add(string.Format("{0} ({1}) dropped: {2}", name, id, reason));
}
private void FlushPending()
{
if (pending.Count == 0) return;
foreach (var line in pending) Puts("[Disconnect] " + line);
pending.Clear();
foreach (var player in BasePlayer.activePlayerList)
{
if (player != null && permission.UserHasPermission(player.UserIDString, PermView))
SendReply(player, "The last disconnect log batch was written to the server console.");
}
}
private void Unload()
{
if (flushTimer != null)
{
flushTimer.Destroy();
flushTimer = null;
}
FlushPending();
}
}
}The plugin captures each disconnect reason and writes a readable line with the player's name, SteamID and stated reason. Because the hook itself can fire during network teardown, it only stores the event in a short in-memory queue; a repeating timer flushes the queue and writes joined lines, keeping file/console output out of the hook. BasePlayer.FindByID resolves the name while the player still exists, falling back to the raw id when resolution fails. A config switch lets admins turn the log off without unloading the plugin, and Unload destroys the timer and flushes any remaining entries so nothing is lost.
Common use cases
Record kick and timeout reasons
Store the disconnect text alongside the SteamID so admins can review why players dropped, including timeouts versus operator kicks.
Correlate disconnects with combat
Timestamp the disconnect so it can be matched against recent deaths or reports when investigating combat logging claims.
Feed a moderator notification
Queue the reason and announce notable disconnects (for example custom kick text from your own plugins) to a staff channel through your own messaging plugins.
Pitfalls
Not called for every leave
The hook only fires when both the connection string and reason text are non-empty, so do not use it as the sole signal that a player left. Pair it with
OnPlayerDisconnectedfor cleanup logic.Connection can be null
Guard
connectionbefore readinguseridor callingToString(), and never assume the player entity still exists at this point.Keep it cheap
This runs during network teardown. Avoid file writes, chat broadcasts and heavy lookups directly in the hook; queue the data and process it on a timer.
Build a plugin with OnClientDisconnect
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 OnClientDisconnect
Generate