OnPlayerLootEnd
OnPlayerLootEnd is called when a player closes a loot session, right before the game runs its own close-loot cleanup on PlayerLoot.Clear.
void OnPlayerLootEnd(PlayerLoot playerLoot)Parameters
| Parameter | Type | Description |
|---|---|---|
playerLoot | PlayerLoot | The PlayerLoot component of the player who stopped looting. Its baseEntity is the BasePlayer, and entitySource/inventory still point at the container being closed at the moment the hook fires. |
Return value
Return value is ignored. The original PlayerLoot.Clear code always continues with HasClosedLoot(), MarkDirty() and the PlayerStoppedLooting message to the looted entity, so returning a value or null has no effect on the game.
How it works
The hook fires from PlayerLoot.Clear, so it runs whenever the server's loot state for a player is torn down: the player presses the loot key again, walks out of range, closes the loot UI, dies, or the container is emptied or destroyed. Because the call sits after the early IsLooting() check and before HasClosedLoot() and the PlayerStoppedLooting message, you get the hook only for real loot sessions, and you see the state while the player is still considered to be looting. The single argument is the PlayerLoot component; playerLoot.baseEntity is the owning BasePlayer and playerLoot.entitySource / playerLoot.inventory identify what was being looted (a box, corpse, oven or another player). You cannot narrow this by parameter type usefully — there is only one PlayerLoot signature — so null-check the component and its owner. Use it to close UIs you opened with the loot session, log what players searched, or hand out rewards tied to a container. Keep it cheap: it can fire several times per minute per player.
Example plugin: Log container searches and close a custom loot UI
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Loot Session Tracker", "RustPluginGenerator", "1.0.0")]
[Description("Logs container searches and clears a custom loot UI when a loot session ends")]
public class LootSessionTracker : RustPlugin
{
private const string PermView = "lootsessiontracker.view";
private PluginConfig config;
private readonly Dictionary<ulong, LootRecord> records = new Dictionary<ulong, LootRecord>();
private class PluginConfig
{
[JsonProperty("Write loot ends to server log")]
public bool LogToConsole { get; set; } = false;
}
private class LootRecord
{
public string ShortName;
public float When;
}
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);
private void Init()
{
permission.RegisterPermission(PermView, this);
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, "LootOverlay");
}
}
private void OnPlayerLootEnd(PlayerLoot playerLoot)
{
if (playerLoot == null) return;
var player = playerLoot.baseEntity as BasePlayer;
if (player == null || player.IsNpc) return;
CuiHelper.DestroyUi(player, "LootOverlay");
var entity = playerLoot.entitySource;
if (entity == null || entity.IsDestroyed) return;
records[player.userID.Get()] = new LootRecord
{
ShortName = entity.ShortPrefabName,
When = UnityEngine.Time.realtimeSinceStartup
};
if (config.LogToConsole)
{
Puts($"{player.displayName} stopped looting {entity.ShortPrefabName}");
}
}
[ChatCommand("lastloot")]
private void CmdLastLoot(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermView))
{
SendReply(player, "You do not have permission to use this command.");
return;
}
if (args == null || args.Length == 0)
{
SendReply(player, "Usage: /lastloot <playerId>");
return;
}
if (!ulong.TryParse(args[0], out ulong targetId))
{
SendReply(player, "That is not a valid player id.");
return;
}
if (!records.TryGetValue(targetId, out LootRecord record))
{
SendReply(player, "No loot activity recorded for that player.");
return;
}
var secondsAgo = Mathf.Max(0f, UnityEngine.Time.realtimeSinceStartup - record.When);
SendReply(player, $"Last looted container: {record.ShortName}, {secondsAgo:F0}s ago.");
}
}
}The plugin records loot activity per player and cleans up after it. OnLootEntity (fired when a player opens a storage container) is not used here; instead the example pairs OnPlayerLootEnd with a [ChatCommand] that reports the last container searched. When a loot session ends, the hook null-checks the PlayerLoot, its owner and the looted entity, then stores the prefab name in a per-player dictionary with a timestamp taken from UnityEngine.Time.realtimeSinceStartup. It also destroys a LootPanel CUI element so a custom overlay never sticks on screen after the player leaves the box, and logs to chat only if the config enables it. The /lastloot command lets staff check where a given player last searched, making it useful for auditing raid behavior.
Common use cases
Track loot searches for staff audits
Record which container each player searched and when, then expose it through a command so admins can review suspicious behavior on raid-heavy servers.
Close custom loot UIs
If you draw a CUI panel while a player is looting, destroy it in OnPlayerLootEnd so the overlay never lingers after the loot window is gone.
Time-limited loot sessions
Start a timer when a player opens a container and cut off access or apply a cooldown once OnPlayerLootEnd reports that the session is over.
Pitfalls
Do not block looting here
The hook fires after the loot session has already been closed, and its return value is ignored. To deny access use CanLootEntity or CanLootPlayer instead.
entitySource can be null or already gone
If the container was destroyed or the corpse despawned, entitySource may be null or IsDestroyed. Always null-check before reading prefab names or positions.
Not every close path goes through Clear
The hook is tied to PlayerLoot.Clear, so it reflects the game's ordinary close-loot flow. Do not assume it fires exactly once per opened container — a player can reopen and close repeatedly.
Plugin recipes that use OnPlayerLootEnd
Build a plugin with OnPlayerLootEnd
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 OnPlayerLootEnd
Generate