From e706466f42be424c7dd290b8ed2bb32c865f6072 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 b8eb4675..b0ab1ef6 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);