OnDigitalClockRingStop
OnDigitalClockRingStop is called when a DigitalClock's alarm finishes and the clock is about to stop ringing. Returning a non-null value blocks the default stop behavior.
object OnDigitalClockRingStop(DigitalClock digitalClock)Parameters
| Parameter | Type | Description |
|---|---|---|
digitalClock | DigitalClock | The DigitalClock entity that is about to stop ringing. Use it to read its position, owner, net.ID or IO connections and to send network updates or effects. |
Return value
Return null (or nothing) to let the game clear isRinging, send the stop RPC and mark the clock dirty. Returning any non-null value skips that entire block, so the clock stays in its ringing state and clients are not told to stop.
How it works
Oxide fires this hook inside DigitalClock.StopRinging, right before the clock clears its ringing state: it sets isRinging to false, sends the RPC_StopRinging client RPC and marks the entity dirty. This happens when the configured alarm window elapses, not when a player interacts with the clock.
The single digitalClock parameter is the DigitalClock entity itself. From it you can read net.ID.Value, OwnerID, transform.position, IsDestroyed, the attached power/IO state, and any custom data you stored earlier in OnDigitalClockRing.
Oxide matches your hook method by checking that the argument is an instance of the declared parameter type. Declaring DigitalClock (as in the signature) is the only useful option here; declaring a broader type such as BaseEntity would also match but gives no extra calls.
Because the hook only fires on the edge where ringing ends, it is cheap to do work here: send effects, notify the owner, log the event, or cancel the stop entirely by returning a non-null value so the clock keeps ringing. The clock is still ringing at the point the hook runs, so IsDestroyed checks matter if a clock is killed mid-alarm.
Example plugin: Notify the owner when a digital clock alarm ends
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Digital Clock Alarm Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Notifies the owner in chat when a deployed digital clock stops ringing")]
public class DigitalClockAlarmNotifier : RustPlugin
{
private const string PermNotify = "digitalclockalarmnotifier.notify";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastNotify = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Cooldown between notifications (seconds)")]
public float Cooldown = 15f;
[JsonProperty("Play sound at the clock")]
public bool PlaySound = true;
}
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>
{
["AlarmStopped"] = "Your digital clock at {0}m from you stopped ringing."
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
{
return string.Format(lang.GetMessage(key, this, userId), args);
}
private void Init()
{
permission.RegisterPermission(PermNotify, this);
}
private object OnDigitalClockRingStop(DigitalClock digitalClock)
{
if (digitalClock == null || digitalClock.IsDestroyed) return null;
if (digitalClock.OwnerID == 0) return null;
var player = BasePlayer.FindByID(digitalClock.OwnerID);
if (player == null || !player.IsConnected) return null;
if (!permission.UserHasPermission(player.UserIDString, PermNotify)) return null;
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (lastNotify.TryGetValue(player.userID.Get(), out last) && now - last < config.Cooldown)
return null;
lastNotify[player.userID.Get()] = now;
int distance = Mathf.RoundToInt(Vector3.Distance(player.transform.position, digitalClock.transform.position));
player.ChatMessage(Msg("AlarmStopped", player.UserIDString, distance));
if (config.PlaySound)
Effect.server.Run("assets/bundled/prefabs/fx/notice/loot.drag.grab.fx.prefab", digitalClock.transform.position);
return null;
}
private void Unload()
{
lastNotify.Clear();
}
}
}The plugin stores a configurable message and cooldown, then hooks OnDigitalClockRingStop. Each time a clock stops ringing it resolves the owner through OwnerID and BasePlayer.FindByID, checks the cooldown dictionary keyed by player id, and sends the chat message through the language file with the clock's distance from the player. A small sound effect is played at the clock so nearby players hear the alarm end. Cooldowns are pruned by expiry on each call so the dictionary does not grow without bound, and the plugin always returns null so the game still performs its normal stop logic.
Common use cases
Alarm end notification
Chat or console-message the clock's owner when an alarm finishes so they know the timer elapsed without standing next to the clock.
Audit and logging
Record each ringing cycle with the clock's position and owner to data files for troubleshooting base circuits and timer chains.
Keep alarms ringing as a punishment
Return a non-null value to skip the stop block and leave the clock ringing, for example when a raid or event rule forbids silencing alarms.
Pitfalls
Returning non-null freezes the clock
Any non-null return prevents isRinging from being cleared and the RPC from being sent, so the clock keeps ringing indefinitely. Only do this deliberately.
The clock may be destroyed mid-alarm
If the entity was killed while ringing, the hook can still fire. Check
digitalClock == null || digitalClock.IsDestroyedbefore sending effects or network updates.OwnerID is zero for unowned clocks
Deployed outdoors without a tool cupboard owner,
OwnerIDcan be 0. GuardBasePlayer.FindByIDwith a zero check to avoid wasted lookups.
Build a plugin with OnDigitalClockRingStop
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 OnDigitalClockRingStop
Generate