Skip to content
RustPluginGenerator

How to make a Rust day and night control plugin (Oxide/Carbon)

Skips or shortens night by advancing the in-game clock to a configurable morning time, and adds /day and /night admin commands. Uses OnServerInitialized and a repeating timer on TOD_Sky.

Rust nights are long and many PvE or builder-focused servers want them shortened without disabling the day cycle entirely. The in-game clock is owned by TOD_Sky; you can read and write its time of day from Oxide, so a plugin can watch the clock and fast-forward when it passes dusk. This plugin registers a repeating timer that compares TOD_Sky.Instance.Cycle.Hour with a configurable night start time. When night begins it jumps the clock to the configured morning hour, optionally with a configurable speed multiplier so the skip is not instant. Admins can force day or night with chat commands. All values (night start, morning hour, enable/disable, speed) live in the config file and commands are gated by one permission.

The plugin code

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

namespace Oxide.Plugins
{
    [Info("DayNightControl", "RustPluginGenerator", "1.0.0")]
    [Description("Skips or shortens nights by fast-forwarding the in-game clock, with /day and /night admin commands.")]
    public class DayNightControl : RustPlugin
    {
        private const string PermAdmin = "daynightcontrol.admin";

        private PluginConfig config;
        private Timer checkTimer;
        private bool skippedThisNight;

        private class PluginConfig
        {
            [JsonProperty("Enable automatic skip")]
            public bool EnableAutomaticSkip { get; set; } = true;

            [JsonProperty("Skip mode (fast forward instead of instant)")]
            public bool SkipMode { get; set; } = true;

            [JsonProperty("Night start hour (0-24)")]
            public float NightStartHour { get; set; } = 19f;

            [JsonProperty("Morning hour (0-24)")]
            public float MorningHour { get; set; } = 6f;

            [JsonProperty("Hours advanced per timer tick")]
            public float SkipStepHours { get; set; } = 0.5f;

            [JsonProperty("Check interval (seconds)")]
            public float CheckIntervalSeconds { 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>
            {
                ["SetDay"] = "In-game clock set to {0:0.##}:00 (morning).",
                ["SetNight"] = "In-game clock set to {0:0.##}:00 (night start).",
                ["NoPermission"] = "You do not have permission to use this command.",
                ["SkipAnnounce"] = "Night is ending early. Good morning!",
                ["SkyMissing"] = "The sky is not available right now. Is the map fully loaded?"
            }, 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);
        }

        private void OnServerInitialized(bool initial)
        {
            CheckSky();

            if (checkTimer != null) checkTimer.Destroy();
            checkTimer = timer.Every(Mathf.Max(1f, config.CheckIntervalSeconds), Tick);
        }

        private void Unload()
        {
            if (checkTimer != null)
            {
                checkTimer.Destroy();
                checkTimer = null;
            }
        }

        private TOD_Sky CheckSky()
        {
            var sky = TOD_Sky.Instance;
            if (sky == null || sky.Cycle == null)
            {
                Puts("TOD_Sky is not ready yet; skipping this check.");
                return null;
            }
            return sky;
        }

        private void Tick()
        {
            if (!config.EnableAutomaticSkip) return;

            var sky = CheckSky();
            if (sky == null) return;

            float hour = sky.Cycle.Hour;

            if (hour >= config.MorningHour && hour < config.NightStartHour)
            {
                skippedThisNight = false;
                return;
            }

            if (skippedThisNight) return;
            if (hour < config.NightStartHour && hour >= config.MorningHour) return;

            if (!config.SkipMode)
            {
                SetHour(sky, config.MorningHour);
                skippedThisNight = true;
                Server.Broadcast(Msg("SkipAnnounce"));
                return;
            }

            float next = hour + Mathf.Max(0.05f, config.SkipStepHours);
            if (next >= 24f) next -= 24f;

            if (HoursBetween(next, config.MorningHour) < 0.05f || next >= config.MorningHour)
            {
                SetHour(sky, config.MorningHour);
                skippedThisNight = true;
                Server.Broadcast(Msg("SkipAnnounce"));
                return;
            }

            SetHour(sky, next);
        }

        private static float HoursBetween(float from, float to)
        {
            float diff = to - from;
            if (diff < 0f) diff += 24f;
            return diff;
        }

        private void SetHour(TOD_Sky sky, float hour)
        {
            if (sky == null || sky.Cycle == null) return;
            sky.Cycle.Hour = hour;
        }

