Skip to content

These docs were made completely by AI, so they might be right, or wrong, you'll need to test them yourself. This was made for a easier understanding of everything. So use at your own risk. If anything is wrong, please don't hurt to make a PR on the page you have a problem with. ON GITHUB

Player Limit

Legacy Console Edition supports up to 8 players in a multiplayer game. On PS Vita, it’s 4. This limit is baked in deep across every layer of the codebase. Here’s the full picture of where it shows up, what it controls, and what happens when you try to change it.

The max player count is defined per-platform in the network headers. The main definition lives in extraX64.h (for the x64 build) and extra.h (for Xbox 360):

Minecraft.World/x64headers/extraX64.h
#ifdef __PSVITA__
const int MINECRAFT_NET_MAX_PLAYERS = 4;
#else
const int MINECRAFT_NET_MAX_PLAYERS = 8;
#endif
// Xbox/Network/extra.h (Xbox 360 build)
const int MINECRAFT_NET_MAX_PLAYERS = 8;

PS Vita gets 4 because it only supports 1 local user (XUSER_MAX_COUNT = 1) and the hardware can’t handle more network traffic. Every other platform gets 8.

This constant appears in over 70 locations across both Minecraft.Client and Minecraft.World. It’s used for array sizes, loop bounds, validation checks, network packet fields, session advertising, QNet small ID allocation, UI screen layouts, and voice chat buffers. It is not a config value you can just change in one place.

When a player tries to join, PlayerList handles the connection. The max player count gets clamped right in the constructor:

// PlayerList.cpp - constructor (MinecraftConsoles build)
int rawMax = server->settings->getInt(L"max-players", 8);
maxPlayers = static_cast<unsigned int>(
Mth::clamp(rawMax, 1, MINECRAFT_NET_MAX_PLAYERS)
);

In the LCEMP build, it’s even simpler. The settings check is gone:

// PlayerList.cpp - constructor (LCEMP build)
maxPlayers = MINECRAFT_NET_MAX_PLAYERS;

Even if you set max-players to 16 in the server settings, it gets capped at 8.

When someone connects, they get a player index from 0 to MINECRAFT_NET_MAX_PLAYERS - 1. The system uses a fixed-size boolean array to find the first free slot:

// PlayerList.cpp - placeNewPlayer()
DWORD playerIndex = (DWORD)MINECRAFT_NET_MAX_PLAYERS;
{
bool usedIndexes[MINECRAFT_NET_MAX_PLAYERS];
ZeroMemory(&usedIndexes, MINECRAFT_NET_MAX_PLAYERS * sizeof(bool));
for (auto& p : players)
{
usedIndexes[p->getPlayerIndex()] = true;
}
for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
{
if (!usedIndexes[i])
{
playerIndex = i;
break;
}
}
}
if (playerIndex >= static_cast<unsigned int>(MINECRAFT_NET_MAX_PLAYERS))
{
connection->send(
std::make_shared<DisconnectPacket>(DisconnectPacket::eDisconnect_ServerFull)
);
connection->sendAndQuit();
}

If all slots are taken, the player gets a “server full” disconnect. There’s also an early check before the index assignment even runs:

// PlayerList.cpp - getPlayerForLogin()
if (players.size() >= (unsigned int)MINECRAFT_NET_MAX_PLAYERS)
{
pendingConnection->disconnect(DisconnectPacket::eDisconnect_ServerFull);
return shared_ptr<ServerPlayer>();
}

The login packet sent back to the joining player includes the max player count. But it’s encoded as a single byte:

// PlayerList.cpp - placeNewPlayer()
int maxPlayersForPacket = getMaxPlayers() > 255 ? 255 : getMaxPlayers();
// ...
static_cast<byte>(maxPlayersForPacket),

So the wire format technically caps at 255, but MINECRAFT_NET_MAX_PLAYERS caps you at 8 way before that matters.

This is the part that makes increasing the limit painful. The constant sizes arrays all over the codebase. Here’s the complete list.

Consoles_App.h
BYTE m_playerColours[MINECRAFT_NET_MAX_PLAYERS];
unsigned int m_playerGamePrivileges[MINECRAFT_NET_MAX_PLAYERS];

m_playerColours maps QNet small IDs to player color indexes. m_playerGamePrivileges stores per-player permission flags. Both are indexed by player index. Overflow here means you’re writing past the end of these arrays into whatever memory comes after them in the Consoles_App object.

