Skip to content
RustPluginGenerator

How to Spawn and Manage Entities in a Rust Plugin (Oxide/Carbon)

To spawn an entity in a Rust plugin, call GameManager.server.CreateEntity with a full prefab path, a position and a rotation, then call Spawn() on the result. Entities form a hierarchy (BaseNetworkable, BaseEntity, BaseCombatEntity) that decides which members you can use. This guide covers prefab paths, finding entities, ownership, flags, network updates, killing entities and cleanup on unload.

6 min readUpdated Oct 26, 2026

To spawn an entity from a Rust plugin, call GameManager.server.CreateEntity(prefabPath, position, rotation), check that the result is not null, optionally configure it, and then call Spawn(). The prefab path must be the full resource path, not the short name. The same code works on Oxide and on Carbon, which loads Oxide plugins.

The entity class hierarchy

Almost everything in the world is a networked object. Knowing where a member is defined tells you what you can call on a given instance.

ClassWhat it addsTypical members
BaseNetworkableNetwork identity and lifetimenet.ID.Value, ShortPrefabName, PrefabName, IsDestroyed, Kill()
BaseEntityPosition, ownership, flags, parentingOwnerID, skinID, SendNetworkUpdate(), HasFlag, SetFlag, GetParentEntity()
BaseCombatEntityHealth and damageHealth(), MaxHealth(), health, Hurt(), Heal(), Die()

BasePlayer, BradleyAPC, PatrolHelicopter, AutoTurret and BuildingBlock are all combat entities. StorageContainer, Door and CodeLock are entities that you reach through more specific types. A StorageContainer has an inventory (ItemContainer), and a BuildingBlock has grade and buildingID.

Most hooks hand you a BaseEntity or BaseCombatEntity. Cast with as and null-check before using a specific type:

C#
var box = entity as StorageContainer;
if (box == null || box.inventory == null) return;

Prefab paths versus short prefab names

Two different strings identify a prefab, and they are not interchangeable.

  • Prefab path (PrefabName): the full resource path, for example assets/prefabs/deployable/campfire/campfire.prefab. This is what CreateEntity needs.
  • Short prefab name (ShortPrefabName): only the file name without folders or extension, for example campfire. This is convenient for comparing what an entity is, but it cannot be used to spawn.

Short names are not guaranteed unique across the whole game, so for strict identification compare PrefabName when it matters, and use ShortPrefabName for quick checks in hooks:

C#
if (entity.ShortPrefabName != "campfire") return;

To find a path, read it from an existing entity. Spawn the object in game with an admin tool, look at PrefabName through a small debug command, and copy the value into a constant. Do not guess paths from memory; a wrong path simply returns null.

Spawning with CreateEntity and Spawn

The process has three steps: create, configure, spawn. Configure before Spawn() so the initial network snapshot already contains your values.

C#
private BaseEntity SpawnAt(string prefab, Vector3 position, ulong ownerId = 0)
{
    var entity = GameManager.server.CreateEntity(prefab, position, Quaternion.identity);
    if (entity == null) return null;

    entity.OwnerID = ownerId;
    entity.Spawn();
    return entity;
}

Notes:

  • CreateEntity returns a BaseEntity and may return null. Never call Spawn() on an unchecked result.
  • Until Spawn() runs, the entity is not visible to players and is not in BaseNetworkable.serverEntities.
  • A rotation of Quaternion.identity points the entity along the world axes. To face a player, build a rotation from their view direction, for example with Quaternion.LookRotation.
  • Some entities need extra setup before or after spawning, such as vehicles needing fuel or NPCs needing a loadout. Ready-made examples are in Animal spawn command, Car spawn command and Horse spawn command.
  • For ground placement, get the terrain height with TerrainMeta.HeightMap.GetHeight(position) and set the y value, so the entity does not float or sink.

A complete spawn command plugin

This plugin spawns a campfire in front of a player, owned by them, and tracks it so it can be cleaned up when the plugin unloads.

