Skip to content
RustPluginGenerator

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

OnlinePlayersList.cs
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.

OnlinePlayersList.json
{
  "Page size": 10,
  "Show steam ids to admins": true,
  "Message color": "#cec8b6",
  "Highlight color": "#8fbf6b"
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/players [page]Lists online players sorted by name, one page at a time, with online, sleeping and joining counts.onlineplayerslist.use
Permissions
PermissionGrants
onlineplayerslist.useGrants access to the /players chat command.
onlineplayerslist.steamidShows SteamIDs next to player names in the list (server admins always see them).

How the code works

  1. Collect and sort the connected players

    The command iterates BasePlayer.activePlayerList, skips anything that is not connected or is an NPC, and builds a List<PlayerEntry> of name and ID pairs. It sorts with List.Sort using string.Compare on displayName with StringComparison.OrdinalIgnoreCase, so Bob and bob land next to each other. No Rust gameplay hook is needed: activePlayerList is always current, so the list is built from scratch on every call and never goes stale.

  2. Paginate with a configurable page size

    Page size in the config controls how many entries fit per page and is clamped with Mathf.Max(1, ...). The optional command argument is parsed with int.TryParse; anything missing or out of range falls back to page 1, and a page beyond the end prints InvalidPage and snaps to the last page. The visible slice is taken with an index loop over start = (page - 1) * pageSize, and ListHeader shows the current page and total page count.

  3. Count online, sleeping and joining

    Online is simply the number of entries in BasePlayer.activePlayerList. Sleeping comes from iterating BasePlayer.sleepingPlayerList and counting entries where IsSleeping() returns true. Joining is read from SingletonComponent<ServerMgr>.Instance.connectionQueue.Queued with a null check on both. These three numbers are sent through the Counts lang message after the page, so they are visible on any page, including when no players are online.

  4. Gate SteamIDs by permission or admin flag

    Before formatting the entries the code sets showIds from permission.UserHasPermission(player.UserIDString, PermSteamId), or from player.IsAdmin when Show steam ids to admins is enabled. Lines are then formatted with EntryWithId or Entry. Because the check runs per command call, revoking onlineplayerslist.steamid takes effect immediately with no reconnect.

  5. Register the command and permissions in Init

    The class registers onlineplayerslist.use and onlineplayerslist.steamid in Init. The chat command itself is declared with [ChatCommand("players")], so Oxide wires it up automatically. Inside the method the first check is permission.UserHasPermission(player.UserIDString, PermUse); a failed check sends the NoPermission lang message and returns. This keeps the list and counts hidden from players who should not see them.

  6. Keep output in the language file and out of global chat

    All text lives in LoadDefaultMessages: ListHeader, Entry, EntryWithId, Counts, NoPlayers, NoPermission and InvalidPage. The Msg helper calls lang.GetMessage(key, this, userId) and formats placeholders with string.Format. Each line is sent with SendReply, which writes to that player's chat only, so a page of twenty entries never spams the global chat.

Install it on your server

  1. Save OnlinePlayersList.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. 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.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/OnlinePlayersList.json (Carbon: carbon/configs/OnlinePlayersList.json). After editing, reload it:oxide.reload OnlinePlayersList c.reload OnlinePlayersList # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip: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.

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.

Prompt: Create a Rust Oxide plugin with a /players chat command that lists online players alphabetically, paginated with a configurable page size, showing online, sleeping and joining counts and SteamIDs for admins and players with the onlineplayerslist.steamid permission.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize online players list

Customize