Skip to content
RustPluginGenerator

OnCCTVDirectionChange

OnCCTVDirectionChange is called when a player at a CCTV_RC computer turns the camera, before the new pitch and yaw are applied. Returning a non-null value cancels the change.

Signature
object OnCCTVDirectionChange(CCTV_RC cctv, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
cctvCCTV_RCThe CCTV_RC computer station whose camera direction is being changed. Only non-static cameras reach this hook, because the game skips static cameras before calling it.
playerBasePlayerThe BasePlayer at the terminal who is aiming the camera. The game has already confirmed this player can build and is authorized on the surrounding cupboard before the hook fires.

Return value

Return null to let the camera direction update normally. Returning any non-null value stops the original code path, so pitchAmount and yawAmount are not changed and the camera stays where it is; the method returns void, so the value itself is discarded.

How it works

The hook fires inside CCTV_RC.Server_SetDir, which runs when the client sends the aim RPC for a camera the player is actively controlling. The game first checks IsStatic() and rejects static cameras, then requires player.CanBuild() and player.IsBuildingAuthed(), and only then calls the hook. Your code runs before the pitch and yaw are recalculated from the player's eye position toward the camera, so any return value can stop that calculation.

The cctv parameter is the computer station entity (prefab cctv_computer), useful for reading its position, OwnerID, or building privilege. The player parameter is the person aiming; use it for permission checks, team lookups, or restricting who may control the camera.

Declaring cctv as CCTV_RC is already the specific type, so Oxide dispatches the hook only for CCTV terminals. Avoid heavy work here: this fires on every direction packet while the player is dragging the mouse, which can be many times per second. Keep the body to quick permission or distance checks. Return null to let the change proceed, anything else to block it.

Example plugin: Restrict camera control to permitted players

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

namespace Oxide.Plugins
{
    [Info("CctvDirectionGuard", "RustPluginGenerator", "1.0.0")]
    [Description("Restricts CCTV camera aiming to players holding a permission and optionally notifies them.")]
    public class CctvDirectionGuard : RustPlugin
    {
        private const string PermUse = "cctvdirectionguard.use";

        private PluginConfig config;
        private readonly Dictionary<ulong, float> lastNotify = new Dictionary<ulong, float>();

        private class PluginConfig
        {
            [JsonProperty("Require permission to aim cameras")]
            public bool RequirePermission = true;

            [JsonProperty("Notify blocked players in chat")]
            public bool NotifyBlockedPlayers = true;

            [JsonProperty("Notification cooldown seconds")]
            public float NotifyCooldown = 2f;
        }

        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>
            {
                ["NoPermission"] = "You are not allowed to aim this camera."
            }, this);
        }

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

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

        private object OnCCTVDirectionChange(CCTV_RC cctv, BasePlayer player)
        {
            if (!config.RequirePermission) return null;
            if (cctv == null || player == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermUse)) return null;

            if (config.NotifyBlockedPlayers && player.IsConnected)
            {
                ulong id = player.userID.Get();
                float now = Time.realtimeSinceStartup;
                float last;
                if (!lastNotify.TryGetValue(id, out last) || now - last >= config.NotifyCooldown)
                {
                    lastNotify[id] = now;
                    player.ChatMessage(Msg("NoPermission", player.UserIDString));
                }
            }

            return false;
        }

        private void Unload()
        {
            lastNotify.Clear();
        }
    }
}

The plugin registers a single permission, cctvdirectionguard.use, and checks it in OnCCTVDirectionChange. Players without the permission who try to aim a non-static CCTV camera are blocked by returning a non-null value, so the camera does not move. An optional config flag NotifyBlockedPlayers sends a localized chat message, throttled to once every two seconds per player so rapid mouse movement does not spam chat. The config is loaded through the standard Oxide config pattern with a fallback to defaults, and messages go through lang so admins can translate them. Because the hook fires only after the game's own build and cupboard-authorization checks, unauthorized players are already handled before this code runs.

Common use cases

  • Camera access per rank

    Gate CCTV aiming behind a permission so only VIP or staff members can operate base cameras, blocking others by returning false.

  • Range-limited cameras

    Compare the player's position to the camera and block the change when the distance exceeds a configured limit, preventing remote surveillance abuse.

  • Team-only camera control

    Read cctv.OwnerID and the player's currentTeam to allow only the owner or their teammates to aim the camera.

Pitfalls

  • Fires very frequently

    The hook runs on every direction packet while a player aims. Avoid Puts, allocations, or entity scans in the body; cache results and throttle chat feedback.

  • Static cameras never reach it

    Server_SetDir exits early when IsStatic() is true, so fixed cameras will not trigger the hook at all. Do not rely on it for monitoring every camera.

  • OwnerID is not a player ID for computers

    The computer station's OwnerID may be zero or a building owner rather than the controller. Verify with player.userID.Get() and team data rather than assuming a match.

Build a plugin with OnCCTVDirectionChange

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

Prompt: Camera access per rank using the OnCCTVDirectionChange 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 OnCCTVDirectionChange

Generate