Skip to content
RustPluginGenerator

OnDigitalClockRing

OnDigitalClockRing is called when a DigitalClock reaches the time of a configured alarm and is about to start ringing. Returning a non-null value suppresses the ringing entirely.

Signature
object OnDigitalClockRing(DigitalClock digitalClock)

Parameters

Hook parameters
ParameterTypeDescription
digitalClockDigitalClockThe DigitalClock entity whose alarm is about to ring. Use it for its position, OwnerID, net.ID and to send effects or messages.

Return value

Return null (or fall through) to let the clock ring normally. Return any non-null object to stop the call — the clock will not enter its ringing state, no ring RPC is sent and no stop timer is scheduled.

How it works

This hook fires inside DigitalClock.Ring, the method the clock invokes at the configured alarm time. When it runs, nothing has happened yet: the clock is not marked as ringing, no ringing RPC has been sent to clients and the five-second stop timer has not been scheduled. Because of that, it is the right point to decide whether the alarm should be heard at all.

The only argument is the clock entity itself. digitalClock.net.ID.Value identifies the device, digitalClock.transform.position gives its location, and OwnerID tells you who placed it. There is no player parameter, so any player-facing logic has to come from your own data, such as a stored owner list or a nearby-player scan.

Declaring the parameter more specifically than DigitalClock will not help here since no other type reaches this hook. If you return any non-null object, Oxide skips the rest of the original method, which means the clock never sets isRinging, never sends the client RPC and never schedules StopRinging. Players simply hear nothing.

The hook fires on the server only, once per alarm event, regardless of how many players are nearby.

Example plugin: Silence alarms during server night and quiet hours

QuietDigitalClocks.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("QuietDigitalClocks", "RustPluginGenerator", "1.0.0")]
    [Description("Suppresses DigitalClock alarms during configured quiet hours or when the owner is offline")]
    public class QuietDigitalClocks : RustPlugin
    {
        private const string PermAdmin = "quietdigitalclocks.admin";

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> lastRing = new Dictionary<ulong, DateTime>();
        private Timer cleanupTimer;

        private class PluginConfig
        {
            [JsonProperty("Quiet hours enabled")]
            public bool QuietEnabled = true;

            [JsonProperty("Quiet start hour (UTC, 0-23)")]
            public int QuietStartHour = 23;

            [JsonProperty("Quiet end hour (UTC, 0-23)")]
            public int QuietEndHour = 8;

            [JsonProperty("Require owner online")]
            public bool RequireOwnerOnline = true;

            [JsonProperty("Minimum seconds between rings per clock")]
            public float MinSecondsBetweenRings = 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>
            {
                ["QuietOn"] = "Clock alarms are now silenced during quiet hours.",
                ["QuietOff"] = "Clock alarms are no longer silenced by quiet hours.",
                ["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);

        private void Init()
        {
            permission.RegisterPermission(PermAdmin, this);
            cleanupTimer = timer.Every(300f, CleanupCache);
        }

        private void Unload()
        {
            cleanupTimer?.Destroy();
            cleanupTimer = null;
        }

        private object OnDigitalClockRing(DigitalClock digitalClock)
        {
            if (digitalClock == null || digitalClock.IsDestroyed) return null;

            ulong id = digitalClock.net.ID.Value;

            if (config.RequireOwnerOnline && digitalClock.OwnerID != 0)
            {
                BasePlayer owner = BasePlayer.FindByID(digitalClock.OwnerID);
                if (owner == null || !owner.IsConnected) return digitalClock;
            }

            if (config.QuietEnabled && IsQuietHour())
                return digitalClock;

            if (config.MinSecondsBetweenRings > 0f)
            {
                DateTime previous;
                if (lastRing.TryGetValue(id, out previous) &&
                    (DateTime.UtcNow - previous).TotalSeconds < config.MinSecondsBetweenRings)
                {
                    return digitalClock;
                }
            }

            lastRing[id] = DateTime.UtcNow;
            return null;
        }

        private bool IsQuietHour()
        {
            int hour = DateTime.UtcNow.Hour;
            int start = config.QuietStartHour;
            int end = config.QuietEndHour;
            if (start == end) return false;
            return start < end ? hour >= start && hour < end : hour >= start || hour < end;
        }

        private void CleanupCache()
        {
            DateTime cutoff = DateTime.UtcNow.AddMinutes(-30);
            var stale = new List<ulong>();
            foreach (var pair in lastRing)
            {
                if (pair.Value < cutoff) stale.Add(pair.Key);
            }
            foreach (ulong id in stale) lastRing.Remove(id);
        }

        [ChatCommand("quietclocks")]
        private void CmdQuietClocks(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                SendReply(player, Msg("NoPerm", player.UserIDString));
                return;
            }
            config.QuietEnabled = !config.QuietEnabled;
            SaveConfig();
            SendReply(player, Msg(config.QuietEnabled ? "QuietOn" : "QuietOff", player.UserIDString));
        }
    }
}

The plugin keeps a config section with a per-clock cooldown, a list of hours during which alarms stay silent, and an option to require the clock's owner to be online. OnDigitalClockRing checks DateTime.UtcNow.Hour against the configured quiet window, and looks up the owner with BasePlayer.FindByID. If the alarm should be suppressed, it records the attempt and returns the clock entity, which is non-null and therefore stops the ring. Otherwise it returns null. A ChatCommand lets admins toggle quiet hours for all clocks, and Unload clears the periodic cleanup timer. Because the hook can fire for many clocks, it never prints anything to the console.

Common use cases

  • Night-time silence

    Suppress every clock alarm during a configured window so base defenses or players are not woken up by noise. Return the clock to block the ring.

  • Owner offline detection

    Only let an alarm ring when the player who placed the clock is online and awake. Returning a value when BasePlayer.FindByID(clock.OwnerID) is null keeps empty bases quiet.

  • Rate limiting per clock

    Track the last ring time per net.ID.Value and block rings that occur sooner than a configured minimum interval, which keeps spammy wiring from flooding the area with sound.

Pitfalls

  • Returning false still blocks

    The check compares against null only, so returning false, 0 or an empty string is a non-null value and will stop the alarm. Always return null when you want normal behavior.

  • No stop event for suppressed rings

    Because the method exits early, OnDigitalClockRingStop will not fire for that alarm. Do not wait for a stop hook to close out bookkeeping for blocked alarms.

  • Hook runs on the server tick path

    Ring can be called for many clocks, so avoid logging with Puts or scanning every player inside the hook. Cache results and keep the check cheap.

Build a plugin with OnDigitalClockRing

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

Prompt: Night-time silence using the OnDigitalClockRing 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 OnDigitalClockRing

Generate