The GameSessionData struct (used for session advertising and discovery) has per-player arrays on every platform:

// SessionInfo.h - Xbox build
GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS]; // 64 bytes (8*8) on Xbox
char szPlayers[MINECRAFT_NET_MAX_PLAYERS][XUSER_NAME_SIZE]; // 128 bytes (8*16)
// SessionInfo.h - PS3/PS4/Vita build
GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS]; // 192 bytes (24*8) on PS3
// SessionInfo.h - x64/LAN build
GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS];
char szPlayers[MINECRAFT_NET_MAX_PLAYERS][XUSER_NAME_SIZE];
unsigned char maxPlayers; // also stored as a byte here

The x64 LAN build also stores maxPlayers as a unsigned char inside the struct. This gets set to MINECRAFT_NET_MAX_PLAYERS in the constructor. It’s broadcast over the network during LAN discovery, so other clients use it to decide if the game is full.

Multiple UI screens have their own per-player arrays:

UIScene_TeleportMenu.h
BYTE m_players[MINECRAFT_NET_MAX_PLAYERS];
char m_playersVoiceState[MINECRAFT_NET_MAX_PLAYERS];
short m_playersColourState[MINECRAFT_NET_MAX_PLAYERS];
wstring m_playerNames[MINECRAFT_NET_MAX_PLAYERS];
// UIScene_InGameInfoMenu.h (LCEMP build)
BYTE m_players[MINECRAFT_NET_MAX_PLAYERS];
char m_playersVoiceState[MINECRAFT_NET_MAX_PLAYERS];
short m_playersColourState[MINECRAFT_NET_MAX_PLAYERS];
wstring m_playerNames[MINECRAFT_NET_MAX_PLAYERS];
// XUI_InGameInfo.h (Xbox XUI build)
BYTE m_players[MINECRAFT_NET_MAX_PLAYERS];
// XUI_Teleport.h (Xbox XUI build)
BYTE m_players[MINECRAFT_NET_MAX_PLAYERS];

These arrays store the player list as QNet small IDs along with their voice state, nametag color, and display name. The teleport menu uses them to show the list of players you can teleport to. The in-game info menu shows the online player list overlay.

PlayerRenderer.h
static unsigned int s_nametagColors[MINECRAFT_NET_MAX_PLAYERS];

This array stores the ARGB color for each player’s nametag. The first 8 are hardcoded to specific colors (black, green, red, blue, magenta, orange, yellow, cyan). Indexes 8 and above use procedurally generated colors via HSV:

PlayerRenderer.cpp
for (int i = 8; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
float hue = fmodf(i * 137.508f, 360.0f);
float sat = 0.65f + (float)(i % 3) * 0.15f;
float val = 0.75f + (float)(i % 4) * 0.08f;
s_nametagColors[i] = HsvToArgb(hue, sat, val);
}

The color lookup does a bounds check:

if (index >= 0 && index < MINECRAFT_NET_MAX_PLAYERS)
return s_nametagColors[index];
return 0xFF000000; // fallback to black

If you increase the constant but forget this array, any player with an index past the old size reads garbage memory for their nametag color.

The QNet abstraction layer (used on x64 builds) allocates a static array of player objects:

Extrax64Stubs.cpp
IQNetPlayer IQNet::m_player[MINECRAFT_NET_MAX_PLAYERS];

This is the core player tracking array for the network layer. Every QNet function that looks up a player by index or small ID checks against MINECRAFT_NET_MAX_PLAYERS:

if (dwUserIndex >= MINECRAFT_NET_MAX_PLAYERS) return E_FAIL;
if (SmallId >= MINECRAFT_NET_MAX_PLAYERS) return NULL;

When a player is joining and the server needs to check UGC (user-generated content) permissions, it allocates a temporary array:

PendingConnection.cpp
PlayerUID *ugcXuids = new PlayerUID[MINECRAFT_NET_MAX_PLAYERS];

This is heap-allocated so it won’t overflow the same way, but it still only has room for MINECRAFT_NET_MAX_PLAYERS entries.

The save system uses the constant when migrating old player data files:

DirectoryLevelStorage.cpp
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
PlayerUID oldXuid = WIN64_XUID_BASE + i;
tag = loadPlayerDataTag(oldXuid);
// ...
}

