Skip to content
RustPluginGenerator

Rust BasePlayer Plugin Guide: Identity, Lookup, Position, Health, Teams and Null Cases

BasePlayer is the class that represents every player and NPC player in a Rust server plugin. Use UserIDString for permissions and data keys, BasePlayer.activePlayerList for online players, and check IsNpc, IsConnected and IsSleeping before acting on a player you did not receive from a connection hook.

5 min readUpdated Oct 26, 2026

BasePlayer is the class behind every human player, sleeper and NPC player in a Rust server, and almost every Oxide hook that involves a person hands you one. The safe pattern is simple: identify players by UserIDString, get online players from BasePlayer.activePlayerList, and null-check or state-check before touching anything you did not just receive from a connection hook.

Identity: userID, UserIDString and displayName

MemberTypeUse it for
UserIDStringstringPermission checks, data file keys, lang lookups
userIDimplicit ulongNumeric comparisons, BasePlayer.FindByID
displayNamestringText shown to people only

Oxide's permission and language APIs take the ID as a string, so UserIDString avoids conversions. When you need a real ulong, use player.userID.Get(). Passing player.userID directly where a string is expected is a common cause of CS1503.

displayName is the Steam name at connection time. It can repeat across players, contain unusual characters, and change between sessions. Never use it as a key.

C#
private void OnPlayerConnected(BasePlayer player)
{
    if (player == null) return;
    var id = player.UserIDString;
    var name = player.displayName;
}

See OnPlayerConnected and OnPlayerDisconnected for the connection hooks.

Online players and sleepers

Two static lists cover most needs:

  • BasePlayer.activePlayerList holds players who are connected and awake in the world.
  • BasePlayer.sleepingPlayerList holds players whose bodies are sleeping, which includes people who have logged out.
C#
foreach (var player in BasePlayer.activePlayerList)
{
    if (player == null || !player.IsConnected) continue;
    // work with an online player
}

Do not add or remove players from the list you are iterating, and avoid kicking inside the loop in ways that change the list. If your loop might trigger a disconnect, copy it first with new List<BasePlayer>(BasePlayer.activePlayerList). Otherwise you risk a collection-was-modified error.

A player who is connecting may not be in activePlayerList yet. Players sleeping in the world are not in it either, so a loop over it never sees offline players.

Finding players by name or ID

CallSearchesNotes
BasePlayer.FindByID(ulong)Awake playersReturns null if not found
BasePlayer.FindAwakeOrSleeping(string)Awake and sleepingAccepts name or ID string
Loop over activePlayerListOnline onlyFull control over matching

FindAwakeOrSleeping is convenient for admin commands where the target may be offline. Its result can be a sleeper, so check state before sending messages or UI.

C#
private BasePlayer FindOnline(string nameOrId)
{
    if (string.IsNullOrEmpty(nameOrId)) return null;
    foreach (var p in BasePlayer.activePlayerList)
    {
        if (p == null) continue;
        if (p.UserIDString == nameOrId) return p;
        if (p.displayName.IndexOf(nameOrId, StringComparison.OrdinalIgnoreCase) >= 0) return p;
    }
    return null;
}

This returns the first partial match. For admin tools that change state, require an exact ID or report ambiguity when several names match.

Position and teleporting

player.transform.position is the body position. player.eyes.position is the camera position and player.eyes.HeadForward() is the look direction, which is what you want for raycasts and "what is the player looking at" logic.

C#
var pos = player.transform.position;
var distance = Vector3.Distance(pos, other.transform.position);

To move a player, call player.Teleport(Vector3). Pick a destination you know is valid; teleporting into terrain or a wall will leave the player stuck. Sample ground height with TerrainMeta.HeightMap.GetHeight(Vector3) when building positions from X and Z coordinates.

Before teleporting, decide how you handle special states:

  • player.IsDead() or player.IsWounded(): usually refuse.
  • player.isMounted or player.GetMounted() != null: the player is seated in a vehicle or on a chair, so decide whether to dismount first or block the action.
  • player.IsSleeping(): a sleeper has no client to confirm the move, so treat it as an admin-only case.

For ready-made behavior, see the recipes for admin teleport commands, home teleport and teleport requests.

Health and vitals

MemberPurpose
healthCurrent health value
MaxHealth()Maximum health
Heal(float)Add health
Hurt(float)Apply damage
IsAlive(), IsDead(), IsWounded()State checks

Check IsDead() before healing; do not try to revive by setting health on a dead player. Players can be wounded (crawling) while still counting as alive, so decide whether your feature should apply to wounded players.

C#
if (player.IsAlive() && !player.IsWounded())
    player.Heal(player.MaxHealth() - player.health);

Hunger, thirst and temperature live in the player's metabolism component. The exact fields vary with game updates, so verify them against the current game build before using them. The respawn health and food recipe shows how to apply vitals after OnPlayerRespawned. If you intercept damage rather than change health directly, read Rust damage handling.

Teams

player.currentTeam is a ulong team ID, with 0 meaning no team. Resolve the team object through the relationship manager:

C#
private bool AreTeammates(BasePlayer a, BasePlayer b)
{
    if (a == null || b == null) return false;
    if (a.currentTeam == 0UL || a.currentTeam != b.currentTeam) return false;
    return true;
}

