Skip to content
RustPluginGenerator

How to make a Rust team size limit plugin (invite blocking)

Caps every Rust team at a configurable member count by returning a non-null value from OnTeamAcceptInvite and OnTeamMemberInvite when the team is already full, with a chat message explaining the limit.

Vanilla Rust teams accept up to a fixed maximum, and the server convar that changes it applies globally, so you cannot vary it per server or add your own wording without touching gameplay code. Returning any non-null object from Oxide's team hooks cancels the action, which is the clean way to block a sixth member while still letting every other team operation run as usual.

This plugin checks the member count of the team being invited into during both the invite step and the accept step. If the team already holds the configured maximum, the invite is refused and the inviter gets a localized message showing the cap. Because accept is checked as well, an invite sent while the team had space can no longer be used after the team filled up. The limit lives in oxide/config/TeamSizeLimit.json and the text is edited in the language file.

The plugin code

TeamSizeLimit.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Team Size Limit", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks team invites and accepts when a team has reached the configured maximum size.")]
    public class TeamSizeLimit : RustPlugin
    {
        private const string PermUse = "teamsizelimit.use";
        private const string PermBypass = "teamsizelimit.bypass";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Maximum team size")]
            public int MaximumTeamSize { get; set; } = 5;

            [JsonProperty("Skip bypassed players when counting")]
            public bool CountBypassedPlayers { 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();
            }
            if (config.MaximumTeamSize < 1) config.MaximumTeamSize = 1;
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Team Full"] = "That team is full. The maximum team size is {0} players.",
                ["Team Full Self"] = "You cannot join this team, it already has the maximum of {0} players.",
                ["Size Info"] = "Maximum team size: {0}. Your team has {1} members.",
                ["No Team"] = "You are not in a team. The maximum team size is {0} players."
            }, this);
        }

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

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

        private int CountMembers(RelationshipManager.PlayerTeam team)
        {
            if (team == null || team.members == null) return 0;

            int count = 0;
            foreach (ulong id in team.members)
            {
                if (!config.CountBypassedPlayers && permission.UserHasPermission(id.ToString(), PermBypass))
                    continue;
                count++;
            }
            return count;
        }

        private object OnTeamMemberInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player, ulong uLong, bool flag)
        {
            if (playerTeam == null || player == null) return null;
            if (playerTeam.members == null) return null;

            if (CountMembers(playerTeam) >= config.MaximumTeamSize)
            {
                player.ChatMessage(Msg("Team Full", player.UserIDString, config.MaximumTeamSize));
                return false;
            }

            return null;
        }

        private object OnTeamAcceptInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player)
        {
            if (playerTeam == null || player == null) return null;
            if (playerTeam.members == null) return null;

            // Player might already be a member of this team.
            if (playerTeam.members.Contains(player.userID.Get())) return null;

            if (CountMembers(playerTeam) >= config.MaximumTeamSize)
            {
                player.ChatMessage(Msg("Team Full Self", player.UserIDString, config.MaximumTeamSize));
                return false;
            }

            return null;
        }

        [ChatCommand("teamsize")]
        private void CmdTeamSize(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                player.ChatMessage(Msg("No Permission", player.UserIDString));
                return;
            }

            ulong teamId = player.currentTeam;
            if (teamId == 0)
            {
                player.ChatMessage(Msg("No Team", player.UserIDString, config.MaximumTeamSize));
                return;
            }

            RelationshipManager.PlayerTeam team = RelationshipManager.ServerInstance.FindTeam(teamId);
            int members = CountMembers(team);
            player.ChatMessage(Msg("Size Info", player.UserIDString, config.MaximumTeamSize, members));
        }
    }
}

Hooks used: OnTeamMemberInvite, OnTeamAcceptInvite

Config file

Created automatically on first load at oxide/config/TeamSizeLimit.json.

