Skip to content
RustPluginGenerator

OnBookmarkControl

OnBookmarkControl is called when a player selects one of the computer station's saved bookmarks and is about to take control of that remote entity.

Signature
object OnBookmarkControl(ComputerStation computerStation, BasePlayer player, string text, IRemoteControllable remoteControllable)

Parameters

Hook parameters
ParameterTypeDescription
computerStationComputerStationThe ComputerStation the player is interacting with. Use it to read its position, owner or currently controlled entity.
playerBasePlayerThe BasePlayer who selected the bookmark and is trying to take control of the remote entity.
textstringThe bookmark identifier string that was selected. This is the RC identifier used to look up the remote controllable.
remoteControllableIRemoteControllableThe IRemoteControllable being started. Call GetEnt() to get the BaseEntity, or CanControl(player.userID) and MaxRange to inspect access.

Return value

Return null to allow the control switch to proceed. Return any non-null object to cancel it: the hook result replaces the extra condition in the game's if-statement, so the original body (swapping currentlyControllingEnt and sending the control update) never runs.

How it works

This hook fires inside ComputerStation.BeginControllingBookmark, right after the game has resolved the control identifier text into an IRemoteControllable and confirmed that the entity exists, is in range (MaxRange) and that the player is allowed to control it (CanControl). It runs before the station actually swaps your currently controlled entity, so it is a pre-control gate. Use player to identify who is driving, computerStation for location and ownership, text for the raw bookmark identifier, and remoteControllable plus GetEnt() if you need the concrete entity (turret, CCTV, car). The remoteControllable argument is an interface, so you cannot narrow this hook by declaring a more specific class type; the game passes the interface instance and Oxide checks the declared parameter type against it. To filter by entity kind, call GetEnt() and test with as inside the hook. Returning a value rejects the control attempt; the player stays on the previous camera view.

Example plugin: Restrict computer station control of hostile turrets

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

namespace Oxide.Plugins
{
    [Info("Bookmark Turret Lock", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks computer station bookmark control of turrets owned by other players or teams.")]
    public class BookmarkTurretLock : RustPlugin
    {
        private const string PermBypass = "bookmarkturretlock.bypass";

        private PluginConfig config;

        public class PluginConfig
        {
            [JsonProperty("Block only player turrets (skip NPC samsites and turrets)")]
            public bool PlayerTurretsOnly { get; set; } = true;

            [JsonProperty("Allow players authorised on building privilege")]
            public bool AllowAuthed { get; set; } = true;
        }

        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>
            {
                ["Denied"] = "You cannot control this turret through this computer station."
            }, this);
        }

        private void Init()
        {
            permission.RegisterPermission(PermBypass, this);
        }

        private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);

        private object OnBookmarkControl(ComputerStation computerStation, BasePlayer player, string text, IRemoteControllable remoteControllable)
        {
            if (player == null || remoteControllable == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            BaseEntity entity = remoteControllable.GetEnt();
            if (entity == null) return null;

            var turret = entity as AutoTurret;
            var samSite = entity as SamSite;
            if (turret == null && samSite == null) return null;

            ulong ownerId = turret != null ? turret.OwnerID : samSite.OwnerID;
            if (config.PlayerTurretsOnly && (ownerId == 0uL || ownerId >= 76561197960265728uL == false)) return null;

            if (IsAllowed(player, ownerId, entity)) return null;

            player.ChatMessage(Msg("Denied", player.UserIDString));
            return false;
        }

        private bool IsAllowed(BasePlayer player, ulong ownerId, BaseEntity entity)
        {
            if (ownerId == player.userID.Get()) return true;

            if (ownerId != 0uL)
            {
                BasePlayer owner = BasePlayer.FindByID(ownerId);
                if (owner != null && player.currentTeam != 0 && player.currentTeam == owner.currentTeam) return true;
            }

            if (!config.AllowAuthed) return false;

            BuildingPrivlidge privilege = entity.GetBuildingPrivilege();
            return privilege != null && privilege.IsAuthed(player);
        }

        private void Unload()
        {
            config = null;
        }
    }
}

The plugin blocks bookmark control when the remote entity is a turret owned by a player who is not on the driver's team and who has not authorized the driver. On OnBookmarkControl it reads remoteControllable.GetEnt(), checks whether it is an AutoTurret (or SamSite), and compares OwnerID against the driver and their currentTeam. Because it also asks BuildingPrivlidge.IsAuthed for the entity's building privilege, raiders can still take over turrets they have authorized on. Returning a non-null value cancels the switch, then the plugin warns the player through a language message. Admins with the bookmarkturretlock.bypass permission are skipped.

Common use cases

  • Team-only turret control

    Reject control of turrets that belong to a foreign player or team so raiders cannot hijack a base turret from a captured computer station.

  • Raid-protection for CCTV

    Block bookmark control of cameras inside building privilege you do not own, preventing remote surveillance during a raid.

  • Audit logging per station

    Record who started controlling which entity from which station, so admins can trace misuse of shared bases or clan stations.

Pitfalls

  • The argument is an interface

    Declaring the fourth parameter as BaseEntity or AutoTurret will cause Oxide to skip the call, because the value is an IRemoteControllable. Keep the interface type and use GetEnt() to inspect the entity.

  • GetEnt() may be null

    The game logs a warning when the entity is missing, but you still need a null check before casting or reading OwnerID, or the plugin will throw.

  • Range and permission already checked

    The hook only runs after CanControl and MaxRange checks pass, so if you return non-null the player gets no additional feedback from the game: send your own message.

Build a plugin with OnBookmarkControl

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

Prompt: Team-only turret control using the OnBookmarkControl 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 OnBookmarkControl

Generate