How to make a Rust online players list plugin (sorted and paged)
Adds a /players chat command that prints an alphabetically sorted, paginated list of online players with online, sleeping and joining counts; admins also see SteamIDs. It uses no Rust gameplay hooks and only reads player state when the command runs.
A vanilla Rust server gives admins no single chat-friendly view of who is connected right now. The server browser is outside the game and only shows a count, and the F1 player list is client-side. This plugin adds a /players chat command that builds the list on demand from BasePlayer.activePlayerList, sorts it by display name, and prints one page at a time with a configurable page size.
The output ends with three counts: online players, sleeping players and players currently in the connection queue. SteamIDs are shown only to players who hold the onlineplayerslist.steamid permission or are server admins, so regular players can see who is online without leaking account IDs. /players 2 jumps straight to a page. Everything is regenerated each time the command runs, so there is no stored state to keep in sync and nothing to clean up on unload.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Online Players List", "RustPluginGenerator", "1.0.0")]
[Description("Adds a /players chat command that lists online players with online, sleeping and joining counts.")]
public class OnlinePlayersList : RustPlugin
{
private const string PermUse = "onlineplayerslist.use";
private const string PermSteamId = "onlineplayerslist.steamid";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Page size")]
public int PageSize { get; set; } = 10;
[JsonProperty("Show steam ids to admins")]
public bool ShowSteamIdToAdmins { get; set; } = true;
[JsonProperty("Message color")]
public string MessageColor { get; set; } = "#cec8b6";
[JsonProperty("Highlight color")]
public string HighlightColor { get; set; } = "#8fbf6b";
}
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>
{
["ListHeader"] = "Online players (page {0} of {1}):",
["Entry"] = " {0}. {1}",
["EntryWithId"] = " {0}. {1} ({2})",
["Counts"] = "Online: {0} | Sleeping: {1} | Joining: {2}",
["NoPlayers"] = "No players are currently online.",
["NoPermission"] = "You do not have permission to use this command.",
["InvalidPage"] = "Page {0} does not exist, showing page {1}."
}, this);
}
private void Init()
{
permission.RegisterPermission(PermUse, this);
permission.RegisterPermission(PermSteamId, this);
}
private string Msg(string key, string userId = null, params object[] args)
{
return string.Format(lang.GetMessage(key, this, userId ?? "0"), args);
}
[ChatCommand("players")]
private void CmdPlayers(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
SendReply(player, Msg("NoPermission", player.UserIDString));
return;
}
var entries = new List<PlayerEntry>();
foreach (var target in BasePlayer.activePlayerList)
{
if (target == null || !target.IsConnected || target.IsNpc) continue;
entries.Add(new PlayerEntry(target.displayName, target.UserIDString));
}
int pageSize = Mathf.Max(1, config.PageSize);
int totalPages = Mathf.Max(1, (entries.Count + pageSize - 1) / pageSize);
int page = 1;
if (args != null && args.Length > 0)
{
if (!int.TryParse(args[0], out page) || page < 1) page = 1;
if (page > totalPages)
{
SendReply(player, Msg("InvalidPage", player.UserIDString, page, totalPages));
page = totalPages;
}
}
if (entries.Count == 0)
{
SendReply(player, Msg("NoPlayers", player.UserIDString));
}
else
{
entries.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
bool showIds = permission.UserHasPermission(player.UserIDString, PermSteamId)
|| (config.ShowSteamIdToAdmins && player.IsAdmin);
SendReply(player, Msg("ListHeader", player.UserIDString, page, totalPages));
int start = (page - 1) * pageSize;
int count = Mathf.Min(pageSize, entries.Count - start);
for (int i = 0; i < count; i++)
{
var entry = entries[start + i];
int number = start + i + 1;
if (showIds)
SendReply(player, Msg("EntryWithId", player.UserIDString, number, entry.Name, entry.Id));
else
SendReply(player, Msg("Entry", player.UserIDString, number, entry.Name));
}
}
int online = BasePlayer.activePlayerList.Count;
int sleeping = 0;
foreach (var sleeper in BasePlayer.sleepingPlayerList)
{
if (sleeper != null && sleeper.IsSleeping()) sleeping++;
}
int joining = 0;
var serverMgr = SingletonComponent<ServerMgr>.Instance;
if (serverMgr != null && serverMgr.connectionQueue != null)
joining = serverMgr.connectionQueue.Queued;
SendReply(player, Msg("Counts", player.UserIDString, online, sleeping, joining));
}
private struct PlayerEntry
{
public readonly string Name;
public readonly string Id;
public PlayerEntry(string name, string id)
{
Name = name;
Id = id;
}
}
}
}Config file
Created automatically on first load at oxide/config/OnlinePlayersList.json.
{
"Page size": 10,
"Show steam ids to admins": true,
"Message color": "#cec8b6",
"Highlight color": "#8fbf6b"
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/players [page] | Lists online players sorted by name, one page at a time, with online, sleeping and joining counts. | onlineplayerslist.use |
| Permission | Grants |
|---|---|
onlineplayerslist.use | Grants access to the /players chat command. |
onlineplayerslist.steamid | Shows SteamIDs next to player names in the list (server admins always see them). |
How the code works
Collect and sort the connected players
The command iterates
BasePlayer.activePlayerList, skips anything that is not connected or is an NPC, and builds aList<PlayerEntry>of name and ID pairs. It sorts withList.Sortusingstring.CompareondisplayNamewithStringComparison.OrdinalIgnoreCase, soBobandbobland next to each other. No Rust gameplay hook is needed:activePlayerListis always current, so the list is built from scratch on every call and never goes stale.Paginate with a configurable page size
Page sizein the config controls how many entries fit per page and is clamped withMathf.Max(1, ...). The optional command argument is parsed withint.TryParse; anything missing or out of range falls back to page 1, and a page beyond the end printsInvalidPageand snaps to the last page. The visible slice is taken with an index loop overstart = (page - 1) * pageSize, andListHeadershows the current page and total page count.Count online, sleeping and joining
Online is simply the number of entries in
BasePlayer.activePlayerList. Sleeping comes from iteratingBasePlayer.sleepingPlayerListand counting entries whereIsSleeping()returns true. Joining is read fromSingletonComponent<ServerMgr>.Instance.connectionQueue.Queuedwith a null check on both. These three numbers are sent through theCountslang message after the page, so they are visible on any page, including when no players are online.Gate SteamIDs by permission or admin flag
Before formatting the entries the code sets
showIdsfrompermission.UserHasPermission(player.UserIDString, PermSteamId), or fromplayer.IsAdminwhenShow steam ids to adminsis enabled. Lines are then formatted withEntryWithIdorEntry. Because the check runs per command call, revokingonlineplayerslist.steamidtakes effect immediately with no reconnect.Register the command and permissions in Init
The class registers
onlineplayerslist.useandonlineplayerslist.steamidinInit. The chat command itself is declared with[ChatCommand("players")], so Oxide wires it up automatically. Inside the method the first check ispermission.UserHasPermission(player.UserIDString, PermUse); a failed check sends theNoPermissionlang message and returns. This keeps the list and counts hidden from players who should not see them.Keep output in the language file and out of global chat
All text lives in
LoadDefaultMessages:ListHeader,Entry,EntryWithId,Counts,NoPlayers,NoPermissionandInvalidPage. TheMsghelper callslang.GetMessage(key, this, userId)and formats placeholders withstring.Format. Each line is sent withSendReply, which writes to that player's chat only, so a page of twenty entries never spams the global chat.
Install it on your server
Save OnlinePlayersList.cs
Keep the file name exactly as the class name. Oxide matches them.Upload it to the plugins folder
Oxide:oxide/plugins/. Carbon:carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.Edit the config if you want
On first load the default config is written tooxide/config/OnlinePlayersList.json(Carbon:carbon/configs/OnlinePlayersList.json). After editing, reload it:oxide.reload OnlinePlayersList c.reload OnlinePlayersList # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default onlineplayerslist.use c.grant group default onlineplayerslist.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Team grouping
Group the list by each player's Rust team so admins can see teams at a glance.
Generate this versionConsole version
Add a console command so RCON can query the list without a player.
Generate this versionGUI overlay
Render the list in a CUI panel instead of chat lines.
Generate this version
FAQ
Why do some players appear as sleeping instead of online?
On a persistent server activePlayerList holds spawned, connected players. Players who logged out but whose sleeper body still exists live in BasePlayer.sleepingPlayerList; the plugin counts them separately so the online number matches who is actually awake.
What does the joining count mean?
It is the length of the connection queue, meaning clients that finished authentication but have not spawned into the world yet. A high number usually means a slow server or a lot of people reconnecting at once.
How do players get to see SteamIDs?
Visibility is controlled by admin. Only players holding onlineplayerslist.steamid are shown IDs, plus admins while the Show steam ids to admins config key is true. To let everybody see IDs, grant it to the default group with oxide.grant group default onlineplayerslist.steamid.
Does this plugin work on Carbon?
Yes. Copy OnlinePlayersList.cs into oxide/plugins for Oxide, or the Carbon plugins folder for Carbon; both load the same file. The command and permission names stay identical.
What happens if a name contains color codes?
The plugin does not strip or sanitize names; displayName is printed as-is. If your server allows name colors, the codes carry into the chat line. Add a strip step around displayName if you want plain text.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More utility plugins
- Command aliasesThis plugin maps short chat commands like /h and /k to longer ones such as /home and /kit, defined entirely in its config file, using theโฆOnServerInitializedOnPlayerCommandOnPlayerDisconnected
- Info commandsA config-driven text command plugin: every entry in the config defines a command name and a list of chat lines, and the plugin registers them allโฆ
- Map grid locationA /grid chat command that converts a player's world position into a Rust map grid cell (like G12) using the server's world size, with exactโฆOnServerInitializedOnPlayerConnected
Last updated Oct 26, 2026.
Customize online players list
Customize