TeamSizeLimit.json
{
  "Maximum team size": 5,
  "Skip bypassed players when counting": true
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/teamsizeShows the current maximum team size and how many players are in your team (if any).teamsizelimit.use
Permissions
PermissionGrants
teamsizelimit.useAllows the player to run /teamsize.
teamsizelimit.bypassMembers with this permission are not counted toward the limit when they are added to a team.

How the code works

  1. Read the limit from config

    The nested PluginConfig class holds MaximumTeamSize (default 5) and CountBypassedPlayers (default true). LoadDefaultConfig fills it with defaults and LoadConfig catches broken JSON so a malformed file never blocks startup. Both team hooks read config.MaximumTeamSize, so changing the cap only requires saving the config and reloading the plugin.

  2. Block the invite with OnTeamMemberInvite

    OnTeamMemberInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player, ulong uLong, bool flag) fires when player invites the user id uLong. If playerTeam.members.Count is already at or above the cap, the plugin sends the localized message to player and returns false. Any non-null return value cancels the original action; returning null lets the invite through unchanged.

  3. Block the accept with OnTeamAcceptInvite

    OnTeamAcceptInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player) fires when player accepts an outstanding invite to playerTeam. This second check closes the gap where an invite was created before the team filled: the same count comparison runs, the accepting player gets the message, and a non-null return stops the join. Returning null keeps vanilla behavior.

  4. Counting members with bypass

    CountMembers walks playerTeam.members and, when CountBypassedPlayers is false, skips user ids that have teamsizelimit.bypass. The count is what both hooks compare against the cap. Because it is computed per call, promoted team leaders and members leaving are always reflected immediately; nothing is cached.

  5. Localized messages

    LoadDefaultMessages registers three keys: Team Full for the inviter, Team Full Self for the accepting player and Size Info for the /teamsize reply. Both messages use {0} for the configured cap so admins can change MaximumTeamSize without editing text. The Msg helper wraps lang.GetMessage with the player's UserIDString.

  6. The /teamsize command

    [ChatCommand("teamsize")] CmdTeamSize requires teamsizelimit.use, then reads player.currentTeam. A team id of 0 replies with No Team; otherwise RelationshipManager.ServerInstance.FindTeam returns the PlayerTeam and the reply shows members.Count against the cap. The command only reads data, so it is safe to call at any time.

Install it on your server

  1. Save TeamSizeLimit.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/TeamSizeLimit.json (Carbon: carbon/configs/TeamSizeLimit.json). After editing, reload it:oxide.reload TeamSizeLimit c.reload TeamSizeLimit # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip:oxide.grant group default teamsizelimit.use c.grant group default teamsizelimit.use # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Per-player team size

    Lets donors own larger teams than regular players by reading the limit from permissions.

    Generate this version
  • Warn instead of block

    Allows oversized teams but broadcasts a notice when a team passes the cap.

    Generate this version
  • Kick on leave-free slot

    Blocks leaving a full team during a cooldown so members cannot cycle invites.

    Generate this version

FAQ

Does the plugin stop players from being added when the leader invites several people quickly?

Each invite is checked against the live member count, and the accept check runs again when the invited player accepts. Two players accepting at nearly the same time can both pass if the count was below the cap for both hook calls, so treat the limit as best-effort rather than a hard lock under simultaneous accepts.

How do I install it on Oxide or Carbon?

Drop TeamSizeLimit.cs into oxide/plugins, run oxide.reload TeamSizeLimit from the server console, then edit oxide/config/TeamSizeLimit.json and oxide/lang/en/TeamSizeLimit.json. Carbon loads the same file from its plugins directory and supports the same hooks.

What happens to teams that are already over the limit?

Nothing. The hooks only block new invites and accepts, so an existing team above the cap keeps its members. Existing members can still leave freely, and no one new can join until the count drops below MaximumTeamSize.

Why is `CountBypassedPlayers` useful?

Staff or donors sometimes need to join teams that are otherwise full. Setting CountBypassedPlayers to false means players with teamsizelimit.bypass are not counted by CountMembers, so the cap effectively rises for every team that contains them.

Can this conflict with a clan plugin?

Clan plugins usually manage their own data and do not route through Rust's built-in team hooks. If a clan plugin also writes to RelationshipManager teams, both plugins see the same hook calls, and the first one to return non-null cancels the action, so test them together before going live.

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 called TeamSizeLimit that caps teams at 5 members using OnTeamMemberInvite and OnTeamAcceptInvite, with a config file for the limit and bypass 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 team size limit

Customize