From 898d4352a3eedb9788d20f8ec03fcbfa0b192f6b Mon Sep 17 00:00:00 2001 From: itsRevela Date: Thu, 26 Mar 2026 23:54:51 -0500 Subject: [PATCH] Send AddPlayerPacket for all players on join and RemoveEntitiesPacket on disconnect Players now appear in each other's Tab list immediately on join, regardless of render distance. Previously, players only appeared when they entered entity tracking range because AddPlayerPacket was only sent through the TrackedEntity system. On disconnect, a RemoveEntitiesPacket is broadcast to all clients so players added via the join broadcast are properly cleaned up, not just those within tracking range. --- Minecraft.Client/PlayerList.cpp | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index ba82ec6ac..8a6b531a5 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -496,6 +496,50 @@ void PlayerList::add(shared_ptr player) } } + // Send AddPlayerPackets so all players appear in each other's Tab list + // regardless of render distance. The entity tracking system will send + // another AddPlayerPacket when they enter range, which is handled + // gracefully by putEntity replacing the old entity. + { + PlayerUID xuid = INVALID_XUID; + PlayerUID onlineXuid = INVALID_XUID; +#ifndef MINECRAFT_SERVER_BUILD + xuid = player->getXuid(); + onlineXuid = player->getOnlineXuid(); +#endif + int xp = Mth::floor(player->x * 32.0); + int yp = Mth::floor(player->y * 32.0); + int zp = Mth::floor(player->z * 32.0); + int yRotp = Mth::floor(player->yRot * 256.0f / 360.0f); + int xRotp = Mth::floor(player->xRot * 256.0f / 360.0f); + int yHeadRotp = Mth::floor(player->yHeadRot * 256.0f / 360.0f); + + // Broadcast the new player to all existing players + broadcastAll(std::make_shared(player, xuid, onlineXuid, xp, yp, zp, yRotp, xRotp, yHeadRotp)); + + // Send all existing players to the new player + for (size_t i = 0; i < players.size(); i++) + { + shared_ptr op = players.at(i); + if (op != player && op->connection->getNetworkPlayer()) + { + PlayerUID opXuid = INVALID_XUID; + PlayerUID opOnlineXuid = INVALID_XUID; +#ifndef MINECRAFT_SERVER_BUILD + opXuid = op->getXuid(); + opOnlineXuid = op->getOnlineXuid(); +#endif + int oxp = Mth::floor(op->x * 32.0); + int oyp = Mth::floor(op->y * 32.0); + int ozp = Mth::floor(op->z * 32.0); + int oyRotp = Mth::floor(op->yRot * 256.0f / 360.0f); + int oxRotp = Mth::floor(op->xRot * 256.0f / 360.0f); + int oyHeadRotp = Mth::floor(op->yHeadRot * 256.0f / 360.0f); + player->connection->send(std::make_shared(op, opXuid, opOnlineXuid, oxp, oyp, ozp, oyRotp, oxRotp, oyHeadRotp)); + } + } + } + if(level->isAtLeastOnePlayerSleeping()) { shared_ptr firstSleepingPlayer = nullptr; @@ -528,6 +572,14 @@ if (player->riding != nullptr) level->removeEntityImmediately(player->riding); app.DebugPrintf("removing player mount"); } + // Notify all clients to remove this player entity, not just those who + // had the player in tracking range. This ensures players added to the + // Tab list via the AddPlayerPacket broadcast are properly cleaned up. + { + intArray ids(1); + ids[0] = player->entityId; + broadcastAll(std::make_shared(ids)); + } level->getTracker()->removeEntity(player); level->removeEntity(player); level->getChunkMap()->remove(player);