OnPluginLoaded
OnPluginLoaded is called by Oxide right after a plugin finishes loading and is marked as loaded, before Oxide writes its "Loaded plugin" log line. It fires for every plugin on the server, including your own and each plugin loaded at runtime.
void OnPluginLoaded(Plugin plugin)Parameters
| Parameter | Type | Description |
|---|---|---|
plugin | Plugin | The Oxide Plugin instance that has just finished loading. Use plugin.Name, plugin.Title, plugin.Version and plugin.Author to identify it; do not assume its hooks are fully wired for other plugins yet. |
Return value
The return value is ignored. Whether you return null or any other value, Oxide continues its normal plugin load path and logs the "Loaded plugin" line as before.
How it works
The hook is invoked from Oxide's own plugin load path (OxideMod.PluginLoaded) after the plugin's error state is checked, IsLoaded is set to true and the plugin is added to the root plugin manager. Because the call sits inside that method's try block, a thrown exception is caught by Oxide and recorded as a plugin error, so keep your handler cheap and safe.
Typical triggering events: server startup and reload (oxide.reload, oxide.load <name>), plus the initial load of every plugin after a restart. The single plugin argument is the Plugin object, so plugin.Name is the useful key for comparisons; there is no way to narrow this hook by parameter type because the argument is always the same Plugin type. If you only care about one specific plugin, compare plugin.Name and ignore everything else.
This is the reliable place to react to another plugin becoming available. It is not a substitute for Loaded on your own plugin, and it does not guarantee that the other plugin has already run its own startup logic (Init, OnServerInitialized).
Example plugin: Report newly loaded plugins to staff
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Plugin Load Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Notifies staff when Oxide plugins are loaded or unloaded and keeps a short in-memory history")]
public class PluginLoadNotifier : RustPlugin
{
private const string PermAlerts = "pluginloadnotifier.alerts";
private PluginConfig config;
private readonly List<string> history = new List<string>();
private class PluginConfig
{
[JsonProperty("Notify admins in chat")]
public bool NotifyAdmins { get; set; } = true;
[JsonProperty("Notify permission holders in chat")]
public bool NotifyPermission { get; set; } = true;
[JsonProperty("Maximum history entries")]
public int MaxHistory { get; set; } = 50;
[JsonProperty("Ignore own plugin events")]
public bool IgnoreSelf { get; set; } = 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>
{
["Loaded"] = "Plugin loaded: {0} v{1} by {2}",
["Unloaded"] = "Plugin unloaded: {0}",
["NoHistory"] = "No plugin load events recorded since startup.",
["HistoryEntry"] = "{0} {1}",
["HistoryHeader"] = "{0} load events recorded:"
}, 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(PermAlerts, this);
}
private void OnPluginLoaded(Plugin plugin)
{
if (plugin == null) return;
if (config.IgnoreSelf && plugin.Name == Name) return;
Record("+ " + plugin.Name);
Broadcast(Msg("Loaded", null, plugin.Name, plugin.Version, plugin.Author));
}
private void OnPluginUnloaded(Plugin plugin)
{
if (plugin == null) return;
if (config.IgnoreSelf && plugin.Name == Name) return;
Record("- " + plugin.Name);
Broadcast(Msg("Unloaded", null, plugin.Name));
}
private void Record(string entry)
{
history.Add(entry);
while (history.Count > Mathf.Max(1, config.MaxHistory))
history.RemoveAt(0);
}
private void Broadcast(string message)
{
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null) continue;
if (config.NotifyAdmins && player.IsAdmin)
{
player.ChatMessage(message);
continue;
}
if (config.NotifyPermission && permission.UserHasPermission(player.UserIDString, PermAlerts))
player.ChatMessage(message);
}
}
[ChatCommand("plstatus")]
private void CmdStatus(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAlerts)) return;
if (history.Count == 0)
{
player.ChatMessage(Msg("NoHistory", player.UserIDString));
return;
}
player.ChatMessage(Msg("HistoryHeader", player.UserIDString, history.Count));
foreach (var entry in history)
player.ChatMessage(Msg("HistoryEntry", player.UserIDString, DateTime.Now.ToString("HH:mm:ss"), entry));
}
}
}The plugin watches Oxide's load events and tells online staff when a plugin appears or disappears. OnPluginLoaded receives the Plugin object, and the handler skips its own name so the notification does not fire for itself during startup. It builds a short message from plugin.Name, plugin.Version and plugin.Author, then sends it to every connected admin (or to everyone with the pluginloadnotifier.alerts permission) through the message table in the config. OnPluginUnloaded covers the opposite case, and /plstatus prints the current load event history captured in memory, which is handy after a reload storm. Nothing is persisted; history is capped and resets on restart.
Common use cases
Notify staff of hot reloads
Send a chat line to admins when a plugin is loaded at runtime, so a live
oxide.reloadduring a wipe does not go unnoticed.Run setup once a dependency is present
Detect that a plugin your server depends on (economy, permissions helper) has loaded and then register the hooks or commands that reference it.
Track load history for auditing
Keep an in-memory list of plugin names and timestamps that loaded since the last restart, printed on demand by a staff command.
Pitfalls
Fires for every plugin, including yours
Compare
plugin.Namebefore acting, otherwise your handler runs once per loaded plugin and can spam chat or duplicate setup work.Load order is not guaranteed
Another plugin may be loaded but not yet past its own Init or OnServerInitialized. Use
[PluginReference]and null checks in the code that actually calls into it rather than assuming it is ready here.Throwing here marks the loaded plugin with an error
The hook fires inside Oxide's try block, so an uncaught exception in your handler is recorded against the plugin being loaded. Wrap risky work in try/catch or keep the handler simple.
Build a plugin with OnPluginLoaded
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 OnPluginLoaded
Generate