Skip to content
RustPluginGenerator

How to make a Rust wipe day kit plugin (Oxide)

Adds a one-time starter kit that can only be claimed during the first N hours after a map wipe, using OnNewSave to start the window and /wipekit to claim or show it is closed.

Wipe day crowds are good for a server, but hand-adding a starter kit to every fresh wipe is tedious and error-prone. The usual problems: the kit gets claimed weeks later by a player who just found the server, or you forget to enable it and the wipe starts without one. This plugin solves both by anchoring the claim window to the actual wipe.

When Rust calls OnNewSave, the plugin records the current Unix timestamp in oxide/data/WipeDayKit.json. From then until the configured window closes, any player can run /wipekit once to receive the configured items. After the window ends, /wipekit reports that claiming is closed. The window length, the item list, and the claimed player IDs all live in the config and data files, so you can change loadouts between wipes without touching code.

The plugin code

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

namespace Oxide.Plugins
{
    [Info("Wipe Day Kit", "RustPluginGenerator", "1.0.0")]
    [Description("Gives players a one-time kit that can only be claimed during the first N hours after a wipe.")]
    public class WipeDayKit : RustPlugin
    {
        private const string PermUse = "wipedaykit.use";

        private PluginConfig config;
        private StoredData data;

        private class PluginConfig
        {
            [JsonProperty("Window hours after wipe")]
            public int WindowHours = 48;

            [JsonProperty("Items")]
            public List<KitItem> Items = new List<KitItem>
            {
                new KitItem { Shortname = "scrap", Amount = 500 },
                new KitItem { Shortname = "stone.pickaxe", Amount = 1 },
                new KitItem { Shortname = "stone.hatchet", Amount = 1 },
                new KitItem { Shortname = "bandage", Amount = 3 }
            };
        }

        private class KitItem
        {
            [JsonProperty("Shortname")]
            public string Shortname = "scrap";

            [JsonProperty("Amount")]
            public int Amount = 1;

            [JsonProperty("Skin")]
            public ulong Skin = 0;
        }

        private class StoredData
        {
            [JsonProperty("Start unix")]
            public long StartUnix = 0;

            [JsonProperty("Claimed user ids")]
            public List<ulong> Claimed = new List<ulong>();
        }

        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>
            {
                ["NoPermission"] = "You do not have permission to claim the wipe day kit.",
                ["AlreadyClaimed"] = "You already claimed your wipe day kit.",
                ["WindowClosed"] = "The wipe day kit window has closed.",
                ["WindowOpen"] = "Wipe day kit available. Time left: {0}h {1}m.",
                ["Claimed"] = "Wipe day kit claimed. Have fun!",
                ["ClaimFailed"] = "Your kit could not be granted right now. Try again later.",
                ["NoItems"] = "No items were given. Check your inventory space or contact an admin."
            }, 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(PermUse, this);
            LoadData();
        }

        private void OnServerInitialized()
        {
            if (data == null) LoadData();
            if (data.StartUnix <= 0)
            {
                data.StartUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                Interface.Oxide.DataFileSystem.WriteObject(Name, data);
            }
        }

        private void Unload()
        {
            SaveData();
        }

        private void LoadData()
        {
            try
            {
                data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
            }
            catch
            {
                data = null;
            }
            if (data == null) data = new StoredData();
            if (data.Claimed == null) data.Claimed = new List<ulong>();
        }

        private void SaveData()
        {
            if (data != null) Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }

        private void OnNewSave(string strFilename)
        {
            LoadData();
            data.StartUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            data.Claimed.Clear();
            SaveData();
        }

        [ChatCommand("wipekit")]
        private void CmdWipeKit(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                SendReply(player, Msg("NoPermission", player.UserIDString));
                return;
            }

            if (data == null) LoadData();

            if (data.Claimed.Contains(player.userID.Get()))
            {
                SendReply(player, Msg("AlreadyClaimed", player.UserIDString));
                return;
            }

            double elapsed = (DateTimeOffset.UtcNow.ToUnixTimeSeconds() - data.StartUnix) / 3600.0;
            if (data.StartUnix <= 0 || elapsed < 0 || elapsed > config.WindowHours)
            {
                SendReply(player, Msg("WindowClosed", player.UserIDString));
                return;
            }

            double remaining = config.WindowHours - elapsed;
            int hours = (int)remaining;
            int minutes = Mathf.Max(0, (int)((remaining - hours) * 60));
            SendReply(player, Msg("WindowOpen", player.UserIDString, hours, minutes));