C#
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Spawn Campfire", "RustPluginGenerator", "1.0.0")]
    [Description("Spawns a campfire in front of the player and removes it on unload")]
    public class SpawnCampfire : RustPlugin
    {
        private const string PermUse = "spawncampfire.use";
        private const string Prefab = "assets/prefabs/deployable/campfire/campfire.prefab";

        private readonly List<BaseEntity> spawned = new List<BaseEntity>();
        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Spawn distance in meters")]
            public float Distance = 3f;
        }

        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 use this command.",
                ["Failed"] = "Could not spawn the entity.",
                ["Spawned"] = "Campfire spawned."
            }, 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);
        }

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

            Vector3 pos = player.transform.position + player.eyes.HeadForward() * config.Distance;
            pos.y = TerrainMeta.HeightMap.GetHeight(pos);

            var entity = GameManager.server.CreateEntity(Prefab, pos, Quaternion.identity);
            if (entity == null)
            {
                player.ChatMessage(Msg("Failed", player.UserIDString));
                return;
            }

            entity.OwnerID = player.userID.Get();
            entity.Spawn();
            spawned.Add(entity);
            player.ChatMessage(Msg("Spawned", player.UserIDString));
        }

        private void Unload()
        {
            foreach (var entity in spawned)
            {
                if (entity != null && !entity.IsDestroyed) entity.Kill();
            }
            spawned.Clear();
        }
    }
}

The permission is named after the lowercase class name, and the list of spawned entities is cleared in Unload. See Oxide permissions and groups for granting it.

Finding entities

Pick the cheapest method that answers your question.

Tracking through hooks

If you only care about entities created while the plugin runs, use OnEntitySpawned and keep your own collection. Remove entries in OnEntityKill or OnEntityDeath, or check IsDestroyed before use. This costs almost nothing per frame.

Scanning serverEntities

To adopt entities that already exist, enumerate BaseNetworkable.serverEntities once, typically in OnServerInitialized:

C#
private void OnServerInitialized(bool initial)
{
    foreach (var networkable in BaseNetworkable.serverEntities)
    {
        var entity = networkable as BaseEntity;
        if (entity == null || entity.IsDestroyed) continue;
        if (entity.ShortPrefabName != "campfire") continue;
        // adopt or inspect the entity
    }
}

Do not kill or spawn entities inside this loop. Changing the collection while enumerating it can raise the exception described in collection was modified. Collect targets into a list first, then act on the list. A full scan is expensive on large maps, so avoid running it on frequent timers; see Rust plugin performance.

Physics overlap

For entities near a point, use a Unity physics query, such as Physics.OverlapSphere(position, radius), and read the entity from each collider with GetComponentInParent<BaseEntity>(). Colliders belong to child objects, so look upward to the parent. Use a layer mask where you can, so you do not collect terrain and unrelated colliders. Deduplicate results, since one entity can have several colliders.

Raycasts

To find what a player is looking at, cast a ray from player.eyes.position along player.eyes.HeadForward() with Physics.Raycast. Then resolve the hit collider to an entity the same way, with GetComponentInParent<BaseEntity>(). Set a sensible maximum distance, and handle the case where the ray hits nothing or only terrain. The Admin entity remover recipe is built on this pattern.

Ownership with OwnerID

OwnerID is a ulong on BaseEntity. It is 0 when nobody owns the entity. Game code sets it for deployables and vehicles placed by a player, and many plugins read it for limits, protection and cleanup.

  • Set it with entity.OwnerID = player.userID.Get(); before Spawn().
  • Compare with a player using entity.OwnerID == player.userID.Get().
  • It identifies who placed an object. It does not grant building privilege or lock access.
  • Entities spawned by your plugin for the server (events, props) should usually keep OwnerID at 0 so owner-based plugins ignore them.

See Entity owner lookup and Deployable limits for practical uses.

Flags and network updates

