OnEntityLoaded
OnEntityLoaded is called from BaseNetworkable.Load, after a network update payload arrives for an entity and before the game applies that payload to the object. It fires for existing entities receiving state from the network, including on the client boundary of the server's load path.
void OnEntityLoaded(BaseNetworkable entity, BaseNetworkable.LoadInfo info)Parameters
| Parameter | Type | Description |
|---|---|---|
entity | BaseNetworkable | The BaseNetworkable instance whose Load method is running. Cast it to BaseEntity, StorageContainer, BaseOven and so on to read game state; IsDestroyed is false here because loading happens before the entity is usable. |
info | BaseNetworkable.LoadInfo | The BaseNetworkable.LoadInfo struct passed into Load. info.msg holds the ProtoBuf payload; info.msg.baseNetworkable is non-null here because Oxide only fires the hook inside that branch, so you can inspect prefabID and other network fields. |
Return value
The hook's return value is ignored; Load returns void and continues unconditionally after the call. You cannot cancel or alter the load from this hook.
How it works
The hook fires inside BaseNetworkable.Load(LoadInfo info), guarded by info.msg.baseNetworkable != null. That means it runs whenever a load message carrying a BaseNetworkable record is processed: entity spawn/creation data arriving at a client or server-side load, resends, and network streaming of existing objects. It runs before the prefab ID check and before the message fields are copied onto the instance, so the entity is still in its previous state when your code executes.
Because it fires on the load path rather than a single spawn event, it can be called repeatedly for the same entity. Treat it as a notification that a load message was received, not a one-time spawn signal; use OnEntitySpawned for first-spawn logic.
Use entity to identify the object (entity.net.ID.Value, ShortPrefabName) and info.msg.baseNetworkable.prefabID to inspect what the message declares. Narrowing by declaring BaseEntity entity instead of BaseNetworkable makes Oxide skip non-entity networkables such as bases or other non-entity types, but does not otherwise restrict which prefabs you see. Keep the body cheap: it can fire for many networkables during map load and during play.
Example plugin: Alert admins when a high-value entity loads with a suspicious prefab ID
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Load Prefab Audit", "RustPluginGenerator", "1.0.0")]
[Description("Records entities whose load message prefab ID does not match the loaded entity")]
public class LoadPrefabAudit : RustPlugin
{
private const string PermUse = "loadprefabaudit.use";
private PluginConfig config;
private readonly List<string> findings = new List<string>();
private class PluginConfig
{
[JsonProperty("Alert permission holders")]
public bool AlertPermissionHolders = true;
[JsonProperty("Maximum stored findings")]
public int MaxFindings = 50;
[JsonProperty("Ignored prefab shortnames")]
public List<string> IgnoredPrefabs = new List<string>
{
"player",
"scientistnpc",
"crate_normal",
"cupboard.tool"
};
}
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);
private void Init()
{
permission.RegisterPermission(PermUse, this);
}
private void OnEntityLoaded(BaseNetworkable entity, BaseNetworkable.LoadInfo info)
{
if (entity == null || entity.IsDestroyed) return;
if (info.msg == null || info.msg.baseNetworkable == null) return;
var baseEntity = entity as BaseEntity;
if (baseEntity == null) return;
var shortName = entity.ShortPrefabName;
if (string.IsNullOrEmpty(shortName)) return;
if (config.IgnoredPrefabs.Contains(shortName)) return;
if (baseEntity.prefabID == info.msg.baseNetworkable.prefabID) return;
var line = string.Format("{0} ({1}) declared prefab {2}, entity has {3}",
shortName, entity.net.ID.Value, info.msg.baseNetworkable.prefabID, baseEntity.prefabID);
findings.Add(line);
if (findings.Count > config.MaxFindings) findings.RemoveAt(0);
if (config.AlertPermissionHolders) AlertAdmins(line);
}
private void AlertAdmins(string line)
{
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null || !player.IsConnected) continue;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) continue;
player.ChatMessage("[PrefabAudit] " + line);
}
}
[ChatCommand("prefabaudit")]
private void CmdPrefabAudit(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse) && !player.IsAdmin)
{
player.ChatMessage("You do not have permission to use this command.");
return;
}
if (findings.Count == 0)
{
player.ChatMessage("No prefab mismatches recorded.");
return;
}
player.ChatMessage(string.Format("Last {0} mismatches (newest last):", findings.Count));
foreach (var entry in findings) player.ChatMessage(" " + entry);
}
}
}The plugin keeps a config list of short prefab names it cares about and watches OnEntityLoaded. Each call casts the networkable to BaseEntity, ignores anything not matching a watched prefab on the ignore list, and compares the prefab ID in the load message against the entity's own prefab ID. A mismatch is written to a bounded in-memory list and players with the audit permission get a chat warning. An admin command dumps recent findings. Data stays in memory only; nothing is logged from the hot path beyond a single broadcast that is rate-limited by the mismatch check itself.
Common use cases
Detect prefab ID mismatches
Compare
info.msg.baseNetworkable.prefabIDwith the entity's own prefab ID to spot corrupted or spoofed load messages early.Track load traffic for key prefabs
Count how often load messages arrive for crates, vehicles or deployables to diagnose network churn on a busy server.
Tag entities before their state is applied
Record the network ID and prefab of incoming entities in a data structure so later hooks such as OnEntitySpawned can correlate them.
Pitfalls
It is not a spawn hook
Load runs for existing entities receiving network state and can fire multiple times per object. Use OnEntitySpawned when you need once-per-lifetime logic.
Fires in bulk
During map load and streaming this can be called for thousands of networkables. Avoid expensive lookups, allocations and logging inside it.
State is not applied yet
The message fields are copied after the hook returns, so reading health, flags or inventory from the entity gives pre-load values, not the incoming payload.
Build a plugin with OnEntityLoaded
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 OnEntityLoaded
Generate