OnNewSave
OnNewSave is called by SaveRestore.Load when the save file the server tries to load does not exist, which happens when a server starts with a freshly generated world.
void OnNewSave(string filename)Parameters
| Parameter | Type | Description |
|---|---|---|
filename | string | The save file path the server tried to load, normally World.SaveFolderName + "/" + World.SaveFileName (for example "proceduralmap.4500.123.sav"). Useful for logging or for naming a backup file; by the time your hook runs the file is already known to be missing. |
Return value
The return value is ignored. Your reload still proceeds with the original file-existence check, so returning null, true or nothing at all makes no difference to whether the server starts.
How it works
The hook fires inside SaveRestore.Load, right after the server builds the save path from World.SaveFolderName and World.SaveFileName and finds that no file exists at that path. In practice this means the world is new: the map seed generated new terrain and there is no saved state to restore, so the server is about to write a clean save. The single parameter is the filename that failed the existence check.
Because the call happens during server startup and loading, the hook runs before OnServerInitialized and before any player can connect. Do not expect network entities, player data or your own saved data to be meaningful at this point beyond what already exists in oxide/data. Treat the hook as a one-shot signal that this world has never been saved before.
There is only one variant of the signature. Declaring it as private void OnNewSave(string filename) is the normal form; declaring it with no parameters is also accepted by Oxide's dispatch, but then you lose the file path. The return value is ignored, so returning anything has no effect on the load path.
Example plugin: Message the admin and snapshot data on a fresh wipe
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("New Save Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Records when a server starts on a brand new save and notifies staff")]
public class NewSaveNotifier : RustPlugin
{
private const string PermNotify = "newsavenotifier.notify";
private const string DataFile = "NewSaveNotifier";
private PluginConfig config;
private StoredData data;
private class PluginConfig
{
[JsonProperty("Announce in chat to admins")]
public bool AnnounceToAdmins { get; set; } = true;
}
private class StoredData
{
[JsonProperty("Last new save filename")]
public string LastFilename { get; set; } = string.Empty;
[JsonProperty("Last new save time (UTC)")]
public string LastTimeUtc { get; set; } = string.Empty;
[JsonProperty("Announcement sent")]
public bool AnnouncementSent { get; set; }
}
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>
{
["NewSaveDetected"] = "New save detected: {0}",
["AdminSummary"] = "This server started on a new save ({0}). Staff have been notified."
}, 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(PermNotify, this);
}
private void OnServerInitialized()
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(DataFile) ?? new StoredData();
if (string.IsNullOrEmpty(data.LastFilename))
{
return;
}
PrintWarning(Msg("NewSaveDetected", null, data.LastFilename));
if (!config.AnnounceToAdmins || data.AnnouncementSent)
{
return;
}
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
if (player == null || !player.IsConnected) continue;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermNotify)) continue;
player.ChatMessage(Msg("AdminSummary", player.UserIDString, data.LastFilename));
}
}
private void OnNewSave(string filename)
{
data = new StoredData
{
LastFilename = filename ?? string.Empty,
LastTimeUtc = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
AnnouncementSent = false
};
Interface.Oxide.DataFileSystem.WriteObject(DataFile, data);
PrintWarning("Fresh world detected, save file was missing: " + data.LastFilename);
}
private void Unload()
{
if (data != null)
{
Interface.Oxide.DataFileSystem.WriteObject(DataFile, data);
}
}
}
}The plugin listens for OnNewSave, stores the reported filename and startup time in a small data file, and immediately writes a message to the server console and to the chat queue so staff know a brand new world was detected. A flag in the data file prevents repeated broadcasts if the world is saved and the server restarted later. OnServerInitialized then prints a short summary and, if a permission is configured, tells connected admins the server started on a new save. The message text lives in the language file so the wording can be translated or edited, and the data file is flushed in Unload.
Common use cases
Wipe announcements
Record the moment a new save is created and broadcast a wipe message once players can actually receive chat, for example from
OnServerInitializedinstead of directly insideOnNewSave.Reset plugin data per world
Clear per-wipe counters, leaderboards or event cooldowns stored in
oxide/dataso a fresh map starts with fresh statistics.Operational logging
Write the generated save filename and timestamp to a log file or Discord bridge webhook so administrators can track when each server instance was actually wiped.
Pitfalls
Not the same as a manual wipe of saved entities
The hook only fires when the save file is absent at load time. Deleting individual entity data, running a wipe command on a live server, or restoring a backup does not trigger it.
Chat will not reach anyone
No players are connected during startup, so
PrintToChatandServer.Broadcastsent from this hook are effectively lost. Defer player-facing messages toOnServerInitializedorOnPlayerConnected.Runs on every server in a cluster
Each server that starts with a missing save file raises the hook independently, so cluster setups can fire it once per instance. Guard shared data writes with a timestamp or flag.
Plugin recipes that use OnNewSave
- How to make a Rust first blood plugin (announce + reward first kill)Announces the first player kill after a restart as First Blood and pays the killer a configured reward. It listens to OnEntityDeath, stores the claim time in oxide/data so it survives restarts, and optionally wipes that data on a new save via OnNewSave.
- How to make a Rust gather statistics plugin (wipe totals)Tracks wood, stone, metal ore, sulfur ore and cloth gathered per player for the current wipe, persists the totals in oxide/data, and shows them with /gathered. Uses OnDispenserGathered, OnCollectiblePickup and OnNewSave.
- How to make a Rust kits plugin with cooldowns (Oxide/Carbon)Adds permission-aware kits with per-kit cooldowns and per-wipe use limits. Uses OnNewSave to reset stored cooldowns and uses when a new save is created.
- How to make a Rust new player protection plugin (raid protection)Blocks damage to buildings owned by players under a configurable playtime threshold, tracks playtime per player in oxide/data, and removes protection early when the protected player damages another player's base. Uses OnEntityTakeDamage and OnNewSave.
- How to make a Rust one-time claim kits plugin (Oxide)Gives players configurable kits that can be claimed once per wipe, tracked in a data file cleared when the server starts a new map save, using the OnNewSave hook.
- How to make a Rust player stats plugin (K/D tracker)A plugin that records kills, deaths, headshots and NPC kills per player in oxide/data, shows them with /stats [player], and optionally wipes the data on a new save. It uses OnEntityDeath to detect who killed whom and OnNewSave to reset on wipe.
- How to make a Rust playtime tracker plugin (total and wipe time)Records session start time on connect and adds elapsed connected time on disconnect, persisted per player ID in oxide/data, with a /playtime command that reports hours and minutes. Uses OnPlayerConnected, OnPlayerDisconnected and OnNewSave.
- 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.
- 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.
Build a plugin with OnNewSave
Describe what should happen and get a compiled plugin that uses this hook.
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 OnNewSave
Generate