BaseEntity.Flags are bits that describe state, such as Open or On. Read with HasFlag(BaseEntity.Flags.Open) and change with SetFlag(BaseEntity.Flags.Open, true). SetFlag normally sends the change to clients itself, so you rarely need to do more.

SendNetworkUpdate() is for the other case: you changed a field directly, for example skinID, and clients must receive the new data. Call it once after your changes, not once per field. Calling it every tick for many entities creates network load and shows up as slow hooks.

Prefer the typed accessors when they exist, such as Door.IsOpen() and BaseOven.IsOn(), since they express intent better than raw flag checks.

Killing entities

  • Kill() on BaseNetworkable removes the entity right away. It is the right call for your own props and for silent cleanup.
  • Die() on BaseCombatEntity triggers the death path, so use it when you want death effects and death hooks to run.
  • Check IsDestroyed before touching an entity you stored earlier. Players and the game can remove it at any time, so a stored reference may be stale.
  • Never kill a BasePlayer with Kill() to simulate death. Use the damage path or Die() so the game handles corpses and respawn correctly.

If a hook receives an entity you want to remove, consider delaying with NextTick rather than killing it in the middle of the game's own processing.

Cleanup on unload

Plugins reload often: every time the file is saved, and on every oxide.reload. Anything you spawn is not removed for you. Without cleanup, each reload stacks another copy.

  1. Keep every spawned entity in a list, or save their net.ID.Value values in a data file if they must persist.
  2. In Unload, kill the tracked entities that are not destroyed, destroy timers, and save data.
  3. At startup, decide how to treat leftovers from a previous run: adopt them from your data file or kill them.
  4. For permanent entities, store identifiers in data files and re-find them in OnServerInitialized. Saves are written on OnServerSave, and a new wipe triggers OnNewSave, where stored ids become invalid and should be cleared.

For broader cleanup ideas, see Entity cleanup.

Common mistakes

  • Using ShortPrefabName in CreateEntity. Use the full path.
  • Not checking null from CreateEntity, which then causes a NullReferenceException in a hook.
  • Comparing net.ID directly with a ulong. Use net.ID.Value; see the NetworkableId conversion error.
  • Scanning serverEntities on a short timer.
  • Holding references to entities across a wipe or after they are destroyed.

For the underlying hook model, read Oxide hooks explained. To have a plugin written for you, use the generator.

FAQ

Why does CreateEntity return null?

The most common cause is a wrong or misspelled prefab path, such as a short prefab name or a path with a typo. Always null-check the result before calling Spawn(). Prefab paths are case-sensitive in practice and change rarely, but a game update can rename one, so log a single warning at startup if a path you rely on fails.

Does an entity I spawn survive a server restart?

Entities created with CreateEntity and Spawn() are saved with the world like any other networked entity if they are of a savable type. A plugin that wants to control this should track its own entities in a data file and kill or re-adopt them at startup. Do not assume your plugin's entities vanish on restart.

What is the difference between Kill() and Die()?

Kill() removes the entity immediately without death behavior. Die() exists on BaseCombatEntity and runs the normal death flow, which can trigger effects, corpses, debris and the OnEntityDeath hook. Use Kill() for silent cleanup of your own entities and Die() when you want the entity to be destroyed as if it had been killed.

Is it safe to loop over BaseNetworkable.serverEntities on every tick?

No. serverEntities can hold a very large number of objects on a populated map, so a full scan is expensive. Run it rarely, for example once at startup or on a long timer, and prefer hooks such as OnEntitySpawned to track entities incrementally. Slow hooks are reported in the console as hook performance warnings.

Do I need to set OwnerID on spawned entities?

Only if something should recognize the owner. OwnerID is a ulong on BaseEntity and defaults to 0, meaning no owner. Setting it to a player's id lets other plugins and game logic such as lookups, limits and cleanup tools attribute the entity. Setting OwnerID does not by itself grant building or authorization rights.

Skip the C#: describe your plugin

Open generator