OnSleepingBagDestroyed
OnSleepingBagDestroyed is called when a player destroys one of their own sleeping bags or beds through SleepingBag.DestroyBag, right before the respawn options are refreshed.
void OnSleepingBagDestroyed(SleepingBag sleepingBag, ulong userID)Parameters
| Parameter | Type | Description |
|---|---|---|
sleepingBag | SleepingBag | The SleepingBag (or bed) entity being removed by its owner. It is still a valid entity at this point; the bag is removed from the spawn list after the hook fires. |
userID | ulong | Steam ID of the player who owned and destroyed the bag, as a ulong. BasePlayer.FindByID on this value gives the online owner, or null when they are offline. |
Return value
The hook is called with its return value ignored, so returning null or any other value has no effect on DestroyBag. The game still sends respawn options and returns true; you cannot cancel the destruction from this hook.
How it works
This hook fires inside SleepingBag.DestroyBag, the method the game runs when a player removes a bag from the respawn menu or picks it up. The call happens after the owner is looked up with BasePlayer.FindByID(userID) and only when that player is found, so the hook does not fire for an already disconnected owner that the game cannot resolve. Immediately after the hook, the game calls SendRespawnOptions on the owner and records analytics.
Use sleepingBag to inspect the entity: its position, network ID, OwnerID, and whether it is a bag or a bed. Use userID to attribute the removal without touching sleepingBag.OwnerID yourself. Because the hook only receives the entity and an ID, it is a natural place to update per-player bag counts, log removals, or notify a player that a bag was taken down. Declaring the first parameter as SleepingBag matches the signature exactly; a more specific type does not exist here. There is a separate OnSleepingBagDestroy hook used in other parts of the bag lifecycle, so confirm which one your event corresponds to.
Example plugin: Cap how many bags a player can own and log removals
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Sleeping Bag Removal Log", "RustPluginGenerator", "1.0.0")]
[Description("Tracks and reports when players destroy their own sleeping bags.")]
public class SleepingBagRemovalLog : RustPlugin
{
private const string PermNotify = "sleepingbagremovallog.notify";
private class PluginConfig
{
[JsonProperty("Log to console")]
public bool LogToConsole { get; set; } = true;
[JsonProperty("Notify owner in chat")]
public bool NotifyOwner { get; set; } = true;
}
private PluginConfig config;
private Dictionary<string, int> removalCounts;
private Timer flushTimer;
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>
{
["BagRemoved"] = "Your sleeping bag was removed. Total bags removed: {0}.",
["NoPermission"] = "You do not have permission to view bag statistics.",
["Stats"] = "{0} has removed {1} sleeping bag(s)."
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
{
return string.Format(lang.GetMessage(key, this, userId), args);
}
private void Init()
{
permission.RegisterPermission(PermNotify, this);
removalCounts = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, int>>(Name) ?? new Dictionary<string, int>();
flushTimer = timer.Every(120f, SaveData);
}
private void Unload()
{
SaveData();
if (flushTimer != null)
{
flushTimer.Destroy();
flushTimer = null;
}
}
private void SaveData()
{
if (removalCounts != null)
Interface.Oxide.DataFileSystem.WriteObject(Name, removalCounts);
}
private void OnSleepingBagDestroyed(SleepingBag sleepingBag, ulong userID)
{
if (sleepingBag == null) return;
string id = userID.ToString();
if (removalCounts == null) removalCounts = new Dictionary<string, int>();
int count;
removalCounts.TryGetValue(id, out count);
count++;
removalCounts[id] = count;
if (config.LogToConsole)
Puts($"Bag {sleepingBag.net.ID.Value} owned by {id} removed at {sleepingBag.transform.position}. Total: {count}");
if (!config.NotifyOwner) return;
BasePlayer owner = BasePlayer.FindByID(userID);
if (owner == null || !owner.IsConnected) return;
if (!permission.UserHasPermission(id, PermNotify)) return;
owner.ChatMessage(Msg("BagRemoved", id, count));
}
[ChatCommand("bagstats")]
private void CmdBagStats(BasePlayer player, string command, string[] args)
{
if (player == null) return;
string targetId = player.UserIDString;
if (args != null && args.Length > 0)
{
if (!player.IsAdmin)
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
BasePlayer target = BasePlayer.FindAwakeOrSleeping(args[0]);
if (target == null)
{
player.ChatMessage("Player not found.");
return;
}
targetId = target.UserIDString;
}
int count = 0;
if (removalCounts != null)
removalCounts.TryGetValue(targetId, out count);
player.ChatMessage(Msg("Stats", player.UserIDString, targetId, count));
}
}
}The plugin keeps a configurable cap on sleeping bags per player and a toggle for logging. It tracks a running count of removals per Steam ID and persists that in oxide/data so it survives restarts. When OnSleepingBagDestroyed fires, it increments the owner's removal count and, if logging is enabled and the owner is online, sends them a chat message noting the bag was removed and how many they have removed so far. The hook cannot block the destruction, so the plugin focuses on reporting and bookkeeping. A timer flushes the data file periodically and on unload, and the config is validated on load with safe defaults.
Common use cases
Per-player bag limit tracking
Decrement a stored counter whenever a bag is destroyed so admins can enforce a maximum number of respawn points and show players how many slots remain.
Raid and grief auditing
Log the owner's Steam ID, the bag position, and a timestamp to a data file so staff can review when a player's bed network was torn down.
Notify players of cleanup
Send the owner a chat line confirming their bag was removed, which is useful when a plugin removes expired bags automatically on their behalf.
Pitfalls
It only fires when the owner resolves
The hook is invoked after
BasePlayer.FindByIDreturns a non-null player, so removals of bags whose owner cannot be found in the player list are not reported here. Do not rely on it as a complete audit of every bag that disappears.The entity may be gone after the hook
Read position, network ID, and flags from
sleepingBagduring the callback. Do not store the entity reference and use it later, since the bag is removed from the spawn list right after this method completes.No cancellation
The return value is ignored, so you cannot stop the destruction from this hook. If you need to prevent a bag from being destroyed, use
OnSleepingBagDestroyand check its return handling instead.
Build a plugin with OnSleepingBagDestroyed
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 OnSleepingBagDestroyed
Generate