This loop tries old-style XUIDs (sequential from a base value) to find existing player data that needs migration. If you increase the constant, this loop runs more iterations but that’s harmless. It just takes slightly longer.

Here’s every category of usage and the count:

CategoryCountRisk if you miss it
Fixed-size arrays (stack/static)18Memory corruption, crashes
Loop bounds22Wrong iteration count, missed players
Bounds checks / validation12Out-of-bounds access, security
Function default parameters14Wrong slot counts passed to platform APIs
Assignments / comparisons6Wrong limits enforced

Total: ~72 locations across both Minecraft.Client and Minecraft.World.

On the x64 LAN build, the server assigns “small IDs” (0 through MINECRAFT_NET_MAX_PLAYERS - 1) to each connecting client. This happens in the Winsock network layer:

WinsockNetLayer.cpp
BYTE assignedSmallId;
EnterCriticalSection(&s_freeSmallIdLock);
if (!s_freeSmallIds.empty())
{
assignedSmallId = s_freeSmallIds.back();
s_freeSmallIds.pop_back();
}
else if (s_nextSmallId < MINECRAFT_NET_MAX_PLAYERS)
{
assignedSmallId = s_nextSmallId++;
}
else
{
LeaveCriticalSection(&s_freeSmallIdLock);
app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
closesocket(clientSocket);
continue;
}
LeaveCriticalSection(&s_freeSmallIdLock);

The small ID is sent to the client as a single byte. When a player disconnects, their small ID goes back into the s_freeSmallIds pool for reuse. The key constraint: small IDs are used as indexes into IQNet::m_player[], which is sized to MINECRAFT_NET_MAX_PLAYERS. If the small ID is >= the array size, everything downstream breaks.

The LAN broadcast also advertises the max players:

WinsockNetLayer.cpp
s_advertiseData.maxPlayers = MINECRAFT_NET_MAX_PLAYERS;

Clients see this in the server browser and use it to show “X/8 players” in the game list.

The game’s player limit isn’t the only one. Each platform’s network API has its own session size constraints.

The HostGame function passes MINECRAFT_NET_MAX_PLAYERS as the publicSlots parameter to the platform session API:

PlatformNetworkManagerXbox.h
virtual void HostGame(
int localUsersMask,
bool bOnlineGame,
bool bIsPrivate,
unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS,
unsigned char privateSlots = 0
);

Xbox Live sessions have their own maximum that’s configured in the game’s service config. If you increase the game constant but don’t update the Xbox Live service config, the platform will reject sessions larger than what it expects.

Sony’s SQR network manager defines its own constant:

SQRNetworkManager.h
static const int MAX_ONLINE_PLAYER_COUNT = MINECRAFT_NET_MAX_PLAYERS;

This gets used throughout the Sony network stack. PSN room sizes are configured when creating the network room, and Sony’s AVC2 voice chat API has its own limit:

SonyVoiceChat.h
#define AVC2_PARAM_DEFAULT_MAX_PLAYERS (8)
#define AVC2_PARAM_DEFAULT_MAX_SPEAKERS (4)

Note that AVC2_PARAM_DEFAULT_MAX_PLAYERS is a separate #define set to 8. It does not use MINECRAFT_NET_MAX_PLAYERS. If you change the game constant, voice chat still caps at 8 participants unless you also change this define and the PSN room configuration.

Local splitscreen supports up to XUSER_MAX_COUNT players:

extraX64.h
#ifdef __PSVITA__
const int XUSER_MAX_COUNT = 1; // Vita: no splitscreen
#else
const int XUSER_MAX_COUNT = 4; // Everyone else: 4 local players
#endif

Splitscreen players and network players share the same MINECRAFT_NET_MAX_PLAYERS pool. With 4 local splitscreen players on Xbox, you only have 4 slots left for network players. The UI code that lists “all players in the game” iterates over the same arrays.

The server broadcasts entity updates to all players in range. Here’s a rough estimate of the per-tick network cost based on the entity tracking system.

Each connected player is a tracked entity. The entity tracker sends these packets:

Packet typeSize (bytes)When
MoveEntityPacketSmall8Every tick the player moves (bit-packed delta)
MoveEntityPacket18When delta exceeds small packet range
MoveEntityPacketPosRot22Every 400 ticks (full position resync)
RotateHeadPacket6When head rotation changes
SetEntityMotionPacket10When velocity changes
SetEntityDataPacketvaries (20-80)When metadata changes (health, armor, held item)

