OnDoorClosed
OnDoorClosed is called when a player closes a door, after the game has already started its close animation and blockage check. It fires from Door.RPC_CloseDoor.
void OnDoorClosed(Door door, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
door | Door | The Door entity that was just closed. Use `.net.ID.Value` to identify it, or cast to a subtype such as `Door`'s building relation via `door.GetBuildingPrivilege()`. |
player | BasePlayer | The BasePlayer who triggered the close RPC. It can be null in edge cases (e.g. a desynced/dead connection), so null-check before reading `UserIDString`. |
Return value
The return value is ignored. The original RPC_CloseDoor method returns void and the hook is called with continue semantics, so returning null, a value, or nothing at all has no effect on the door closing.
How it works
This hook fires from Door.RPC_CloseDoor, the client RPC a player sends when they press use on an open door to shut it. By the time it runs, the server has already set the door to closing (SetNavMeshLinkEnabled(false)), started StartCheckingForBlockages, and fired the door's own closed event. The arguments are the Door entity itself and the BasePlayer who closed it. Use the door to read identity (door.net.ID.Value), position, or owner via door.OwnerID, and the player to check permissions, teams, or inventory. Declaring the first parameter as a more specific type (for example Door subtypes are limited, so leave it as Door) does not change much here; Oxide dispatch just requires arguments to match your declared types. The hook is for reaction, not prevention: because it runs after the door is already closing, you cannot veto the close by returning a value. Common uses are logging door activity, closing nearby doors as a group, or reacting to security events.
Example plugin: Log every door close to a per-player activity file
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Door Close Logger", "RustPluginGenerator", "1.0.0")]
[Description("Logs which doors players close, with timestamps, to a data file.")]
public class DoorCloseLogger : RustPlugin
{
private const string PermAdmin = "doorcloselogger.admin";
private class Entry
{
[JsonProperty("Door entity id")] public ulong DoorId;
[JsonProperty("Door prefab")] public string Prefab;
[JsonProperty("Position")] public string Position;
[JsonProperty("Closed at (UTC)")] public string ClosedAt;
}
private class PluginConfig
{
[JsonProperty("Max entries per player")] public int MaxEntriesPerPlayer = 50;
}
private PluginConfig config;
private Dictionary<string, List<Entry>> log;
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>
{
["Log"] = "You closed {0} at {1}.",
["Usage"] = "Usage: /doorlog dump <playerId>",
["NoData"] = "No door closes recorded for {0}.",
["Header"] = "Recent door closes for {0}:"
}, 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(PermAdmin, this);
}
private void OnServerInitialized()
{
log = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, List<Entry>>>(Name)
?? new Dictionary<string, List<Entry>>();
}
private void OnDoorClosed(Door door, BasePlayer player)
{
if (door == null || player == null || player.IsNpc) return;
if (log == null) log = new Dictionary<string, List<Entry>>();
List<Entry> list;
if (!log.TryGetValue(player.UserIDString, out list))
{
list = new List<Entry>();
log[player.UserIDString] = list;
}
var pos = door.transform.position;
list.Add(new Entry
{
DoorId = door.net.ID.Value,
Prefab = door.ShortPrefabName,
Position = string.Format("{0:F0} {1:F0} {2:F0}", pos.x, pos.y, pos.z),
ClosedAt = DateTime.UtcNow.ToString("u")
});
int cap = Mathf.Max(1, config.MaxEntriesPerPlayer);
while (list.Count > cap) list.RemoveAt(0);
}
[ChatCommand("doorlog")]
private void CmdDoorLog(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
{
SendReply(player, Msg("Usage", player.UserIDString));
return;
}
if (args.Length < 2 || args[0] != "dump")
{
SendReply(player, Msg("Usage", player.UserIDString));
return;
}
string target = args[1];
List<Entry> list;
if (log == null || !log.TryGetValue(target, out list) || list.Count == 0)
{
SendReply(player, Msg("NoData", player.UserIDString, target));
return;
}
SendReply(player, Msg("Header", player.UserIDString, target));
int shown = 0;
for (int i = list.Count - 1; i >= 0 && shown < 10; i--, shown++)
{
var e = list[i];
SendReply(player, string.Format("{0} #{1} {2} @ {3}", e.ClosedAt, e.DoorId, e.Prefab, e.Position));
}
}
private void Unload()
{
if (log != null) Interface.Oxide.DataFileSystem.WriteObject(Name, log);
}
}
}The plugin records who closes which door and when. On each OnDoorClosed call it null-checks both arguments and ignores NPC players, then appends an entry to a data structure stored in oxide/data/DoorCloseLogger. Entries are keyed by the player's UserIDString and hold the door entity id, prefab short name, world position, and UTC timestamp. The data is capped per player to avoid unbounded growth. A console command doorlog.dump lets an admin read back a player's recent closes in chat. Everything is flushed in Unload through Interface.Oxide.DataFileSystem, and messages are routed through the language file so server owners can localize the output.
Common use cases
Detect after-hours base access
Track door closes during off-hours and warn online admins when a player without permission closes doors on another player's base.
Automatic lockdown
When a raidable base's doors are closed during a raid timer, send a notification or apply a cooldown on re-opening.
Security audit log
Persist a rolling log of door closes per base so admins can replay activity when investigating theft or griefing reports.
Pitfalls
Fires after the door is already closing
You cannot stop the close here; use
CanUseLockedEntityorOnDoorOpened-side logic if you need to block interaction.Player can be null
The
playerargument comes from the RPC and may be null in rare desync cases, so always null-check before readingUserIDStringor callingChatMessage.High volume on busy servers
Doors close frequently; keep the handler cheap and never call
Puts/PrintWarninginside it.
Build a plugin with OnDoorClosed
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 OnDoorClosed
Generate