OnSleepingBagDestroy
OnSleepingBagDestroy is called when a sleeping bag or bed removes a player's spawn assignment through SleepingBag.DestroyBag, before the assignment is removed. Returning a non-null value cancels the destruction.
object OnSleepingBagDestroy(SleepingBag sleepingBag, ulong userID)Parameters
| Parameter | Type | Description |
|---|---|---|
sleepingBag | SleepingBag | The SleepingBag entity being destroyed. Its deployerUserID, net.ID and position identify the bag; the entity is still valid inside the hook. |
userID | ulong | The player whose bag assignment is being removed (the owner of the spawn point), passed by the game as a ulong. |
Return value
Returning anything non-null stops the original method and makes DestroyBag return false, so the spawn assignment is left in place and the bag keeps working. Returning null lets the game continue and remove the bag for that player normally.
How it works
This hook fires inside SleepingBag.DestroyBag, which is the path the game takes when a bag stops being a valid spawn point for a player — the usual triggers are the bag being picked up, destroyed, or otherwise cleared from the owner's respawn list. It fires after the method's earlier validation and before RemoveBagForPlayer runs, so the assignment still exists while your hook executes.
sleepingBag is the entity, so you can read net.ID.Value, transform.position and deployerUserID. userID is the player the assignment is being removed for; it is not always the deployer for shared/team setups, so use it rather than assuming ownership. Negative net.ID values mark spawned bags, which matters if you track bags by id.
Declaring SleepingBag keeps the hook called only for bags and beds of that type; narrowing to a more specific subclass such as BaseOven would never match here. Because this fires on every bag removal, keep the body cheap and never log from it.
Example plugin: Protect beds and bags placed inside building privilege
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Bag Protection", "RustPluginGenerator", "1.0.0")]
[Description("Blocks removal of sleeping bags and beds placed inside another player's building privilege")]
public class BagProtection : RustPlugin
{
private const string PermAdmin = "bagprotection.admin";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Protection enabled")]
public bool Enabled { get; set; } = true;
[JsonProperty("Block bags inside foreign privilege")]
public bool BlockForeignPrivilege { get; set; } = true;
[JsonProperty("Notify owner on blocked removal")]
public bool NotifyOwner { get; set; } = true;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new System.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>
{
["Blocked"] = "This spawn point is protected while it stands inside another player's base.",
["Info"] = "Bag protection is {0}. Foreign privilege blocking is {1}."
}, 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(PermAdmin, this);
}
private object OnSleepingBagDestroy(SleepingBag sleepingBag, ulong userID)
{
if (!config.Enabled || sleepingBag == null || sleepingBag.IsDestroyed) return null;
if (!config.BlockForeignPrivilege) return null;
if (permission.UserHasPermission(userID.ToString(), PermAdmin)) return null;
var privilege = sleepingBag.GetBuildingPrivilege();
if (privilege == null || privilege.OwnerID == userID) return null;
var ownerViewer = BasePlayer.FindByID(privilege.OwnerID);
if (ownerViewer != null && privilege.IsAuthed(ownerViewer)) return null;
if (config.NotifyOwner)
{
var player = BasePlayer.FindByID(userID);
if (player != null && player.IsConnected)
{
player.ChatMessage(Msg("Blocked", player.UserIDString));
}
}
return true;
}
[ChatCommand("bagprotect")]
private void CmdBagProtect(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermAdmin)) return;
if (args == null || args.Length == 0)
{
player.ChatMessage(Msg("Info", player.UserIDString, config.Enabled, config.BlockForeignPrivilege));
return;
}
bool value;
if (!bool.TryParse(args[0], out value)) return;
config.Enabled = value;
SaveConfig();
player.ChatMessage(Msg("Info", player.UserIDString, config.Enabled, config.BlockForeignPrivilege));
}
}
}The plugin allows admins to protect sleeping bags and beds that sit inside a building privilege zone they do not own. In OnSleepingBagDestroy it first checks that protection is enabled and that the calling player is not an admin, then resolves the building privilege the bag stands on with GetBuildingPrivilege(). If the bag is inside someone else's zone and that zone's IsAuthed check fails for the bag owner, the hook sends a chat warning and returns true, which cancels the destruction. Both hook arguments are null-checked and the permission is registered in Init, keeping the hook fast enough for a hook that runs on every bag removal.
Common use cases
Raid protection for spawn points
Block destruction of beds placed inside another player's building privilege so raiders cannot wipe spawn points without taking the base.
Audit spawn point removals
Record bag id, position and owner to a data file to investigate reports of disappearing spawn points, then inspect the log through a chat command.
Keep event beds after an event ends
Return non-null for bags tagged by your event system so cleanup of the arena does not remove them until the next round starts.
Pitfalls
This does not cover every removal path
The hook only fires from
SleepingBag.DestroyBag. Other code paths that despawn the entity do not necessarily reach this method, so a non-null return is not a universal bag shield.Blocking survivors can leave dead entries
If you always cancel, spawn points that owners meant to remove stay in their respawn list. Scope the block to a condition such as building privilege or a plugin flag.
Do not assume userID is the deployer
The passed id is the player whose assignment is being removed. Read
sleepingBag.deployerUserIDwhen you need the original placer.
Build a plugin with OnSleepingBagDestroy
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 OnSleepingBagDestroy
Generate