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
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.
{
"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
| Command | What it does | Permission |
|---|---|---|
/day | Sets the in-game clock to the configured morning hour. | daynightcontrol.admin |
/night | Sets the in-game clock to the configured night start hour. | daynightcontrol.admin |
| Permission | Grants |
|---|---|
daynightcontrol.admin | Grants access to /day and /night. |
How the code works
Read and write the clock through TOD_Sky
The
TOD_Sky.Instancesingleton is the source of truth for the game clock. ItsCycle.Hourproperty is a float from 0 to 24. Setting it moves the sun and moon to that position immediately. The plugin caches the instance inOnServerInitializedand null-checks it on every timer tick, because the sky can be recreated on map change.Detect dusk with a repeating timer
A
timer.Everytimer runs at the interval set byCheckIntervalSeconds(default 5). On each tick it compares the current hour withNightStartHourandMorningHourfrom 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.Fast-forward instead of snapping
When
SkipModeistrue, the plugin advances the clock bySkipStepHoursevery timer pass until it reachesMorningHour, 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.Track whether the night has already been skipped
A boolean flag
skippedThisNightis cleared when the hour is betweenMorningHourandNightStartHour, 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.Admin commands for /day and /night
Both commands check
permission.UserHasPermission(player.UserIDString, PermAdmin)before doing anything./daysetsCycle.HourtoMorningHourand broadcasts a chat message fromlang./nightsets it toNightStartHour. The commands also resetskippedThisNightso an admin can force a skip immediately after using/night.Config, messages and unloading
LoadDefaultConfigbuilds aPluginConfigwith[JsonProperty]fields forEnableAutomaticSkip,NightStartHour,MorningHour,SkipStepHoursandCheckIntervalSeconds.LoadDefaultMessagesregisters the chat strings used by the commands and the automatic skip notification.Unloaddestroys the timer, and a null guard stops the timer ifTOD_Sky.Instancedisappears mid-cycle.
Install it on your server
Save DayNightControl.cs
Keep the file name exactly as the class name. Oxide matches them.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.Edit the config if you want
On first load the default config is written tooxide/config/DayNightControl.json(Carbon:carbon/configs/DayNightControl.json). After editing, reload it:oxide.reload DayNightControl c.reload DayNightControl # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip: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 versionSpeed up time during the night
Instead of jumping, multiply TOD_Sky's day length while it is night.
Generate this versionAnnounce 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.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More quality of life plugins
- BackpacksAdds a /backpack command that opens a per-player StorageContainer sized by permission, with contents persisted to oxide/data and optionally droppedโฆOnPlayerLootEndOnPlayerDisconnectedOnPlayerDeath
- Crafting speedA plugin that speeds up crafting by scaling the duration of ItemCraftTask instances when a craft starts, using a global multiplier plus optionalโฆOnItemCraft
- Default item skinsLets each player store a default skin ID per item shortname with /skin and applies it to every item the player crafts through theโฆOnServerInitializedOnItemCraftFinished
Last updated Oct 26, 2026.
Customize day and night control
Customize