            if (GiveKit(player))
            {
                data.Claimed.Add(player.userID.Get());
                SaveData();
                SendReply(player, Msg("Claimed", player.UserIDString));
            }
            else
            {
                SendReply(player, Msg("ClaimFailed", player.UserIDString));
            }
        }

        private bool GiveKit(BasePlayer player)
        {
            int given = 0;
            foreach (var entry in config.Items)
            {
                if (entry == null || string.IsNullOrEmpty(entry.Shortname)) continue;
                int amount = Mathf.Max(1, entry.Amount);
                Item item = ItemManager.CreateByName(entry.Shortname, amount, entry.Skin);
                if (item == null)
                {
                    PrintWarning($"Unknown shortname in config: {entry.Shortname}");
                    continue;
                }
                player.GiveItem(item);
                given++;
            }

            if (given == 0)
            {
                SendReply(player, Msg("NoItems", player.UserIDString));
                return false;
            }
            return true;
        }
    }
}

Hooks used: OnServerInitialized, OnNewSave

Config file

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

WipeDayKit.json
{
  "Window hours after wipe": 48,
  "Items": [
    {
      "Shortname": "scrap",
      "Amount": 500,
      "Skin": 0
    },
    {
      "Shortname": "stone.pickaxe",
      "Amount": 1,
      "Skin": 0
    },
    {
      "Shortname": "stone.hatchet",
      "Amount": 1,
      "Skin": 0
    },
    {
      "Shortname": "bandage",
      "Amount": 3,
      "Skin": 0
    }
  ],
  "Start unix": 0,
  "Claimed user ids": []
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/wipekitClaims the wipe day kit, or tells the player the window is closed or already claimed.wipedaykit.use
Permissions
PermissionGrants
wipedaykit.useGrants access to the /wipekit command and the claim.

How the code works

  1. Detecting the wipe with OnNewSave

    OnNewSave(string strFilename) fires when the server saves a brand new map. The hook loads the data file, sets data.StartUnix to DateTimeOffset.UtcNow.ToUnixTimeSeconds(), and clears data.Claimed. It then writes oxide/data/WipeDayKit.json. OnServerInitialized covers the fresh-install case: if data.StartUnix is still zero, it stores the current time so the window starts now rather than never.

  2. The claim window

    The Window hours after wipe key in the config controls how long players can claim. /wipekit subtracts data.StartUnix from the current Unix time and divides by 3600 to get elapsed hours. If data.StartUnix is zero, or if the result is negative or larger than WindowHours, the player gets the WindowClosed message and no items are granted.

  3. One kit per player

    data.Claimed is a List<ulong> of player IDs. Before granting items, the command checks data.Claimed.Contains(player.userID.Get()) and stops if true. A successful grant adds the ID and calls SaveData, which writes the file. To let everybody claim again without wiping, delete oxide/data/WipeDayKit.json and reload the plugin.

  4. Granting the items

    The Items list in the config holds objects with Shortname, Amount, and Skin. GiveKit walks the list, calls ItemManager.CreateByName(shortname, amount, skin) for each entry, and passes the result to player.GiveItem(item). Entries whose shortname is unknown print a warning and are skipped. If no items get created at all, the player is told NoItems and stays unclaimed.

  5. The /wipekit command

    Init registers the wipedaykit.use permission, and [ChatCommand("wipekit")] marks CmdWipeKit as the handler. Before doing any work, the command checks permission.UserHasPermission and sends NoPermission if it fails. Grant the permission on the default group with oxide.grant group default wipedaykit.use, or manage it per player with a permission plugin.

Install it on your server

  1. Save WipeDayKit.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/WipeDayKit.json (Carbon: carbon/configs/WipeDayKit.json). After editing, reload it:oxide.reload WipeDayKit c.reload WipeDayKit # Carbon
  4. Grant the permission

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

Variations

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

FAQ

Does the window restart on every server restart?

No. Only OnNewSave resets data.StartUnix, and that hook fires on a new map save. A plain restart just reloads the stored timestamp from oxide/data/WipeDayKit.json, so the remaining hours keep counting down across restarts during wipe day.

How do I install this on Oxide or Carbon?

Drop WipeDayKit.cs into oxide/plugins/ on Oxide, or the same folder on Carbon (Carbon loads Oxide plugins). Reload the plugin; it writes oxide/config/WipeDayKit.json and oxide/data/WipeDayKit.json. Edit the config and run oxide.reload WipeDayKit.

What happens if there is no room in the player's inventory?

player.GiveItem drops overflow next to the player, and the plugin counts the entry as given. The claim is still recorded, so the player cannot double dip. If every shortname in Items is wrong, GiveKit returns false and the player remains unclaimed with a NoItems message.

How do I test without wiping the map?

Stop the server or reload the plugin, delete oxide/data/WipeDayKit.json, and reload. OnServerInitialized then sets data.StartUnix to the current time with an empty claimed list. Run /wipekit twice to confirm the first claim succeeds and the second is rejected.

Customize it for your server

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

Prompt: Create WipeDayKit, an Oxide plugin that grants a 48-hour wipe day kit of 500 scrap plus basic tools, started by OnNewSave, claimed once per player via /wipekit guarded by wipedaykit.use, with state saved in oxide/data.
Customize for my server

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

Last updated Oct 26, 2026.

Customize wipe day kit

Customize