private List<ulong> GetTeamMembers(BasePlayer player)
{
    if (player == null || player.currentTeam == 0UL) return null;
    var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
    return team?.members;
}

PlayerTeam.members is a list of member IDs, not BasePlayer objects, and teamLeader is also an ID. Convert with BasePlayer.FindByID and null-check each result, since teammates may be offline. Related recipes: team info command, team size limit and team teleport. Browse more under teams and clans plugins.

Admin checks

player.IsAdmin reflects the server-level admin flag (owner or moderator status set through the server config). It is separate from Oxide permissions. For gameplay features, prefer an Oxide permission so owners can delegate access with groups:

C#
private bool CanUse(BasePlayer player) =>
    player != null &&
    (player.IsAdmin || permission.UserHasPermission(player.UserIDString, PermUse));

Allowing IsAdmin as a bypass is a reasonable default for admin tools, but make it deliberate. See Oxide permissions and groups.

Sending messages

  • player.ChatMessage(string) sends a line to one player's chat.
  • SendReply(player, string) does the same through the plugin.
  • PrintToChat(string) and Server.Broadcast(string) send to everyone.
  • player.SendConsoleCommand(cmd, args) runs a client console command, which is how some UI and sound effects are triggered.

Route player-facing text through the lang API so server owners can edit it, as covered in Rust plugin localization. Pass player.UserIDString to the lang lookup so each player's language is respected.

Connection info

A connected human has player.net.connection, which holds the client connection data. Chain the null checks, because net and connection can both be null:

C#
var conn = player.net?.connection;
if (conn == null) return;

Use player.IsConnected for a quick online check. Do not assume net.connection exists for sleepers, NPCs or players in the middle of disconnecting.

Common null and state cases

Most BasePlayer errors come from the same few situations:

  • Disconnected player. Code running later in a timer may hold an ID whose player has left. Re-lookup and check IsConnected. The OnPlayerDisconnected hook is the place to clean up per-player state.
  • Sleeping player. FindByID and FindAwakeOrSleeping can return sleepers. They have no active client, so UI and chat messages to them do nothing useful.
  • NPC players. Scientists and similar NPCs are BasePlayer-derived. They have IsNpc set and no connection. Damage hooks such as OnEntityTakeDamage and OnEntityDeath hand you NPCs as ordinary players when you declare a BasePlayer parameter, so filter explicitly.
  • Missing initiator. HitInfo.InitiatorPlayer can be null for fall damage, fire, animals or traps.
  • Dead players. After death the object remains briefly; check IsDead() before healing or teleporting.

A reusable guard keeps hooks tidy:

C#
private static bool IsRealPlayer(BasePlayer player) =>
    player != null && !player.IsNpc && player.IsConnected && !player.IsSleeping();

If a hook throws because one of these was null, the log shows a NullReferenceException in a hook. Fixing it usually means adding the missing guard rather than catching the exception.

Putting it together

This small plugin ties identity, state checks, teams and messaging into one command that follows the standard plugin layout.

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

namespace Oxide.Plugins
{
    [Info("Player Info", "RustPluginGenerator", "1.0.0")]
    [Description("Shows basic details about the calling player")]
    public class PlayerInfo : RustPlugin
    {
        private const string PermUse = "playerinfo.use";

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Info"] = "ID: {0} | Health: {1} | Team: {2} | Position: {3}"
            }, this);
        }

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

        [ChatCommand("pinfo")]
        private void CmdInfo(BasePlayer player, string command, string[] args)
        {
            if (player == null || player.IsNpc) return;

            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            var pos = player.transform.position;
            player.ChatMessage(Msg("Info", player.UserIDString,
                player.UserIDString,
                Mathf.RoundToInt(player.health),
                player.currentTeam,
                pos.ToString("F0")));
        }
    }
}

For where to go next, read how to make a Rust plugin for the full workflow, Oxide hooks explained for how player arguments reach your methods, and Rust items and inventories for the inventory side of BasePlayer. To skip the boilerplate, the plugin generator can produce a starting file from a description.

FAQ

Why does BasePlayer.FindByID return a sleeping player?

FindByID searches both awake and sleeping players, so a result does not prove the person is online. Check IsConnected when you need a live connection, or IsSleeping() when you only want to exclude sleepers. For online-only lookups, loop BasePlayer.activePlayerList instead.

Should I store BasePlayer references in a dictionary or field?

Store the ID string instead. A BasePlayer object becomes invalid when the player disconnects or dies and respawns, and a stale reference can throw errors or point to a destroyed object. Key your data by UserIDString and look the player up again when you need them.

How do I tell a real player from an NPC in a hook?

Check player.IsNpc and also whether player.net?.connection is null. Scientists and other NPC players are BasePlayer-derived objects that never have a client connection. Many damage and targeting hooks pass these as ordinary players, so check before using chat, UI or permissions.

What is the difference between displayName and a stable identifier?

displayName is the player's current Steam name and can change, duplicate another player's name, or contain odd characters. The Steam ID never changes. Use the ID for permissions, saved data and lookups, and use displayName only for text shown to people.

Does Teleport work on sleeping or disconnected players?

Teleport is intended for a player object that is currently in the world. For a sleeper it moves the sleeping body, but a disconnected player has no live connection to update. Check state first, and keep teleport logic away from dead or wounded players unless you handle those states deliberately.

Skip the C#: describe your plugin

Open generator