OnWindmillUpdated
OnWindmillUpdated is called after an ElectricWindmill runs its wind update on the server, right after the generator recalculates its output and sends a network update.
void OnWindmillUpdated(ElectricWindmill electricWindmill)Parameters
| Parameter | Type | Description |
|---|---|---|
electricWindmill | ElectricWindmill | The ElectricWindmill entity that just completed a wind update cycle. It is a BaseEntity subclass, so you can read its position, owner, net ID and flags directly. |
Return value
The return value is ignored. WindUpdate returns void and the call site in the decompiled code does not consume the hook result, so returning a value or null makes no difference to the game's behavior. This hook is observation-only.
How it works
Oxide fires OnWindmillUpdated at the end of ElectricWindmill.WindUpdate, after the windmill has recalculated its current production based on wind conditions, marked itself dirty and sent a network update to clients. This runs on the server whenever the windmill's periodic wind tick executes, so it fires repeatedly for every deployed windmill rather than once on placement.
The single parameter is the ElectricWindmill instance. Use it to read the entity's position, net.ID.Value, OwnerID, or flags, or to react to the fact that a fresh output value is now current. Because WindUpdate is a void method and the call site ignores the return value, you cannot cancel or change the update from this hook.
The hook only takes an ElectricWindmill, so declaring a broader type gives no advantage and a narrower type adds nothing. Keep the parameter as the default type. Since this fires on a timer for every windmill on the map, avoid heavy work, logging and entity scans inside it; gate any response with a cheap condition such as an owner check or a timestamp.
Example plugin: Broadcast warning when a windmill's output drops
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Windmill Output Alert", "RustPluginGenerator", "1.0.0")]
[Description("Warns a windmill owner's team when the windmill's output drops below a threshold")]
public class WindmillOutputAlert : RustPlugin
{
private const string PermUse = "windmilloutputalert.use";
private PluginConfig config;
private StoredData data;
private readonly Dictionary<ulong, float> lastAlert = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Minimum output to stay silent")]
public float Threshold = 2f;
[JsonProperty("Seconds between alerts per windmill")]
public float Cooldown = 120f;
}
private class StoredData
{
public HashSet<string> OptOut = new HashSet<string>();
}
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);
private void Init()
{
permission.RegisterPermission(PermUse, this);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["LowOutput"] = "Your windmill is producing almost no power ({0}). Check placement or wiring.",
["Toggled"] = "Windmill alerts are now {0}.",
["NoPerm"] = "You do not have permission to use this command."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
[ChatCommand("windmill")]
private void CmdWindmill(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPerm", player.UserIDString));
return;
}
bool optedOut = data.OptOut.Contains(player.UserIDString);
if (optedOut) data.OptOut.Remove(player.UserIDString);
else data.OptOut.Add(player.UserIDString);
player.ChatMessage(Msg("Toggled", player.UserIDString, optedOut ? "on" : "off"));
}
// Hook signature for ElectricWindmill output changes.
private void OnWindmillUpdated(ElectricWindmill windmill)
{
if (windmill == null || windmill.IsDestroyed) return;
// Use the generated power as the current output
float output = windmill.currentEnergy;
if (output >= config.Threshold) return;
CheckWindmill(windmill, output);
}
private void CheckWindmill(ElectricWindmill windmill, float output)
{
if (windmill == null || windmill.IsDestroyed) return;
ulong ownerId = windmill.OwnerID;
if (ownerId == 0) return;
ulong key = windmill.net.ID.Value;
float now = Time.realtimeSinceStartup;
float last;
if (lastAlert.TryGetValue(key, out last) && now - last < config.Cooldown) return;
BasePlayer owner = BasePlayer.FindByID(ownerId);
if (owner == null) return;
if (data.OptOut.Contains(owner.UserIDString)) return;
lastAlert[key] = now;
string text = Msg("LowOutput", null, output.ToString("0.0"));
var team = owner.currentTeam != 0
? RelationshipManager.ServerInstance.FindTeam(owner.currentTeam)
: null;
if (team != null)
{
foreach (ulong memberId in team.members)
{
BasePlayer member = BasePlayer.FindByID(memberId);
if (member != null && member.IsConnected) member.ChatMessage(text);
}
}
else
{
owner.ChatMessage(text);
}
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
}
}The plugin tracks each windmill's reported current output and notifies its owner's team through chat when that value falls below a configurable threshold. OnWindmillUpdated reads the entity, looks up the owning player, resolves their team with RelationshipManager.ServerInstance.FindTeam, and sends a short lang-driven message. A per-windmill timestamp prevents the same windmill from alerting more often than the cooldown, though several low-output windmills can each send their own alert. Ownership comes from OwnerID; windmills with no player owner (0) are ignored. The owner can toggle alerts with the windmill command, and that opt-out is persisted to oxide/data. All player-facing text goes through the language file, and the config exposes the threshold and cooldown.
Common use cases
Alert base owners to a stalled generator
Watch windmills on a base and warn the owner's team when output falls near zero, prompting them to check wiring or placement.
Track renewable power statistics
Sample each windmill's current output on this hook and aggregate it into a data file for a server dashboard or per-player power report.
Throttle overuse on a PvE server
Count active windmills per player and trigger an admin warning when a limit is exceeded, using the update tick as a cheap check point.
Pitfalls
It fires often, not once
WindUpdate repeats on the windmill's timer, so any chat output or disk write must be gated by a cooldown or timestamp or you will flood players and spam oxide/data.
Return values do nothing
The call site ignores the hook result, so you cannot block, modify or delay the windmill update. Treat this as a passive notification only.
OwnerID is not always a player
Unowned or admin-spawned windmills report OwnerID 0; casting it with BasePlayer.FindByID will return null, so always null-check before messaging.
Build a plugin with OnWindmillUpdated
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 OnWindmillUpdated
Generate