CanUnlock
CanUnlock is called when a player tries to open a code lock or key lock, before the lock's own gating (code entry block, whitelist, key permission) is evaluated. Returning a non-null value cancels the unlock.
object CanUnlock(BasePlayer player, CodeLock codeLock)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who is attempting to unlock, i.e. the RPCMessage sender for CodeLock/KeyLock or the opener passed to ModularCarCodeLock. Use it for permission checks, team checks and messaging. |
codeLock | CodeLock | The lock entity being unlocked. Typed as CodeLock for the CodeLock.TryUnlock call site; the same hook also fires with a KeyLock or a ModularCarCodeLock for their respective paths, so declare a narrower type if you only care about one kind. |
Return value
Return null to let the game continue with its normal unlock checks (whitelist for CodeLock, key permission for KeyLock). Return any non-null value to stop the unlock entirely. For ModularCarCodeLock, a returned bool is used directly as the result of TryOpenWithCode.
How it works
The hook fires inside CodeLock.TryUnlock when an RPCMessage arrives, and again in KeyLock.RPC_Unlock. Both places first check rpc.player.CanInteract(), that the lock is locked, and then call CanUnlock; the rest of the unlock logic runs only when the hook returns null. A third call site, ModularCarCodeLock.TryOpenWithCode, passes the entered code as an extra argument and treats a returned bool as the function result.
Use player for permission checks, raid protection rules, team ownership tests or chat feedback. Use codeLock to inspect the lock itself: OwnerID, GetParentEntity() to find the door or box it is attached to, or building privilege via the parent.
Because Oxide dispatches by parameter type, declaring BasePlayer for the player is safe, but declaring CodeLock means the hook body is skipped for KeyLock and ModularCarCodeLock calls; the same applies in reverse. To cover every lock, add separate overloads or fall back to BaseLock if you have verified it.
Returning null always allows the vanilla check to continue, so whitelists and key permissions still apply after your hook.
Other signatures
Oxide also calls CanUnlock from other places in the game with these parameter types. Declare the overload you need, or several:
object CanUnlock(BasePlayer player, KeyLock keyLock)object CanUnlock(BasePlayer player, ModularCarCodeLock modularCarCodeLock, string codeEntered)
Example plugin: Block unlocking while a raid block is active on the locked entity
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Unlock Block Zone", "RustPluginGenerator", "1.0.0")]
[Description("Prevents unlocking locks owned by players in a blocked permission group.")]
public class UnlockBlockZone : RustPlugin
{
private const string PermBlocked = "unlockblockzone.blocked";
private class PluginConfig
{
[JsonProperty("Blocked owners cannot be unlocked")]
public bool BlockOwners = true;
}
private PluginConfig config;
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 lock is protected. Try again later."
}, this);
}
private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);
private void Init()
{
permission.RegisterPermission(PermBlocked, this);
}
private object CanUnlock(BasePlayer player, CodeLock codeLock)
{
if (player == null || codeLock == null) return null;
if (!config.BlockOwners) return null;
BaseEntity parent = codeLock.GetParentEntity();
if (parent == null) return null;
ulong ownerId = parent.OwnerID;
if (ownerId == 0UL) return null;
if (!permission.UserHasPermission(ownerId.ToString(), PermBlocked))
return null;
if (player.userID.Get() == ownerId) return null;
player.ChatMessage(Msg("Blocked", player.UserIDString));
return false;
}
}
}The plugin keeps a configurable set of permission groups whose members may never unlock another player's code or key lock while a simple cooldown flag is set for the lock's owner. CanUnlock resolves the locked entity, finds the owning player via OwnerID, and checks if that owner is currently in the unlockblockzone.blocked permission group. If so, the hook returns false (non-null), which cancels the unlock and sends a chat message to the player via lang. The same plugin registers the permission in Init, so admins can grant or revoke it with oxide.grant user <id> unlockblockzone.blocked. The example deliberately uses a permission rather than a database to keep the plugin self-contained and testable.
Common use cases
Prevent raiders from opening locks they did not craft
Check the parent entity's OwnerID and compare it to the player; return non-null to cancel when they differ, or only when the owner is currently offline.
Restrict unlocking to members of a group permission
Return non-null unless
permission.UserHasPermission(player.UserIDString, ...)passes, so only staff or VIP players can open locks.Log suspicious unlock attempts
Return null and record the player, lock position and parent entity into a data file, so admins can review attempts later without blocking anything.
Pitfalls
Declaring the wrong lock type silently skips other locks
A method declared as
CanUnlock(BasePlayer, CodeLock)will not be invoked forKeyLockorModularCarCodeLockcalls. Add separate overloads when you need to cover every lock type.This hook does not bypass whitelist checks
Returning null leaves the vanilla whitelist and key permission checks in place. To actually allow an unlock the player must still be on the whitelist or hold key permission.
The player may already have the code in the UI
Returning non-null aborts the unlock but the client may have sent a code entry. Keep messages short and avoid extra UI to prevent confusing feedback loops.
Build a plugin with CanUnlock
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 CanUnlock
Generate