At 20 TPS with 8 players, assume each player moves every tick (worst case):

  • 8 players, all moving: 8 players * 8 bytes * 20 ticks = 1,280 bytes/sec just for position updates
  • Plus metadata, rotation, velocity: roughly 3x the position data = ~3,840 bytes/sec
  • Each player receives updates for all other players in range: 7 other players * 3,840 = ~26,880 bytes/sec per client

With 16 players:

  • 15 other players per client: 15 * 3,840 = ~57,600 bytes/sec per client
  • Server total outbound: 16 clients * 57,600 = ~921,600 bytes/sec (~900 KB/s)

That doesn’t include chunk data, block updates, tile entity updates, item drops, mob positions, or any other traffic. The real number with a busy world is significantly higher.

For reference, Xbox 360 games typically had about 256 Kbps (32 KB/s) of guaranteed bandwidth per peer in Xbox Live sessions. 8 players is already pushing it. 16 players would require a fundamentally different network architecture.

The tracking range determines how far away a player can be and still receive updates. More players in range means more packets:

Entity typeRange (blocks)Update interval (ticks)
Players5122
Mobs803
Items6420
Arrows/projectiles6420
TNT16010
Falling blocks16020

Players have the largest tracking range (512 blocks) and the most frequent update interval (every 2 ticks). In a small world, all 8 players are almost always within tracking range of each other. Doubling the player count roughly quadruples the player-to-player tracking work (each of 16 players tracks 15 others vs each of 8 tracking 7).

The voice chat system has separate limits per platform:

PS3 (AVC2 API): Hardcoded to AVC2_PARAM_DEFAULT_MAX_PLAYERS = 8 with AVC2_PARAM_DEFAULT_MAX_SPEAKERS = 4. The AVC2 library allocates audio buffers per participant. It operates through a state machine (init, load, join, session processing, leave, unload) and tracks talking status per room member in an unordered_map.

PS4 (Party Voice): Uses the party system API (SonyVoiceChatParty_Orbis) instead of in-game voice. The party size is controlled by Sony’s platform, not the game.

PS Vita: Has its own voice chat implementation but the game only supports 4 players total anyway.

Xbox 360/One: Voice chat goes through Xbox Live party chat. The game checks IsTalking() and IsMutedByLocalUser() per QNet player, but the actual voice routing is handled by the platform.

x64/LAN build: Voice chat stubs return false for everything. No actual voice chat in LAN mode.

Player data is saved per player in the world save. The save system iterates over player files:

DirectoryLevelStorage.cpp
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
PlayerUID oldXuid = WIN64_XUID_BASE + i;
tag = loadPlayerDataTag(oldXuid);
// ...
}

Each player’s save data includes inventory, position, health, and other state. With 8 players, that’s 8 .dat files. With 16, it’s 16. On consoles with tight storage limits (especially Vita with its small memory card), this adds up.

The PlayerList also maintains per-dimension tracking lists:

PlayerList.h
vector<shared_ptr<ServerPlayer>> receiveAllPlayers[3];

These are dynamic vectors, not fixed arrays, so they grow as needed. But each entry means more broadcast targets per packet send.

Every UI screen that shows a player list iterates up to MINECRAFT_NET_MAX_PLAYERS. If you increase the limit, these screens need wider layouts or scrolling:

ScreenWhat it does with the constant
UIScene_TeleportMenuLists players you can teleport to. 4 arrays sized to the limit.
UIScene_InGameInfoMenuShows online players overlay. 4 arrays sized to the limit.
UIScene_JoinMenuShows players in a session you’re joining. 5 loops bounded by the limit.
XUI_InGameInfoXbox XUI version of the player overlay. 1 array sized to the limit.
XUI_TeleportXbox XUI version of the teleport screen. 1 array sized to the limit.
XUI_MultiGameInfoShows game info for multiplayer. 2 loops bounded by the limit.
XUI_MultiGameJoinLoadJoin/load flow for multiplayer. Uses the limit for maxPlayers checks.
UIScene_LoadOrJoinMenuCombined load/join screen. Uses the limit for maxPlayers display.

If you still want to try, here’s the full plan.

// Change in every platform header:
const int MINECRAFT_NET_MAX_PLAYERS = 16;

