Skip to content
RustPluginGenerator

OnServerInitialized

OnServerInitialized is called by Oxide once the server has fully started and map data is ready, and again with false whenever a plugin is (re)loaded while the server is already running.

Signature
void OnServerInitialized(bool initial)

Parameters

Hook parameters
ParameterTypeDescription
initialboolTrue when the hook fires once during normal boot after the server finished loading; false when it fires because a plugin was reloaded or hotloaded into an already running server.

Return value

The return value is ignored. Both call sites treat the hook as void and continue with their own code regardless of what the plugin returns, so there is nothing to influence by returning a value.

How it works

This hook is invoked by Oxide core rather than a patched game method. RustCore.IOnServerInitialized runs after the initial map and world load completes, sets its internal serverInitialized flag and calls OnServerInitialized with that flag (true). The second path, RustCore.OnPluginLoaded, fires when a plugin loads while serverInitialized is already true, passing false – this is what happens on oxide.reload, hotloads and Carbon reloads.

That means initial lets you separate one-time startup work (spawning entities, building caches, scheduling long timers) from re-initialisation after a reload, where the world already reflects earlier runs. Guard against duplicate spawns by checking for existing entities before creating new ones when initial is false.

This is the right place for work that needs the map, world and all plugins to exist: reading BaseNetworkable.serverEntities, requiring permissions, creating data files, and reading config. Do not assume it fires exactly once per process – reloads trigger it again. Keep the hook short; heavy synchronous loops here delay startup.

Example plugin: Broadcast server uptime on a command

ServerStartInfo.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Server Start Info", "RustPluginGenerator", "1.0.0")]
    [Description("Reports server uptime and a configurable server name through a chat command.")]
    public class ServerStartInfo : RustPlugin
    {
        private const string PermUse = "serverstartinfo.use";

        private PluginConfig config;
        private DateTime startedAt;
        private bool bootedFromInitial;

        private class PluginConfig
        {
            [JsonProperty("Server display name")]
            public string ServerName = "My Rust Server";

            [JsonProperty("Require permission to use the command")]
            public bool RequirePermission = true;
        }

        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>
            {
                ["Response"] = "{0} - up to {1} hours {2} minutes online (boot: {3}).",
                ["NoPermission"] = "You do not have permission to use this command."
            }, 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 OnServerInitialized(bool initial)
        {
            if (initial)
            {
                startedAt = DateTime.UtcNow;
                bootedFromInitial = true;
            }
            else if (startedAt == default(DateTime))
            {
                // Plugin was reloaded after boot; keep the process alive time unknown.
                startedAt = DateTime.UtcNow;
                bootedFromInitial = false;
            }
        }

        [ChatCommand("startinfo")]
        private void CmdStartInfo(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (config.RequirePermission && !permission.UserHasPermission(player.UserIDString, PermUse))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            var uptime = DateTime.UtcNow - startedAt;
            if (uptime.TotalSeconds < 0) uptime = TimeSpan.Zero;

            player.ChatMessage(Msg("Response", player.UserIDString,
                config.ServerName,
                Mathf.FloorToInt((float)uptime.TotalHours),
                uptime.Minutes,
                bootedFromInitial ? "initial load" : "plugin reload"));
        }
    }
}

The plugin records the startup moment and answers a startinfo chat command with uptime, a configured server name and whether the hook fired on boot or on a reload. OnServerInitialized stores DateTime.UtcNow only when initial is true, so reloading the plugin does not reset the uptime clock. Permissions (serverstartinfo.use) are registered in Init, and all player text goes through the language file. A small config object holds the display name and whether the command is permission gated. No timers or entity spawning are involved, so the hook stays cheap even on repeated reloads.

Common use cases

  • One-time world setup

    Spawn custom monuments, vendor NPCs or supply crates only when initial is true, and check BaseNetworkable.serverEntities before spawning on reloads so you do not double up.

  • Startup logging and reports

    Print map size, entity counts and loaded plugin versions to the console once the server is ready, giving admins a clear boot record.

  • Deferred cache building

    Build lookup tables for recyclers, quarries or loot containers that only exist after the map has loaded, refreshing them when the hook fires with false.

Pitfalls

  • It fires more than once per process

    Every reload or hotload of your plugin calls the hook again with initial false. Anything that creates entities or writes data must be idempotent.

  • Do not rely on other plugins being ready

    Plugin load order is not guaranteed. If you depend on another plugin via [PluginReference], check that it is not null here and handle the case where it loads later.

  • Blocking work delays the server

    Long loops over serverEntities or large data file writes run synchronously during startup and can cause a connection queue. Cache what you need and spread heavy work over timers.

Plugin recipes that use OnServerInitialized

Build a plugin with OnServerInitialized

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: One-time world setup using the OnServerInitialized hook, with a config file and a permission
Generate this plugin

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 OnServerInitialized

Generate