Skip to content
RustPluginGenerator

OnCounterModeToggle

OnCounterModeToggle is called when a player toggles a PowerCounter's display mode, before the server checks build permission and applies the new flag.

Signature
object OnCounterModeToggle(PowerCounter powerCounter, BasePlayer player, bool flag)

Parameters

Hook parameters
ParameterTypeDescription
powerCounterPowerCounterThe PowerCounter entity whose display mode the player is trying to toggle. Use it to read the owner, position, current flags or attached IO connections.
playerBasePlayerThe BasePlayer that sent the toggle RPC. Oxide can deliver a null here if the RPC has no attached connection, so null-check before using it.
flagboolThe requested display mode bit read from the RPC payload. For a counter this switches between the counter display and the passthrough/IO display; check the entity's own flags if you need the current state.

Return value

Returning null lets the original code continue: it then checks player.CanBuild() and, if allowed, writes the new mode flag, marks the counter dirty and sends a network update. Returning any non-null value skips that whole block, so the mode change is silently rejected without a network update โ€” use that to deny the toggle.

How it works

The hook fires inside PowerCounter.ToggleDisplayMode, which is invoked by an RPC from the client when a player presses the counter's mode button. The game passes the counter entity, the sending player and the requested mode bit flag.

Because the call happens before the CanBuild() check, the hook sees attempts from players who would normally be rejected โ€” useful if you want to allow or deny mode changes based on your own rules. powerCounter is the entity to inspect (owner, position, building privilege, connected IO); player is the acting player; flag is the mode value the client requested.

Declaring player as a more specific subclass will not help here โ€” it is always a BasePlayer. You cannot narrow this hook by type in a meaningful way, so filter inside the method (for example by entity owner or zone).

The hook fires only for actual mode-toggle RPCs, not for counter output updates, which use OnCounterTargetChange and OnOutputUpdate. It is not called for other electrical components.

Example plugin: Restrict counter mode toggles to the owner and their team

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

namespace Oxide.Plugins
{
    [Info("CounterModeOwnerOnly", "RustPluginGenerator", "1.0.0")]
    [Description("Restricts PowerCounter display mode toggles to the entity owner, their team and staff.")]
    public class CounterModeOwnerOnly : RustPlugin
    {
        private const string PermBypass = "countermodeowneronly.bypass";

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

        private class PluginConfig
        {
            [JsonProperty("Allow teammates of the owner")]
            public bool AllowTeammates { get; set; } = true;

            [JsonProperty("Require building privilege instead of ownership")]
            public bool RequireBuildPrivilege { get; set; } = false;

            [JsonProperty("Notify the player when denied")]
            public bool NotifyDenied { get; set; } = true;

            [JsonProperty("Notice cooldown in seconds")]
            public float NoticeCooldown { 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>
            {
                ["Denied"] = "You are not allowed to change this counter's display mode."
            }, 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(PermBypass, this);
        }

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

        private object OnCounterModeToggle(PowerCounter powerCounter, BasePlayer player, bool flag)
        {
            if (powerCounter == null || player == null || player.IsAdmin) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
            if (player.userID == powerCounter.OwnerID) return null;

            if (config.RequireBuildPrivilege)
            {
                var privilege = powerCounter.GetBuildingPrivilege();
                if (privilege != null && privilege.IsAuthed(player)) return null;
            }
            else if (config.AllowTeammates && IsTeammateOfOwner(player, powerCounter.OwnerID))
            {
                return null;
            }

            Notify(player);
            return false;
        }

        private bool IsTeammateOfOwner(BasePlayer player, ulong ownerId)
        {
            if (ownerId == 0 || player.currentTeam == 0) return false;
            var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
            return team != null && team.members.Contains(ownerId);
        }

        private void Notify(BasePlayer player)
        {
            if (!config.NotifyDenied) return;

            float now = UnityEngine.Time.realtimeSinceStartup;
            float last;
            if (lastNotice.TryGetValue(player.userID, out last) && now - last < config.NoticeCooldown) return;

            lastNotice[player.userID] = now;
            player.ChatMessage(Msg("Denied", player.UserIDString));
        }
    }
}

The plugin denies mode toggles on a PowerCounter unless the player is the entity's owner, the owner's teammate, an admin, or has the countermodeowneronly.bypass permission. On a denied attempt the hook returns false, which makes the game skip the permission check and the network update, so the counter visually stays where it was. Allowed attempts return null so vanilla behavior (including the CanBuild() check) runs untouched. A short chat message tells the player why nothing happened, throttled with a per-player cooldown so repeated clicks do not spam chat.

Common use cases

  • Entity ownership enforcement

    Block strangers from flipping a counter's mode on a shared build so its display stays as the owner configured it.

  • Raiding and zone rules

    Allow mode changes only while the player has building privilege on the counter, or only outside a PvP event zone.

  • Staff tools

    Log or count toggle attempts per player to spot griefers clicking other players' electricals, and grant staff a bypass permission.

Pitfalls

  • Return null to allow

    Any non-null return cancels the change, including false or 0. Only return a value when you actually want to deny; otherwise return null.

  • Player can be null

    The player comes from the RPC message and is not guaranteed to be present. Guard against a null player before calling methods on it.

  • No build check before the hook

    The hook runs before the game's CanBuild() test, so you may see attempts from players who would be rejected anyway. Do not assume every call is a legitimate action.

Build a plugin with OnCounterModeToggle

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

Prompt: Entity ownership enforcement using the OnCounterModeToggle 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 OnCounterModeToggle

Generate