OnStashOcclude
OnStashOcclude is called when a buried stash runs its occlusion check and detects that something is blocking the spot above it. Returning a non-null value stops the stash from dropping its items and being destroyed.
object OnStashOcclude(StashContainer stashContainer)Parameters
| Parameter | Type | Description |
|---|---|---|
stashContainer | StashContainer | The StashContainer (buried stash) whose occlusion check is running. It is still alive and can be inspected for position and inventory; returning a non-null value keeps it from being destroyed. |
Return value
Returning null (or nothing) lets vanilla behavior continue: the stash drops all items and is killed. Returning any non-null value skips that block, so the stash stays in place with its contents intact.
How it works
The hook fires inside StashContainer.DoOccludedCheck, which runs a downward sphere cast from 5 units above the stash with a 0.25 radius. When the cast hits geometry (mask 2097152, the construction/world layer), Rust calls OnStashOcclude; if every hook returns null, the stash drops its contents on the ground and is killed. This is how the game removes stashes that ended up inside a wall, foundation or building after placement.
The only parameter is the stash itself. Use its transform.position to log or report the location, and its inventory if you want to inspect what would be lost. The method returns void, so the return value only controls whether the vanilla destroy path runs; it does not become any result.
You can narrow or filter by other state, but the parameter type is always StashContainer; declaring it differently would simply stop the hook from being called. Because server admins sometimes want stashes to survive world changes, this hook is the clean place to veto the removal. Keep the body light: it runs each time a stash re-checks occlusion, not only once.
Example plugin: Keep buried stashes from being destroyed when occluded
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Stash Occlusion Protector", "RustPluginGenerator", "1.0.0")]
[Description("Keeps stashes from being destroyed when they become occluded, for players with permission")]
public class StashOcclusionProtector : RustPlugin
{
private const string PermProtect = "stashocclusionprotector.protect";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Protect stashes from occlusion cleanup")]
public bool ProtectStashes = true;
}
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>
{
["Protected"] = "Stash protection toggled for {0}: {1}",
["NoPerm"] = "You do not have permission to use this command.",
["NoPlayer"] = "Player not found."
}, 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(PermProtect, this);
}
private object OnStashOcclude(StashContainer stashContainer)
{
if (stashContainer == null) return null;
if (!config.ProtectStashes) return null;
if (permission.UserHasPermission(stashContainer.OwnerID.ToString(), PermProtect))
return true;
return null;
}
[ChatCommand("stashprotect")]
private void CmdStashProtect(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermProtect))
{
SendReply(player, Msg("NoPerm", player.UserIDString));
return;
}
if (args == null || args.Length < 1)
{
SendReply(player, "Usage: /stashprotect <player name or id>");
return;
}
BasePlayer target = BasePlayer.FindAwakeOrSleeping(args[0]);
if (target == null)
{
SendReply(player, Msg("NoPlayer", player.UserIDString));
return;
}
bool has = permission.UserHasPermission(target.UserIDString, PermProtect);
if (has)
permission.RevokeUserPermission(target.UserIDString, PermProtect);
else
permission.GrantUserPermission(target.UserIDString, PermProtect, this);
SendReply(player, Msg("Protected", player.UserIDString, target.displayName, !has));
}
}
}The plugin has one config switch, Protect Stashes, and a permission stashocclusionprotector.protect. In OnStashOcclude it checks the config and whether the stash's owner holds the permission; if both pass it returns true, which makes Oxide treat the hook as handled and skips the vanilla drop-and-kill path. Otherwise it returns null so unproteced stashes are cleaned up as normal. A chat command lets admins whitelist a player, so only chosen players keep their stashes through building changes or terrain edits.
Common use cases
Whitelist survival for donor stashes
Only skip the destroy path when the stash owner has a VIP permission, so paid players keep stashes that end up under a build.
Emergency-disable the cleanup
A global config toggle lets admins stop stashes dropping their loot during map edits or wipe transitions without editing server files.
Audit occluded stashes
Log the stash position and owner to help track down stashes that keep triggering occlusion after building or terrain changes.
Pitfalls
Returning true unconditionally keeps every buried stash forever
The hook fires because something is physically above the stash. If you always return non-null, stashes inside foundations, walls and terrain will never be removed and loot stays hidden.
The stash is still alive when the hook fires
Do not call Kill or take items yourself expecting to prevent the drop; the check happens before those actions, so operate only on the state you intend to keep.
Owner is not always a player
StashContainer.OwnerID is a ulong; match it via
userIDfrom BasePlayer.FindByID or compare the stash owner's user ID string, not the display name.
Build a plugin with OnStashOcclude
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 OnStashOcclude
Generate