OnFreeableContainerReleaseStarted
OnFreeableContainerReleaseStarted is called when a player begins freeing a tied-down freeable loot container, right after FreeableLootContainer.RPC_FreeCrateTimer runs and the untie timer starts.
void OnFreeableContainerReleaseStarted(FreeableLootContainer freeableLootContainer, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
freeableLootContainer | FreeableLootContainer | The FreeableLootContainer entity whose release was started (for example a tied-down crate). Use it to read position, net.ID, IsDestroyed or entity flags. |
player | BasePlayer | The BasePlayer who sent the RPC, taken from the RPC message. The entity only calls the hook when IsTiedDown() is true, so this is normally the player interacting with the tied container; treat it as possibly null. |
Return value
The original method returns void and the hook call sits in the middle of the method body. Whatever your hook returns is discarded, so returning null or any value has no effect on the game and the untie timer continues.
How it works
The hook fires inside FreeableLootContainer.RPC_FreeCrateTimer, the RPC handler the client sends when a player starts the release interaction on a freeable loot container. Rust only reaches Interface.CallHook when IsTiedDown() returns true, so the call means the container is still tied and the server has just recorded startUntieTime. It does not fire on the final release, only on the start.
freeableLootContainer is the entity being freed; use it for position, net.ID.Value, OwnerID or IsDestroyed. player comes from msg.player and is the player who sent the RPC, which is normally the person interacting with the crate.
The method returns void and the return value is ignored, so you cannot block the interaction from this hook. To influence the outcome, act on the entity (for example Kill() it or send a message) instead of returning a value. Because this fires on player input, keep the handler cheap: no Puts or PrintWarning calls, and only send feedback that is actually needed.
Example plugin: Announce and log tied crate releases
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Freeable Release Announcer", "RustPluginGenerator", "1.0.0")]
[Description("Announces and rate-limits tied-down freeable loot container release attempts")]
public class FreeableReleaseAnnouncer : RustPlugin
{
private const string PermAnnounce = "freeablereleaseannouncer.announce";
private PluginConfig config;
private readonly Dictionary<ulong, int> attempts = new Dictionary<ulong, int>();
private class PluginConfig
{
[JsonProperty("Broadcast release starts")]
public bool Broadcast { get; set; } = true;
[JsonProperty("Broadcast radius (meters)")]
public float Radius { get; set; } = 80f;
[JsonProperty("Warn after this many starts per container")]
public int WarnThreshold { get; set; } = 5;
}
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>
{
["ReleaseStarted"] = "{0} is freeing a tied crate at {1}",
["SpamWarning"] = "Container {0} has been poked {1} times"
}, 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(PermAnnounce, this);
}
private void OnFreeableContainerReleaseStarted(FreeableLootContainer freeableLootContainer, BasePlayer player)
{
if (freeableLootContainer == null || freeableLootContainer.IsDestroyed) return;
ulong containerId = freeableLootContainer.net.ID.Value;
int count;
attempts.TryGetValue(containerId, out count);
count++;
attempts[containerId] = count;
if (config.WarnThreshold > 0 && count == config.WarnThreshold)
{
PrintWarning(Msg("SpamWarning", null, containerId, count));
}
if (!config.Broadcast || player == null) return;
Vector3 center = freeableLootContainer.transform.position;
string name = player.displayName ?? "Someone";
string grid = PositionToGrid(center);
foreach (BasePlayer target in BasePlayer.activePlayerList)
{
if (target == null || !target.IsConnected) continue;
if (Vector3.Distance(target.transform.position, center) > config.Radius && !target.IsAdmin) continue;
target.ChatMessage(Msg("ReleaseStarted", target.UserIDString, name, grid));
}
}
private static string PositionToGrid(Vector3 position)
{
int x = Mathf.FloorToInt((position.x + 1024f) / 146.3f);
int z = Mathf.FloorToInt((position.z + 1024f) / 146.3f);
return string.Format("{0}{1}", (char)('A' + x), z);
}
private void Unload()
{
attempts.Clear();
}
}
}The plugin listens for players starting the release of a tied freeable loot container. On each start it resolves the container's grid position using World helpers and broadcasts a formatted message from the language file so nearby players know a crate is being untied. It keeps a per-container counter in memory, keyed by net.ID.Value, and writes a warning to the console only when the same container is started more than the configured number of times, which catches players who repeatedly poke the same crate. The counter and threshold limits come from the config file, Unload clears the dictionary, and the hook itself performs no console output beyond that rate-limited warning.
Common use cases
Alert nearby players
Broadcast a message or play a sound when a tied crate starts being freed so groups know a contest is opening up at that grid.
Log release attempts
Record the player id, container id and world position for each release start so admins can review who interacted with tied containers.
Rate-limit abuse
Count starts per container and warn or punish players who spam the release RPC on the same crate.
Pitfalls
The return value cannot cancel the release
The patched method returns void and ignores the hook result. Killing the entity or acting on the player is the only way to change the outcome.
Only tied containers reach the hook
The call is guarded by IsTiedDown(), so untied containers and finished releases never fire this hook. Use OnFreeableContainerReleased or OnFreeableContainerRelease for the end of the flow.
Fires on client input, not once per container
The client can send the RPC repeatedly, so throttle logging and messages per container instead of sending them on every call.
Build a plugin with OnFreeableContainerReleaseStarted
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 OnFreeableContainerReleaseStarted
Generate