OnBookmarkInput
OnBookmarkInput is called when a player seated at a ComputerStation that is actively controlling a bookmark (CCTV/drone/turret) sends server input, just before that input is forwarded to the remote-controllable entity.
object OnBookmarkInput(ComputerStation computerStation, BasePlayer player, InputState inputState)Parameters
| Parameter | Type | Description |
|---|---|---|
computerStation | ComputerStation | The ComputerStation the player is sitting at and operating. Use it to read the station's position, flag state, or to match it against a stored station ID. |
player | BasePlayer | The BasePlayer who is seated at the computer station and producing the input. This is the controller whose camera and remote-controllable are being driven. |
inputState | InputState | The input snapshot being sent to the controlled entity. Contains buttons (InputState.IsDown), mouse delta and analog values; pass it through to UserInput if you forward input yourself. |
Return value
Return null (or nothing) to let the input be forwarded to the controlled entity as normal. Return any non-null value to block the UserInput call for that frame, so the remote entity does not receive the input. The original method returns void, so the value itself is discarded.
How it works
The hook fires from ComputerStation.PlayerServerInput, after the station's seated-player logic has run and before the IRemoteControllable.UserInput call. It only runs when the station has the Reserved2 flag set (i.e. a bookmark control session is active) and currentlyControllingEnt is a valid entity on the server. Any input the seated player sends while controlling a camera, drone, turret or similar remote entity passes through here.
Use computerStation to identify which station is being operated, player to know who is driving it, and inputState to inspect buttons, view angles or analog axes before they reach the target. Because the parameter type is ComputerStation, declaring the first argument as any base type still works, but declaring something unrelated to a ComputerStation makes Oxide skip the call entirely.
Typical uses are gating specific inputs, logging who is controlling a device, or cancelling input to freeze a remote entity. Input arrives at the server tick rate, so keep the body cheap and avoid logging on every call.
Example plugin: Freeze CCTV control for unauthorised players
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Bookmark Input Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks ComputerStation bookmark input for players without a permission")]
public class BookmarkInputGuard : RustPlugin
{
private const string PermControl = "bookmarkinputguard.control";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastWarn = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Send chat warning when blocked")]
public bool WarnOnBlock { get; set; } = true;
[JsonProperty("Warning cooldown in seconds")]
public float WarnCooldown { get; set; } = 5f;
}
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>
{
["Blocked"] = "You are not allowed to control this device.",
["NoPermission"] = "You do not have the '{0}' permission."
}, 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(PermControl, this);
}
private object OnBookmarkInput(ComputerStation computerStation, BasePlayer player, InputState inputState)
{
if (computerStation == null || player == null || inputState == null) return null;
if (permission.UserHasPermission(player.UserIDString, PermControl)) return null;
if (config.WarnOnBlock && player.IsConnected)
{
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (!lastWarn.TryGetValue(player.userID, out last) || now - last >= config.WarnCooldown)
{
lastWarn[player.userID] = now;
player.ChatMessage(Msg("Blocked", player.UserIDString));
}
}
// Non-null return stops the input from reaching the controlled entity.
return false;
}
private void Unload()
{
lastWarn.Clear();
}
}
}The plugin lets server owners name a permission that a player must hold to actually drive a remote-controllable from a ComputerStation. In OnBookmarkInput it checks the permission for the seated player, sends a throttled chat warning if they lack it, and returns a non-null value so the input is never forwarded to the camera, drone or turret. Authorised players fall through untouched. The warning uses a per-player cooldown dictionary so the chat is not spammed every server frame, and the cooldown map is cleared on unload along with any UI. Config controls the permission name and warning text through the language file.
Common use cases
Restrict remote control by permission
Only let players with a specific permission drive cameras, drones or turrets from a ComputerStation. Return a non-null value for everyone else so the input never reaches the target entity.
Audit who operates a device
Compare
computerStation.net.ID.Valueagainst a configured list and recordplayer.userIDwith a timestamp when they send input, giving admins a trail of who was driving a given device.Block control while a global lockdown is active
Keep a boolean in the plugin that admins toggle via a chat command; when it is set, return non-null so no bookmark input is forwarded anywhere until the lockdown ends.
Pitfalls
Only fires for active bookmark sessions
The injection is guarded by the Reserved2 flag and a valid
currentlyControllingEnt. If the player is seated but not controlling a bookmark, this hook is never called, so do not rely on it as a general ComputerStation input hook. UseOnPlayerInputfor raw input instead.Fires every server frame
Input is submitted at tick rate, so the body runs many times per second per controlling player. Keep lookups cached, avoid building strings unless you actually send them, and never log or broadcast unconditionally.
Blocking input does not dismount
Returning non-null only cancels the forwarded
UserInput. The player stays seated and continues sending input, so if you want to eject them, callcomputerStation.DismountAllPlayers()(orplayer.EnsureDismounted()) yourself rather than relying on the return value.
Build a plugin with OnBookmarkInput
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 OnBookmarkInput
Generate