Skip to content
RustPluginGenerator

OnCodeEntered

OnCodeEntered is called when a player submits a code at a CodeLock, before the game compares the entry to the guest code and the owner code. Returning a non-null value aborts the unlock attempt.

Signature
object OnCodeEntered(CodeLock codeLock, BasePlayer player, string text)

Parameters

Hook parameters
ParameterTypeDescription
codeLockCodeLockThe CodeLock entity the player is typing into. Use it to read OwnerID, building privilege, or to look up the code later via reflection-free means such as OnCodeChanged data.
playerBasePlayerThe BasePlayer attempting the code entry. May be null in edge cases; check it before using player.UserIDString or permission checks.
textstringThe raw string the player submitted as the code. Compare it against stored values to detect brute-force attempts or specific codes.

Return value

Returning null lets the game proceed to compare the submitted code against the guest code and owner code. Returning any non-null object stops the original UnlockWithCode method immediately, so the code is never evaluated and the lock does not unlock.

How it works

The hook fires inside CodeLock.UnlockWithCode, right after the server reads the submitted code string and checks that the player can interact, that the lock is still locked, and that code entry is not currently blocked. It runs before the game computes text == guestCode and text == code, so both correct and incorrect attempts reach the hook. The codeLock parameter identifies the lock entity, player is the one typing, and text is the exact string sent from the client. Because the game method returns void, any non-null return from your hook simply skips the rest of the original method: the lock stays locked and no unlock sound or state change happens. There is no way to force a successful unlock by returning a value here; to grant access you would need to act outside the hook (for example, unlocking the lock via another call is not exposed by this hook's return value). Declaring player as a more specific type will not help because the argument is always a BasePlayer. This is the place for lockout logic, attempt tracking, and notifications about suspicious entries.

Example plugin: Lock out repeated failed code entries

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

namespace Oxide.Plugins
{
    [Info("CodeLockAttemptLimiter", "RustPluginGenerator", "1.0.0")]
    [Description("Limits repeated failed code entries on CodeLocks and alerts admins.")]
    public class CodeLockAttemptLimiter : RustPlugin
    {
        private const string PermAdmin = "codelockattemptlimiter.admin";

        private class PluginConfig
        {
            [JsonProperty("Max attempts before cooldown")]
            public int MaxAttempts { get; set; } = 5;

            [JsonProperty("Cooldown seconds")]
            public float CooldownSeconds { get; set; } = 30f;
        }

        private PluginConfig config;
        private readonly Dictionary<ulong, int> attempts = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, float> cooldowns = new Dictionary<ulong, float>();
        private Timer cleanupTimer;

        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>
            {
                ["Cooldown"] = "Too many wrong codes. Wait {0} seconds before trying again.",
                ["Admin Alert"] = "{0} is repeatedly failing a code lock at {1}"
            }, 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(PermAdmin, this);
            cleanupTimer = timer.Every(60f, CleanupCooldowns);
        }

        private void Unload()
        {
            cleanupTimer?.Destroy();
        }

        private object OnCodeEntered(CodeLock codeLock, BasePlayer player, string text)
        {
            if (codeLock == null || player == null || string.IsNullOrEmpty(text)) return null;

            ulong key = codeLock.net.ID.Value;
            float now = UnityEngine.Time.realtimeSinceStartup;

            float until;
            if (cooldowns.TryGetValue(key, out until) && until > now)
            {
                player.ChatMessage(Msg("Cooldown", player.UserIDString, Mathf.CeilToInt(until - now)));
                return false;
            }

            NextTick(() => EvaluateResult(codeLock, player, key));
            return null;
        }

        private void EvaluateResult(CodeLock codeLock, BasePlayer player, ulong key)
        {
            if (codeLock == null || codeLock.IsDestroyed) return;

            if (!codeLock.IsLocked())
            {
                // Code was correct; reset tracking for this lock.
                attempts.Remove(key);
                cooldowns.Remove(key);
                return;
            }

            int count;
            attempts.TryGetValue(key, out count);
            count++;
            attempts[key] = count;

            if (count >= config.MaxAttempts)
            {
                cooldowns[key] = UnityEngine.Time.realtimeSinceStartup + config.CooldownSeconds;
                attempts[key] = 0;
                AlertAdmins(codeLock, player);
            }
        }

        private void AlertAdmins(CodeLock codeLock, BasePlayer player)
        {
            string message = Msg("Admin Alert", null, player.displayName, codeLock.transform.position);
            foreach (BasePlayer admin in BasePlayer.activePlayerList)
            {
                if (admin == null || !admin.IsConnected) continue;
                if (!permission.UserHasPermission(admin.UserIDString, PermAdmin)) continue;
                admin.ChatMessage(message);
            }
        }

        private void CleanupCooldowns()
        {
            float now = UnityEngine.Time.realtimeSinceStartup;
            var stale = new List<ulong>();
            foreach (var entry in cooldowns)
            {
                if (entry.Value <= now) stale.Add(entry.Key);
            }
            for (int i = 0; i < stale.Count; i++)
            {
                cooldowns.Remove(stale[i]);
                attempts.Remove(stale[i]);
            }
        }
    }
}

The plugin tracks failed attempts per lock-and-player pair. In OnCodeEntered it first clamps the code, then checks a cooldown dictionary; if the player is still on cooldown the hook returns a non-null value to stop the unlock attempt and sends a chat warning. It does not compare the code against the real one (that is the game's job), so instead it records the attempt and lets the game decide. To detect success it schedules a next-tick check that reads codeLock.IsLocked(); if the lock is open the counter is cleared, otherwise the attempt count increases and a cooldown is applied. A cleanup timer keeps the dictionary from growing unbounded.

Common use cases

  • Enforce a failure cooldown

    Count attempts per lock and player, and return a non-null value to stop the unlock path while the player is cooling down after too many wrong codes.

  • Reward correct codes with economy

    Schedule a next-tick check of codeLock.IsLocked() after the hook; if the lock opened, you know the code was correct and can grant a reward through Economics or chat.

  • Log and alert admins

    Store the submitted text with the lock position and player ID and notify online admins when a specific code or a long run of failures is observed.

Pitfalls

  • Returning true does not unlock

    Because the original method returns void, a non-null return only skips the rest of UnlockWithCode. It never forces a successful open; it is strictly a cancel signal.

  • Hook fires for every attempt

    Both correct and incorrect submissions reach the hook, so avoid blanket blocking or you will lock legitimate owners out of their own base.

  • Do not trust the text length

    The client sends whatever string it wishes, including empty or very long values. Validate and clamp before storing or displaying it.

Build a plugin with OnCodeEntered

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

Prompt: Enforce a failure cooldown using the OnCodeEntered 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 OnCodeEntered

Generate