How to make a Rust wipe raid cooldown plugin (block building damage after wipe)
Stores the save timestamp on each new map save and blocks all damage dealt to building blocks, deployables and their building privilege range until N hours have passed. Uses OnNewSave to record the wipe and OnEntityTakeDamage to block the damage.
After a forced wipe everybody rebuilds from scratch, and the first groups to get a stone base up spend the next hours farming rockets against neighbours who still have a wooden 2x1. A short no-raid window evens that out: nobody can damage structures for the first few hours, everyone gets their footprint down, then raiding opens normally.
The plugin records the save time the first time it sees a fresh map. It hooks OnNewSave to reset that timestamp and OnEntityTakeDamage to cancel any incoming damage to a building block, deployable or turret that sits inside a building privilege zone while the cooldown is still running. Damage from NPCs, animals and the environment is left alone, and so are players who are not part of a raid (fall damage, hunger, radiation). /raidcooldown prints the remaining time to chat.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Wipe Raid Cooldown", "RustPluginGenerator", "1.0.0")]
[Description("Blocks player damage to structures for the configured number of hours after each new save.")]
public class WipeRaidCooldown : RustPlugin
{
private class PluginConfig
{
[JsonProperty("Cooldown Hours")]
public float CooldownHours = 12f;
[JsonProperty("Notify Attacker")]
public bool NotifyAttacker = true;
[JsonProperty("Notify Interval Seconds")]
public float NotifyIntervalSeconds = 60f;
[JsonProperty("Save Timestamp")]
public DateTime SaveTimestamp = DateTime.MinValue;
}
private const string PermUse = "wiperaidcooldown.use";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastNotified = new Dictionary<ulong, float>();
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>
{
["CooldownActive"] = "Raiding is disabled for another {0}.",
["RaidingOpen"] = "Raiding is currently enabled.",
["NoPermission"] = "You do not have permission to use this command.",
["BlockedNotice"] = "Raiding is disabled for another {0} after the wipe."
}, 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);
}
private void OnNewSave(string strFilename)
{
config.SaveTimestamp = DateTime.UtcNow;
SaveConfig();
}
private void Unload()
{
SaveConfig();
}
private bool IsCooldownActive()
{
if (config == null || config.SaveTimestamp == DateTime.MinValue) return false;
return DateTime.UtcNow < config.SaveTimestamp.AddHours(config.CooldownHours);
}
private TimeSpan Remaining()
{
if (config == null || config.SaveTimestamp == DateTime.MinValue) return TimeSpan.Zero;
TimeSpan span = config.SaveTimestamp.AddHours(config.CooldownHours) - DateTime.UtcNow;
return span < TimeSpan.Zero ? TimeSpan.Zero : span;
}
private string FormatSpan(TimeSpan span)
{
int hours = (int)span.TotalHours;
int minutes = span.Minutes;
if (hours > 0) return string.Format("{0}h {1}m", hours, minutes);
if (minutes > 0) return string.Format("{0}m", minutes);
return string.Format("{0}s", Mathf.Max(1, span.Seconds));
}
private bool IsProtected(BaseEntity entity)
{
if (entity == null || entity.IsDestroyed) return false;
if (entity is BuildingBlock) return true;
if (entity is Door) return true;
if (entity is StorageContainer) return true;
if (entity is BuildingPrivlidge) return true;
if (entity.GetBuildingPrivilege() != null) return true;
return false;
}
private object HandleDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
if (!IsCooldownActive()) return null;
if (entity == null || hitInfo == null) return null;
if (hitInfo.InitiatorPlayer == null) return null;
if (!IsProtected(entity)) return null;
ulong userId = hitInfo.InitiatorPlayer.userID.Get();
if (config.NotifyAttacker)
{
float now = Time.realtimeSinceStartup;
float last;
if (!lastNotified.TryGetValue(userId, out last) || now - last >= config.NotifyIntervalSeconds)
{
lastNotified[userId] = now;
string msg = Msg("BlockedNotice", hitInfo.InitiatorPlayer.UserIDString, FormatSpan(Remaining()));
hitInfo.InitiatorPlayer.ChatMessage(msg);
}
}
hitInfo.damageTypes.ScaleAll(0f);
return false;
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
return HandleDamage(entity, hitInfo);
}
private object OnEntityTakeDamage(BasePlayer player, HitInfo hitInfo)
{
return null;
}
[ChatCommand("raidcooldown")]
private void CmdRaidCooldown(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
if (IsCooldownActive())
player.ChatMessage(Msg("CooldownActive", player.UserIDString, FormatSpan(Remaining())));
else
player.ChatMessage(Msg("RaidingOpen", player.UserIDString));
}
}
}Hooks used: OnNewSave, OnEntityTakeDamage
Config file
Created automatically on first load at oxide/config/WipeRaidCooldown.json.
{
"Cooldown Hours": 12,
"Notify Attacker": true,
"Notify Interval Seconds": 60,
"Save Timestamp": "0001-01-01T00:00:00"
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/raidcooldown | Shows how much time is left before raiding is enabled, or confirms raiding is already open. | wiperaidcooldown.use |
| Permission | Grants |
|---|---|
wiperaidcooldown.use | Allows the player to run /raidcooldown. |
How the code works
Store the wipe timestamp
The wipe timestamp is a config field (
Save Timestamp) written through the normal config file, so it survives reloads and restarts. WhenOnNewSavefires with the new save filename, the plugin sets the field toDateTime.UtcNowand callsSaveConfig(). A fresh install with no timestamp yet falls back to treating the cooldown as inactive until the next save, so you never block a running server by accident.Decide whether raiding is still closed
A single helper
IsCooldownActive()compares the stored timestamp plus the configuredCooldown HoursagainstDateTime.UtcNow. If the timestamp is missing or older than that, it returns false and the damage hook costs almost nothing. The helper is used by both the damage filter and the chat command, so the number shown by/raidcooldownis always the number the hook uses.Filter damage in OnEntityTakeDamage
The hook returns
nullto keep vanilla damage, or a non-null object to cancel it. TwoOnEntityTakeDamageoverloads are declared forBaseCombatEntityandBasePlayerso Oxide dispatches on the entity type. Only structure damage is cancelled: the entity must be aBuildingBlock, aDoor, aStorageContainer, or any deployable with aGetBuildingPrivilege()result. Player-vs-player damage, animal damage and fall damage are never touched.Require a player attacker
Raiding means a player breaching a base. If
hitInfo.InitiatorPlayeris null (a bear biting a door, decay, a supply drop landing on a box) the damage passes through untouched. This keeps PvE and environmental damage working during the cooldown window and avoids unexpected side effects on server events like airdrops or NPC raids.Give feedback without spamming
Blocked hits are silent by default; a chat line on every bullet would flood the attacker. The config flag
Notify Attackersends a single localised message per player perNotify Interval Secondsusing a dictionary of last-notify times. That is enough for players to learn the rule without the plugin becoming a chat nuisance.The /raidcooldown command
The chat command checks the
wiperaidcooldown.usepermission, then either reports the remaining time formatted as hours and minutes or states that raiding is open. Every string comes from the language file, so admins can rename the wording inoxide/lang/en/WipeRaidCooldown.jsonwithout touching code.
Install it on your server
Save WipeRaidCooldown.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/WipeRaidCooldown.json(Carbon:carbon/configs/WipeRaidCooldown.json). After editing, reload it:oxide.reload WipeRaidCooldown c.reload WipeRaidCooldown # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default wiperaidcooldown.use c.grant group default wiperaidcooldown.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Notify both sides with a UI timer
Add a small CUI bar with the remaining cooldown for every connected player.
Generate this versionRestrict to specific structure types
Only block damage to building blocks and doors, letting players loot boxes and destroy barricades.
Generate this versionManual override for early opens
Add a console command that lifts the cooldown immediately.
Generate this version
FAQ
How does the plugin know when a wipe happened?
Oxide calls OnNewSave when the server creates a new save file, which happens after a forced wipe. The plugin writes DateTime.UtcNow into the Save Timestamp config key at that moment. If you restore an old save the hook does not fire, so the previous timestamp is kept.
Does this block all damage to bases?
It blocks damage that comes from a player to a building block, door, storage container or deployable inside a privilege zone. Decay, animal attacks and environmental damage continue as normal. PvP damage is never blocked by this plugin.
Will it work with Carbon?
Yes. Carbon loads the same Oxide plugin file and dispatches the same hooks, so the compiled DLL runs unchanged. Install it through your Carbon plugins folder like any other Oxide plugin and the config file is written to oxide/config regardless.
What is the typical cooldown length?
Most servers run 6 to 12 hours. Set Cooldown Hours in the config; the value is a float so 1.5 gives a 90 minute window. /raidcooldown reports the remainder in hours and minutes.
Can I change the message language?
Yes. Edit oxide/lang/en/WipeRaidCooldown.json after the first load. Keys are CooldownActive, RaidingOpen, BlockedNotice and NoPermission. Run oxide.reload WipeRaidCooldown after saving changes.
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 raiding plugins
- Explosive damage multiplierScales explosion damage against building blocks, deployables and other structures using per-weapon multipliers keyed by the attacker's weapon prefabโฆOnEntityTakeDamage
- New player protectionBlocks damage to buildings owned by players under a configurable playtime threshold, tracks playtime per player in oxide/data, and removesโฆOnServerInitializedOnNewSaveOnEntityTakeDamage
- Offline raid protectionScales damage to building blocks and deployables when every player authorized on the covering tool cupboard has been offline for a configurable delay.OnServerInitializedOnCupboardAuthorizeOnCupboardDeauthorize
Last updated Oct 26, 2026.
Customize no raiding after wipe
Customize