Skip to content
RustPluginGenerator

OnPlayerSpawn

OnPlayerSpawn is called from ServerMgr.SpawnNewPlayer right after the player entity is created for a connecting client. Returning a non-null value stops the original spawn routine and leaves the behavior entirely to your plugin.

Signature
object OnPlayerSpawn(BasePlayer player, Network.Connection connection)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer entity just created from the player prefab. It has no owner state settled yet and is still in the dead state until the original spawn code runs.
connectionNetwork.ConnectionThe network connection associated with the incoming player. Use connection.userid for the Steam ID and connection.connection.connection to inspect socket-level info if needed.

Return value

Returning null lets ServerMgr.SpawnNewPlayer continue and apply the default health, lifesteat and spawn handling. Returning any non-null object (even true) makes the original method skip the rest of its body and return the player entity immediately, so your plugin becomes responsible for finishing the spawn.

How it works

The hook fires inside ServerMgr.SpawnNewPlayer, immediately after the game creates the player.prefab entity at a spawn point and before the default health/lifestate values are applied. It is part of the join sequence: a connecting client has a connection, the server picks a spawn point, builds the player entity, then calls this hook.

player is the freshly constructed BasePlayer. At hook time it is not yet marked dead and spawn logic has not finished, so treat it as a bare entity: check player.IsConnected, player.userID, and player.IsNpc before doing anything. connection lets you correlate with connection hooks and read connection.userid.

Because the injection site only continues when the hook returns non-null, you can short-circuit the normal spawn path deliberately. Returning null keeps vanilla behavior. This hook is not the same as OnPlayerRespawn or OnPlayerSleepEnded, which fire later in the lifecycle.

Declaring BasePlayer is required since the argument is a BasePlayer; narrowing to a subtype is not useful here.

Example plugin: Grant starter kit on first spawn

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

namespace Oxide.Plugins
{
    [Info("First Spawn Kit", "RustPluginGenerator", "1.0.0")]
    [Description("Gives a configurable starter kit to players the first time they spawn")]
    public class FirstSpawnKit : RustPlugin
    {
        private const string DataFileName = "FirstSpawnKit/granted";

        private PluginConfig config;
        private HashSet<string> granted = new HashSet<string>();

        private class PluginConfig
        {
            [JsonProperty("Starter items (shortname = amount)")]
            public Dictionary<string, int> Items = new Dictionary<string, int>
            {
                ["rock"] = 1,
                ["torch"] = 1,
                ["bandage"] = 3
            };

            [JsonProperty("Announce welcome message")]
            public bool Announce = 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>
            {
                ["Welcome"] = "Welcome to the server! Your starter kit has been placed in your inventory.",
                ["KitGiven"] = "Starter kit granted."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args) =>
            string.Format(lang.GetMessage(key, this, userId), args);

        private void Init()
        {
            granted = Interface.Oxide.DataFileSystem.ReadObject<HashSet<string>>(DataFileName) ?? new HashSet<string>();
        }

        private object OnPlayerSpawn(BasePlayer player, Network.Connection connection)
        {
            if (player == null || connection == null) return null;
            if (player.IsNpc) return null;

            string id = player.UserIDString;
            if (string.IsNullOrEmpty(id) || granted.Contains(id)) return null;

            granted.Add(id);
            SaveData();

            NextTick(() =>
            {
                if (player == null || !player.IsConnected) return;

                foreach (KeyValuePair<string, int> entry in config.Items)
                {
                    Item item = ItemManager.CreateByName(entry.Key, Mathf.Max(1, entry.Value));
                    if (item != null)
                    {
                        player.GiveItem(item);
                    }
                }

                if (config.Announce)
                {
                    player.ChatMessage(Msg("Welcome", id));
                }
            });

            return null;
        }

        private void SaveData()
        {
            Interface.Oxide.DataFileSystem.WriteObject(DataFileName, granted);
        }

        private void Unload()
        {
            SaveData();
        }
    }
}

The plugin tracks which Steam IDs have already received their starter kit by storing a set of IDs in oxide/data. In OnPlayerSpawn it null-checks the player, skips NPCs and disconnected clients, then gives the configured items with inventory.GiveItem on a short NextTick delay so the player entity is fully ready. It sends a chat message through the language file, adds the ID to the set and saves the data file. Returning null is implicit, so the normal spawn path still runs. Unload writes the data file one last time.

Common use cases

  • Starter inventory for new players

    Detect first-time Steam IDs and hand out a rock, torch and food so new joiners do not stand around empty-handed.

  • Spawn position and state overrides

    Return a value to skip vanilla spawn handling and teleport, heal or equip the player yourself. Only do this when you fully replace the spawn routine.

  • Greeting and server info

    Send a welcome message, rules reminder or short server info string to the player as they spawn, routed through the language file for localization.

Pitfalls

  • Not the same as respawn

    Players who die and respawn do not go through ServerMgr.SpawnNewPlayer each time; use OnPlayerRespawn or OnPlayerRespawned for that flow.

  • Returning anything skips vanilla spawn

    Even returning true short-circuits the rest of the method. If you return a value, ensure health, position and inventory are set yourself, or the player may end up in a broken state.

  • Null and NPC checks

    Guard against player == null and skip entities where player.IsNpc is true, since some NPC creation can route through similar spawn code in other setups.

Build a plugin with OnPlayerSpawn

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

Prompt: Starter inventory for new players using the OnPlayerSpawn 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 OnPlayerSpawn

Generate