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
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.
{
"Maximum team size": 5,
"Skip bypassed players when counting": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/teamsize | Shows the current maximum team size and how many players are in your team (if any). | teamsizelimit.use |
| Permission | Grants |
|---|---|
teamsizelimit.use | Allows the player to run /teamsize. |
teamsizelimit.bypass | Members with this permission are not counted toward the limit when they are added to a team. |
How the code works
Read the limit from config
The nested
PluginConfigclass holdsMaximumTeamSize(default 5) andCountBypassedPlayers(default true).LoadDefaultConfigfills it with defaults andLoadConfigcatches broken JSON so a malformed file never blocks startup. Both team hooks readconfig.MaximumTeamSize, so changing the cap only requires saving the config and reloading the plugin.Block the invite with OnTeamMemberInvite
OnTeamMemberInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player, ulong uLong, bool flag)fires whenplayerinvites the user iduLong. IfplayerTeam.members.Countis already at or above the cap, the plugin sends the localized message toplayerand returnsfalse. Any non-null return value cancels the original action; returningnulllets the invite through unchanged.Block the accept with OnTeamAcceptInvite
OnTeamAcceptInvite(RelationshipManager.PlayerTeam playerTeam, BasePlayer player)fires whenplayeraccepts an outstanding invite toplayerTeam. 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. Returningnullkeeps vanilla behavior.Counting members with bypass
CountMemberswalksplayerTeam.membersand, whenCountBypassedPlayersis false, skips user ids that haveteamsizelimit.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.Localized messages
LoadDefaultMessagesregisters three keys:Team Fullfor the inviter,Team Full Selffor the accepting player andSize Infofor the/teamsizereply. Both messages use{0}for the configured cap so admins can changeMaximumTeamSizewithout editing text. TheMsghelper wrapslang.GetMessagewith the player'sUserIDString.The /teamsize command
[ChatCommand("teamsize")] CmdTeamSizerequiresteamsizelimit.use, then readsplayer.currentTeam. A team id of 0 replies withNo Team; otherwiseRelationshipManager.ServerInstance.FindTeamreturns thePlayerTeamand the reply showsmembers.Countagainst the cap. The command only reads data, so it is safe to call at any time.
Install it on your server
Save TeamSizeLimit.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/TeamSizeLimit.json(Carbon:carbon/configs/TeamSizeLimit.json). After editing, reload it:oxide.reload TeamSizeLimit c.reload TeamSizeLimit # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip: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 versionWarn instead of block
Allows oversized teams but broadcasts a notice when a team passes the cap.
Generate this versionKick 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.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More teams & clans plugins
- Simple clansAdds a lightweight clan system with /clan create, invite, join, leave and info, stores clans in a data file, prefixes members' chat with their tagโฆOnServerInitializedOnPlayerChatOnEntityTakeDamage
- Team auto authorizationAutomatically authorizes a player on every tool cupboard, code lock and auto turret owned by their team when they join a team, and removes thatโฆOnTeamAcceptInviteOnTeamLeaveOnTeamKick
- Team death alertsSends a chat alert to every online teammate when a player dies, including the killer name and the map grid of the death.OnPlayerDeath
Last updated Oct 26, 2026.
Customize team size limit
Customize