Skip to content
RustPluginGenerator

OnLootEntity

OnLootEntity is called when a player starts looting an entity, right after PlayerLoot.StartLootingEntity records the entity as the loot source. It fires for containers, corpses, dropped item containers and any other lootable entity.

Signature
void OnLootEntity(BasePlayer player, BaseEntity targetEntity)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer who is opening the loot panel. This is the PlayerLoot owner, not necessarily the owner of the container. Null-check it before use.
targetEntityBaseEntityThe BaseEntity being looted, e.g. a StorageContainer, LootableCorpse, DroppedItemContainer or another ILootableEntity. Cast it to the specific type you care about.

Return value

The return value is ignored. PlayerLoot.StartLootingEntity continues regardless of what your hook returns, so returning false or null does not block the loot action. To cancel looting, hook CanLootEntity instead.

How it works

The hook fires inside PlayerLoot.StartLootingEntity, immediately after the game verifies the entity is server-side and stores it as entitySource, but before the loot panel is marked dirty and before LastLootedBy is written on the target. That means you see the loot event before the container's loot state is finalized, so changes you make here still affect the panel the player is about to see.

player is the player performing the loot action. targetEntity is the entity being opened; it is typed as BaseEntity, so cast it with as to StorageContainer, LootableCorpse, DroppedItemContainer, BaseOven, BradleyAPC and so on. Declaring a more specific type for the second parameter makes Oxide skip the call when the entity is a different type, which is useful if you only care about one kind of container. The same applies to player if you declared a narrower type, though BasePlayer is almost always correct here.

Use it for per-container rules: logging who opened what, applying loot cooldowns, populating or locking specific containers, or tracking hacking progress. Because the hook does not return a value, you cannot cancel the loot from here; use CanLootEntity for that.

Example plugin: Log container loots with player and entity details

LootAuditLog.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Loot Audit Log", "RustPluginGenerator", "1.0.0")]
    [Description("Records who loots which containers and exposes the recent history to admins.")]
    public class LootAuditLog : RustPlugin
    {
        private const string PermView = "lootauditlog.view";

        private PluginConfig config;
        private List<LootEntry> entries = new List<LootEntry>();

        private class PluginConfig
        {
            [JsonProperty("Maximum stored entries")]
            public int MaxEntries = 200;

            [JsonProperty("Log corpses")]
            public bool LogCorpses = true;

            [JsonProperty("Log dropped item containers")]
            public bool LogDroppedContainers = true;
        }

        private class LootEntry
        {
            [JsonProperty("steamId")]
            public string SteamId;

            [JsonProperty("playerName")]
            public string PlayerName;

            [JsonProperty("prefab")]
            public string Prefab;

            [JsonProperty("position")]
            public string Position;

            [JsonProperty("time")]
            public int Time;
        }

        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>
            {
                ["NoEntries"] = "No loot entries recorded yet.",
                ["Header"] = "Last {0} loot entries:",
                ["Line"] = "{0} ({1}) looted {2} at {3}",
                ["NoPermission"] = "You do not have permission to use this command."
            }, 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(PermView, this);
        }

        private void OnServerInitialized()
        {
            var saved = Interface.Oxide.DataFileSystem.ReadObject<List<LootEntry>>(Name);
            if (saved != null) entries = saved;
        }

        private void OnLootEntity(BasePlayer player, BaseEntity targetEntity)
        {
            if (player == null || targetEntity == null) return;

            if (!config.LogCorpses && targetEntity is LootableCorpse) return;
            if (!config.LogDroppedContainers && targetEntity is DroppedItemContainer) return;
            if (!(targetEntity is StorageContainer) &&
                !(targetEntity is LootableCorpse) &&
                !(targetEntity is DroppedItemContainer)) return;

            entries.Add(new LootEntry
            {
                SteamId = player.UserIDString,
                PlayerName = player.displayName,
                Prefab = targetEntity.ShortPrefabName,
                Position = targetEntity.transform.position.ToString("F1"),
                Time = Facepunch.Math.Epoch.Current
            });

            while (entries.Count > config.MaxEntries)
                entries.RemoveAt(0);
        }

        [ChatCommand("lootlog")]
        private void CmdLootLog(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermView))
            {
                SendReply(player, Msg("NoPermission", player.UserIDString));
                return;
            }

            if (entries.Count == 0)
            {
                SendReply(player, Msg("NoEntries", player.UserIDString));
                return;
            }

            int count = Mathf.Min(10, entries.Count);
            SendReply(player, Msg("Header", player.UserIDString, count));

            for (int i = entries.Count - count; i < entries.Count; i++)
            {
                var e = entries[i];
                var ts = DateTimeOffset.FromUnixTimeSeconds(e.Time).LocalDateTime.ToString("HH:mm:ss");
                SendReply(player, Msg("Line", player.UserIDString, e.PlayerName, e.SteamId, e.Prefab, ts));
            }
        }

        private void OnServerSave()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, entries);
        }

        private void Unload()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, entries);
        }
    }
}

The plugin writes a data file entry for every time a player opens a lootable container, recording the player's steam id, display name, the entity's prefab name, world position and a Unix timestamp. OnLootEntity is used to capture the event the moment StartLootingEntity fires, then the entry is buffered in memory. OnServerSave and Unload flush the buffer to oxide/data/LootAuditLog via Interface.Oxide.DataFileSystem, so disk writes happen rarely instead of on every loot. A /lootlog chat command (permission lootauditlog.view) prints the last 10 entries so admins can review recent container access without opening the file. Config controls how many entries the buffer keeps and whether corpses and dropped item containers are recorded.

Common use cases

  • Track container access for admin audits

    Record every player/container pair with a timestamp so you can see who opened a locked box or took from a corpse. Keep the write in memory and flush on save to avoid disk churn.

  • Apply per-container loot cooldowns

    Store the last loot time per entity id and, when a player opens the same box too soon, notify them via chat or drop a marker. Because the hook fires before the panel is shown, you can also clear the container here.

  • Restock or reseed special containers

    For event boxes, insert items into the StorageContainer inventory in OnLootEntity so the player sees the contents on the panel that is about to open, rather than after a delay.

Pitfalls

  • Does not cancel looting

    The hook is informational; its return value is ignored. If you need to deny opening a container, use CanLootEntity which is checked earlier and can return false.

  • targetEntity is not always a container

    Corpses, dropped item containers and vehicles all reach this hook. Cast with as and null-check the result; assuming StorageContainer will throw or silently skip on other lootable types.

  • Fires on every loot action

    Avoid Puts, PrintWarning or per-loot disk writes. Cache in memory and flush on save or shutdown to keep the server responsive during busy raids.

Plugin recipes that use OnLootEntity

Build a plugin with OnLootEntity

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Track container access for admin audits using the OnLootEntity hook, with a config file and a permission
Generate this plugin

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 OnLootEntity

Generate