OnDemoRecordingStop
OnDemoRecordingStop is called on the server just before a player's server-side demo recording is stopped, and returning a non-null value cancels the stop so recording continues.
object OnDemoRecordingStop(string recordFilename, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
recordFilename | string | The filename of the demo being recorded, taken from the player's network connection. Useful for logging which file was stopped, though it does not include the full path. |
player | BasePlayer | The BasePlayer whose server-side demo recording is being stopped. This is the player whose connection is recording. |
Return value
Return null (or fall through without returning) to let the server stop the recording normally. Returning any non-null object cancels the original code path, so the connection keeps recording and the OnDemoRecordingStopped hook is never fired for this stop.
How it works
This hook fires inside BasePlayer.StopServerDemoRecording, which runs when the server is asked to end a demo capture for a player's connection. It only fires when the connection exists, is a live network connection, and IsRecording is true, so spectating admins, anti-cheat capture and any plugin that starts demos via the server are the cases that reach it. The player parameter is the player being recorded and recordFilename is the file name of the recording on the server.
Because Oxide sends the argument types to your hook, declaring BasePlayer for the second parameter is correct here; there is no useful base type to narrow to since the hook always passes a real player. Keep in mind that returning a non-null value from the hook stops the original stop logic: because StopServerDemoRecording returns void, the only effect is that the recording is not stopped and the subsequent OnDemoRecordingStopped notification is not sent. If you want to let the game proceed, return null or nothing.
This pairs naturally with OnDemoRecordingStart, and the pair is useful for tracking who is recording, enforcing a maximum recording window, or alerting staff. It does not fire on the client-side demo command; only server-side recordings reach this method.
Example plugin: Enforce a maximum demo recording duration
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Demo Recording Limiter", "RustPluginGenerator", "1.0.0")]
[Description("Warns staff when a server-side demo recording has run for too long and blocks premature stops.")]
public class DemoRecordingLimiter : RustPlugin
{
private class PluginConfig
{
[JsonProperty("Minimum recording seconds before a stop is blocked")]
public float MinimumSeconds = 60f;
[JsonProperty("Broadcast notice to admins on blocked stop")]
public bool NotifyAdmins = true;
}
private PluginConfig config;
private readonly Dictionary<ulong, float> startTimes = new Dictionary<ulong, float>();
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>
{
["StopBlocked"] = "Demo recording is still in progress. It must run for at least {0} seconds before it can be stopped.",
["AdminNotice"] = "Blocked demo stop for {0} ({1}); recording has only run for {2} seconds."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);
private void OnDemoRecordingStart(string recordFilename, BasePlayer player)
{
if (player == null || player.IsNpc) return;
startTimes[player.userID.Get()] = UnityEngine.Time.realtimeSinceStartup;
}
private object OnDemoRecordingStop(string recordFilename, BasePlayer player)
{
if (player == null || player.IsNpc) return null;
float started;
if (!startTimes.TryGetValue(player.userID.Get(), out started)) return null;
float elapsed = UnityEngine.Time.realtimeSinceStartup - started;
if (elapsed >= config.MinimumSeconds) return null;
player.ChatMessage(Msg("StopBlocked", player.UserIDString, Math.Round(config.MinimumSeconds, 0)));
if (config.NotifyAdmins)
{
string notice = Msg("AdminNotice", null, player.displayName, recordFilename, Math.Round(elapsed, 1));
foreach (var admin in BasePlayer.activePlayerList)
{
if (admin != null && admin.IsAdmin) admin.ChatMessage(notice);
}
}
return true;
}
private void OnDemoRecordingStopped(string recordFilename, BasePlayer player)
{
if (player != null) startTimes.Remove(player.userID.Get());
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (player != null) startTimes.Remove(player.userID.Get());
}
private void Unload()
{
startTimes.Clear();
}
}
}The plugin tracks when each player's server-side demo starts and sends a warning when it has been recording for too long. It starts a per-player timer in OnDemoRecordingStart; when the configured limit expires, OnDemoRecordingStop fires for that player and returning the timer object cancels the stop while it is still active, so recording continues and staff are notified. Once the player actually stops recording (or the timer is cleared in Unload), the hook returns null and the normal stop path runs. The config exposes the recording time limit in seconds and whether to broadcast a notice.
Common use cases
Cap recording length
Cancel a stop request while a player is below the minimum recording time so short demos are not saved, or to force recordings to continue for evidence collection.
Audit who records
Log the player and filename to a data file and notify administrators when a spectating moderator ends a capture.
Warn on accidental stops
Return a non-null value for the first stop attempt, chat a warning to the player, and allow the next attempt through.
Pitfalls
Returning null still stops the demo
Only a non-null object cancels the stop; returning false or an empty string does not work as a cancel because any value, including false, is treated as a non-null result.
OnDemoRecordingStopped will not fire
When you cancel the stop, the follow-up
OnDemoRecordingStoppedhook is skipped, so plugins listening for it will miss the event.Recording is server-side only
This hook is tied to
net.connection.IsRecording, so client-side demos created outside the server connection are never seen here.
Build a plugin with OnDemoRecordingStop
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 OnDemoRecordingStop
Generate