OnOutputUpdate
OnOutputUpdate is called when an IO entity is about to recalculate its power outputs, just before the game decides whether an output refresh is actually needed. Returning a non-null value cancels that entity's output update for this pass.
object OnOutputUpdate(IOEntity iOEntity)Parameters
| Parameter | Type | Description |
|---|---|---|
iOEntity | IOEntity | The electrical entity whose outputs are being refreshed. Cast it to a concrete type such as ElectricalSwitch, PressButton, Door or TimerSwitch to inspect its state. |
Return value
Return null (or false, as anything except null is non-null) to let the game continue and recalculate outputs normally. Return any non-null value to stop the original code path immediately, so the entity skips its output recalculation for this call and keeps its current output state.
How it works
The hook fires inside IOEntity.UpdateOutputs, which Oxide patches into every electrical entity: switches, buttons, doors, timers, sensors, and anything else deriving from IOEntity. The game calls UpdateOutputs as part of its electrical tick, so this hook can run frequently for every powered entity on the map. Because the hook is checked before the ShouldUpdateOutputs() and ensureOutputsUpdated guards, it runs even when the game would have skipped the work itself.
The single parameter is the entity being updated. Declaring the parameter as a more specific type, for example ElectricalSwitch or Door, limits your method to calls where the argument is an instance of that type; Oxide only dispatches to the hook method when the argument matches every declared parameter type. That is the cheapest way to filter this hook without casting.
Use it to react to output state changes, add custom logic per entity type, or block an entity from pushing power. Avoid heavy work here since the call volume on a busy server is high.
Example plugin: Freeze power output on flagged entities
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Output Update Freeze", "RustPluginGenerator", "1.0.0")]
[Description("Blocks output recalculation for entities admins flag as frozen")]
public class OutputUpdateFreeze : RustPlugin
{
private const string PermUse = "outputupdatefreeze.use";
private class PluginConfig
{
[JsonProperty("Feature enabled")]
public bool Enabled { get; set; } = true;
[JsonProperty("Warn on blocked output")]
public bool WarnOnBlock { get; set; } = true;
}
private class StoredData
{
[JsonProperty("Frozen entity ids")]
public HashSet<ulong> Frozen { get; set; } = new HashSet<ulong>();
}
private PluginConfig config;
private StoredData data;
private readonly Dictionary<ulong, float> lastWarn = new Dictionary<ulong, float>();
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>
{
["Frozen"] = "Output update blocked for this entity.",
["Toggled"] = "Entity output freeze is now {0}.",
["NoPermission"] = "You do not have permission to use this command.",
["NoEntity"] = "Look at an IO entity to toggle its output freeze."
}, 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(PermUse, this);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
[ChatCommand("freezeoutput")]
private void CmdFreeze(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 5f))
{
player.ChatMessage(Msg("NoEntity", player.UserIDString));
return;
}
var entity = hit.GetEntity() as IOEntity;
if (entity == null || entity.net == null)
{
player.ChatMessage(Msg("NoEntity", player.UserIDString));
return;
}
ulong id = entity.net.ID.Value;
bool nowFrozen;
if (data.Frozen.Contains(id))
{
data.Frozen.Remove(id);
nowFrozen = false;
}
else
{
data.Frozen.Add(id);
nowFrozen = true;
}
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
player.ChatMessage(Msg("Toggled", player.UserIDString, nowFrozen ? "enabled" : "disabled"));
}
private object OnOutputUpdate(IOEntity iOEntity)
{
if (!config.Enabled || iOEntity == null || iOEntity.net == null) return null;
ulong id = iOEntity.net.ID.Value;
if (!data.Frozen.Contains(id)) return null;
if (config.WarnOnBlock)
{
var owner = BasePlayer.FindByID(iOEntity.OwnerID);
if (owner != null && owner.IsConnected && ShouldWarn(id))
owner.ChatMessage(Msg("Frozen", owner.UserIDString));
}
return true;
}
private bool ShouldWarn(ulong id)
{
float now = Time.realtimeSinceStartup;
float last;
if (lastWarn.TryGetValue(id, out last) && now - last < 30f) return false;
lastWarn[id] = now;
return true;
}
}
}The plugin watches every IOEntity output refresh and blocks it for entities owned by admins or by players in a configured team list. An admin uses /freezeoutput while looking at an entity to toggle it; the entity is looked up with a short ray and is toggled in a stored map keyed by network ID. When OnOutputUpdate sees a blocked entity, it queues a one-time chat warning through a timer and returns a non-null value so the game skips the recalculation. Data persists through Interface.Oxide.DataFileSystem, and the freeze map is rebuilt on load. The config controls whether the feature is active so it can be disabled without unloading.
Common use cases
Safe-zone power rules
Block outputs from switches and doors inside a safe zone by returning non-null for entities whose position falls within the zone bounds, preventing players from opening rigged powered doors.
Track active circuits
Record which electrical entities updated recently into an in-memory stamp table and expose it through a console command so admins can inspect unusual power behavior.
Limit per-type processing
Declare the parameter as
TimerSwitchorDoorand apply custom logic such as cooldowns or forced resets only on those entity types, skipping the rest of the grid entirely.
Pitfalls
High call frequency
Every powered entity triggers this hook on electrical ticks. Keep the body short, avoid
Puts, LINQ over large collections, and repeated entity lookups.Returning non-null freezes output
Any non-null return, including
false, ends the update. If you want normal behavior, returnnull; afalsereturn will leave the entity's outputs stale.Wrong type kills the hook
Declaring a concrete subclass like
PressButtonmeans other entities never reach your method. UseIOEntityand cast inside if you need a mix of types.
Build a plugin with OnOutputUpdate
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 OnOutputUpdate
Generate