You need to change it in extraX64.h, extra.h, and any platform-specific header that defines it. There are conditional defines (like the Vita #ifdef), so check all of them.

Search the full codebase for MINECRAFT_NET_MAX_PLAYERS. You’ll find ~72 locations. Every fixed-size array needs to grow. Every loop bound is already using the constant, so those update automatically when you recompile. But hardcoded 8s that should have been the constant? Those exist too:

// PlayerRenderer.cpp - hardcoded 8
for (int i = 8; i < MINECRAFT_NET_MAX_PLAYERS; i++)

This loop assumes the first 8 colors are hardcoded and generates the rest procedurally. If you set the limit to 16, the first 8 still get their fixed colors and indexes 8-15 get generated colors. That actually works fine. But search for bare 8s near player-related code to make sure there aren’t other assumptions.

Any field that encodes player count or player index as a BYTE caps at 255. The LoginPacket handles this:

int maxPlayersForPacket = getMaxPlayers() > 255 ? 255 : getMaxPlayers();
static_cast<byte>(maxPlayersForPacket),

The PreLoginPacket clamps incoming player counts:

if (m_dwPlayerCount > MINECRAFT_NET_MAX_PLAYERS)
m_dwPlayerCount = MINECRAFT_NET_MAX_PLAYERS;

Check every packet that carries a player index or count. Small IDs are also bytes, so they cap at 255. For 16 players that’s fine. For 256+ you’d need to change the wire format.

  • Xbox Live: Update the service configuration for max session size
  • PSN: Update the room creation parameters in SQRNetworkManager
  • Sony Voice Chat: Change AVC2_PARAM_DEFAULT_MAX_PLAYERS and AVC2_PARAM_DEFAULT_MAX_SPEAKERS
  • LAN broadcast: The maxPlayers field in the advertise data is a unsigned char, so 16 fits fine

With more players, the server does more work per tick. Profile the tick time and make sure it stays under 50ms (for 20 TPS). The entity tracker runs every tick and checks visibility for every tracked entity against every player. That’s O(entities * players) per tick.

Player list screens, tab overlays, scoreboard displays, and teleport menus need to handle more entries. The XUI-based screens (Xbox 360) have fixed layouts that might need redesigning. The newer UI scenes might scroll, but test them.

Make sure saves with more than 8 players load correctly, and that the migration code in DirectoryLevelStorage doesn’t break.

Instead of fighting the hard limit everywhere, consider these alternatives:

  • Dedicated server mode: No local players, all 8 slots go to network clients. The x64 build already has isDedicatedServer support in the session data.
  • Reduce splitscreen: Limit to 2 local players to free up 2 more network slots (6 remote players instead of 4).
  • Spectator slots: Add a spectator mode that doesn’t count toward the player limit. Spectators skip entity tracking and don’t get a player index from the normal pool.
  • Relay server: Instead of peer-to-peer or client-hosted, use a dedicated relay that can handle the bandwidth for more clients.
FileWhat it does
extraX64.h / extra.hMINECRAFT_NET_MAX_PLAYERS definition (per platform)
PlayerList.h/.cppPlayer connection, index assignment, dimension tracking
Consoles_App.h/.cppPlayer color and privilege arrays, color assignment logic
SessionInfo.hGameSessionData struct with per-player arrays (3 platform variants)
PlayerRenderer.h/.cppNametag color array, color generation, bounds checking
Extrax64Stubs.cppQNet player array, small ID validation, player lookup
WinsockNetLayer.cppSmall ID allocation/recycling, LAN broadcast advertising
PendingConnection.cppUGC permission check array
DirectoryLevelStorage.cppPlayer data migration loop
PreLoginPacket.cppPlayer count clamping on incoming packets
LoginPacket.hLogin response packet with max players byte field
UIScene_TeleportMenu.hTeleport screen player arrays (4 arrays)
UIScene_InGameInfoMenu.hPlayer overlay arrays (4 arrays in LCEMP, commented out in newer)
XUI_InGameInfo.hXbox XUI player overlay array
UIScene_JoinMenu.cppJoin screen with 5 loops over the limit
SonyVoiceChat.hPS3 voice chat with hardcoded 8-player limit
SQRNetworkManager.hSony network layer with MAX_ONLINE_PLAYER_COUNT alias
PlatformNetworkManagerInterface.hBase class with MINECRAFT_NET_MAX_PLAYERS default params
GameNetworkManager.hHostGame with MINECRAFT_NET_MAX_PLAYERS default slots