OnUserConnected
OnUserConnected is called by Oxide after a player's connection is fully handled and the player's IPlayer wrapper has been attached, before OnPlayerConnected runs.
void OnUserConnected(IPlayer iplayer)Parameters
| Parameter | Type | Description |
|---|---|---|
iplayer | IPlayer | The Oxide IPlayer wrapper for the connecting player, registered with Covalence by the time this hook fires. Use iplayer.Id for the Steam ID string and iplayer.Name for the name; cast to BasePlayer via iplayer.Object to reach game members like inventory or transform. |
Return value
The return value is ignored. Any value you return has no effect on the connection or on the game's behavior.
How it works
This hook is invoked from Oxide's RustCore.IOnPlayerConnected, which Oxide calls after the language is set, the CUI entity snapshot is sent and Covalence has registered the player. The iplayer argument is the Covalence IPlayer; it is non-null (Oxide only calls the hook when FindPlayerById succeeded), so you can rely on iplayer.Id, iplayer.Name and iplayer.Object. Because the hook is raised by Oxide rather than a patched game method, it fires once per accepted connection, after the network session exists. This is a good place for welcome messages, permission checks or per-player bookkeeping that needs the player object rather than just a name and ID. Do not do heavy work here: the client has just finished connecting and the server is still settling that connection. The hook always uses the IPlayer type, so there is no more specific parameter type to narrow to. If you need the BasePlayer directly and want to run after Oxide's own connection handling, OnPlayerConnected is the better choice.
Example plugin: Send a configurable welcome message with server rules
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Welcome Message", "RustPluginGenerator", "1.0.0")]
[Description("Sends a configurable welcome message to players after they connect.")]
public class WelcomeMessage : RustPlugin
{
private const string PermBypass = "welcomemessage.bypass";
private PluginConfig config;
private readonly Dictionary<string, Timer> pendingTimers = new Dictionary<string, Timer>();
private class PluginConfig
{
[JsonProperty("Delay in seconds")]
public float Delay = 3f;
[JsonProperty("Send rules to console")]
public bool SendRulesToConsole = 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, {0}!",
["Rules"] = "Rules: no cheating, no griefing spawn areas, respect staff. Type /help for commands.",
["Discord"] = "Join our Discord: discord.gg/example"
}, this);
}
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private void OnUserConnected(IPlayer iplayer)
{
if (iplayer == null || string.IsNullOrEmpty(iplayer.Id)) return;
if (permission.UserHasPermission(iplayer.Id, PermBypass)) return;
var player = iplayer.Object as BasePlayer;
if (player == null) return;
string userId = player.UserIDString;
if (pendingTimers.ContainsKey(userId)) return;
pendingTimers[userId] = timer.Once(config.Delay <= 0f ? 0.1f : config.Delay, () =>
{
pendingTimers.Remove(userId);
if (player == null || !player.IsConnected) return;
player.ChatMessage(Msg("Welcome", userId, player.displayName));
if (config.SendRulesToConsole)
{
player.SendConsoleCommand("echo " + Msg("Rules", userId));
player.SendConsoleCommand("echo " + Msg("Discord", userId));
}
});
}
private void Unload()
{
foreach (var entry in pendingTimers)
{
entry.Value?.Destroy();
}
pendingTimers.Clear();
}
}
}OnUserConnected takes the iplayer wrapper and pulls the BasePlayer out of iplayer.Object. A permission check decides whether the player gets the full welcome block; staff with welcomemessage.bypass are skipped entirely. The configurable delay (default 3 seconds) lets the client finish loading before the text arrives, so it is not lost in the loading screen. The chat line and an optional console rules block are printed through the language file, with the player's display name filled into a {0} placeholder. Timers are stored per Steam ID so a fast reconnect does not stack messages, and Unload destroys any timers that are still pending.
Common use cases
Welcome text with server information
Print a short greeting plus a console block of Discord and rules links after a configurable delay, so the text lands once the client has finished loading.
Track first-time joins
Compare the connecting Steam ID against a list stored in oxide/data and only run newcomer logic, such as a starter kit prompt, for players seen for the first time.
Tag or clear stale per-player state
Reset cached data for that Steam ID (open UI, temporary flags) on connect so nothing leaks between sessions.
Pitfalls
The argument is IPlayer, not BasePlayer
Declaring
BasePlayerin the signature means Oxide never calls your method. KeepIPlayerand useiplayer.Objectwhen you need game-side members.Fires before OnPlayerConnected
Oxide raises this from inside IOnPlayerConnected just before the OnPlayerConnected call. If your logic assumes that hook has already run, move it there instead.
Do not block on heavy work
The player has only just finished connecting. Avoid expensive inventory scans or large data reads here; defer them with timer.Once or NextTick.
Build a plugin with OnUserConnected
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 OnUserConnected
Generate