        [ChatCommand("day")]
        private void CmdDay(BasePlayer player, string command, string[] args)
        {
            if (player != null && !permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            var sky = CheckSky();
            if (sky == null)
            {
                if (player != null) player.ChatMessage(Msg("SkyMissing", player.UserIDString));
                return;
            }

            SetHour(sky, config.MorningHour);
            skippedThisNight = true;
            if (player != null) player.ChatMessage(Msg("SetDay", player.UserIDString, config.MorningHour));
        }

        [ChatCommand("night")]
        private void CmdNight(BasePlayer player, string command, string[] args)
        {
            if (player != null && !permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            var sky = CheckSky();
            if (sky == null)
            {
                if (player != null) player.ChatMessage(Msg("SkyMissing", player.UserIDString));
                return;
            }

            SetHour(sky, config.NightStartHour);
            skippedThisNight = false;
            if (player != null) player.ChatMessage(Msg("SetNight", player.UserIDString, config.NightStartHour));
        }
    }
}

Hooks used: OnServerInitialized

Config file

Created automatically on first load at oxide/config/DayNightControl.json.

DayNightControl.json
{
  "Enable automatic skip": true,
  "Skip mode (fast forward instead of instant)": true,
  "Night start hour (0-24)": 19,
  "Morning hour (0-24)": 6,
  "Hours advanced per timer tick": 0.5,
  "Check interval (seconds)": 5
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/daySets the in-game clock to the configured morning hour.daynightcontrol.admin
/nightSets the in-game clock to the configured night start hour.daynightcontrol.admin
Permissions
PermissionGrants
daynightcontrol.adminGrants access to /day and /night.

How the code works

  1. Read and write the clock through TOD_Sky

    The TOD_Sky.Instance singleton is the source of truth for the game clock. Its Cycle.Hour property is a float from 0 to 24. Setting it moves the sun and moon to that position immediately. The plugin caches the instance in OnServerInitialized and null-checks it on every timer tick, because the sky can be recreated on map change.

  2. Detect dusk with a repeating timer

    A timer.Every timer runs at the interval set by CheckIntervalSeconds (default 5). On each tick it compares the current hour with NightStartHour and MorningHour from the config. Only when the clock crosses into night and the plugin has not already skipped for this night does it act, so the fast-forward runs once per cycle rather than on every tick.

  3. Fast-forward instead of snapping

    When SkipMode is true, the plugin advances the clock by SkipStepHours every timer pass until it reaches MorningHour, giving a short sunrise animation instead of an instant jump. The step per tick is clamped so the clock never overshoots morning. Setting the mode to false in config disables the automatic skip and leaves only the commands.

  4. Track whether the night has already been skipped

    A boolean flag skippedThisNight is cleared when the hour is between MorningHour and NightStartHour, and set once the skip completes. This avoids repeated triggers when the timer fires again on the same night. The flag is local state only; no data file is written because the value is regenerated after every restart.

  5. Admin commands for /day and /night

    Both commands check permission.UserHasPermission(player.UserIDString, PermAdmin) before doing anything. /day sets Cycle.Hour to MorningHour and broadcasts a chat message from lang. /night sets it to NightStartHour. The commands also reset skippedThisNight so an admin can force a skip immediately after using /night.

  6. Config, messages and unloading

    LoadDefaultConfig builds a PluginConfig with [JsonProperty] fields for EnableAutomaticSkip, NightStartHour, MorningHour, SkipStepHours and CheckIntervalSeconds. LoadDefaultMessages registers the chat strings used by the commands and the automatic skip notification. Unload destroys the timer, and a null guard stops the timer if TOD_Sky.Instance disappears mid-cycle.

Install it on your server

  1. Save DayNightControl.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. Upload it to the plugins folder

    Oxide: oxide/plugins/. Carbon: carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/DayNightControl.json (Carbon: carbon/configs/DayNightControl.json). After editing, reload it:oxide.reload DayNightControl c.reload DayNightControl # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip:oxide.grant group default daynightcontrol.admin c.grant group default daynightcontrol.admin # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Different times per weekday

    Vary the morning hour so weekends get a longer day and weekdays are tighter.

    Generate this version
  • Speed up time during the night

    Instead of jumping, multiply TOD_Sky's day length while it is night.

    Generate this version
  • Announce the skip in chat

    Tell players when the automatic night skip fires.

    Generate this version

FAQ

Does this also affect Bradley or heli spawn times?

No. Events like BradleyAPC and PatrolHelicopter use their own timers. The plugin only touches TOD_Sky.Instance.Cycle.Hour, so event scheduling is untouched.

Will the skip break solar panels or wind turbines?

Solar panels read the sun position, so if the clock jumps to morning they immediately gain full output. Wind turbines use wind, not the clock, and are unaffected. If you want a gradual transition, keep SkipMode enabled and reduce SkipStepHours.

How do I disable the automatic skip but keep /day and /night?

Set "Enable automatic skip": false in oxide/config/DayNightControl.json, then reload the plugin. The timer keeps running to detect the phase change for the skip flag, but no automatic jump happens. The commands still work for admins.

How do I install this on Oxide or Carbon?

Drop the compiled DayNightControl.cs into oxide/plugins/, then run oxide.reload DayNightControl. Carbon loads the same file from carbon/plugins/. Grant daynightcontrol.admin via oxide.grant user <id> daynightcontrol.admin.

Why does the clock still move while I am AFK?

The plugin does not pause the server clock; it only adjusts it when the timer determines it is night. Between skips the clock runs at vanilla speed. If you want a slower overall cycle, change TOD_Sky.Instance.DayLengthInMinutes separately.

Customize it for your server

Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.

Prompt: Write a Rust Oxide plugin named DayNightControl that skips night by advancing TOD_Sky cycle hour to a configurable morning time, plus /day and /night admin commands and a language file.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize day and night control

Customize