mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-09 03:12:53 +00:00
fix: player list no longer limited by render distance
The previous IQNet cleanup in handleRemoveEntity fired on every entity despawn, which happens both when a player goes out of tracking range and when they disconnect. This caused players to vanish from the Tab list whenever they moved beyond render distance. Introduce two custom payload channels (MC|ForkHello, MC|ForkPLeave) so the client can distinguish "out of range" from "actually left": - Server sends MC|ForkHello during login to identify itself as a fork - Server sends MC|ForkPLeave with the player's gamertag on disconnect - Client skips IQNet cleanup in handleRemoveEntity on fork servers - Client cleans up IQNet only when MC|ForkPLeave arrives Fully backwards-compatible: no existing packet wire formats changed. Upstream clients ignore the unknown channels, fork clients on upstream servers fall back to the old entity-tracking-based cleanup.
This commit is contained in:
parent
8e2b475317
commit
463bf2b93f
|
|
@ -135,6 +135,7 @@ ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUs
|
|||
started = false;
|
||||
savedDataStorage = new SavedDataStorage(nullptr);
|
||||
maxPlayers = 20;
|
||||
m_isForkServer = false;
|
||||
|
||||
this->minecraft = minecraft;
|
||||
|
||||
|
|
@ -1141,7 +1142,11 @@ void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> p
|
|||
void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packet)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_NetworkManager.IsHost())
|
||||
// On fork servers, IQNet cleanup is handled by the MC|ForkPLeave custom
|
||||
// payload so players stay in Tab regardless of render distance. On
|
||||
// upstream servers (no MC|ForkHello received), fall back to the old
|
||||
// behaviour of cleaning up IQNet here.
|
||||
if (!m_isForkServer && !g_NetworkManager.IsHost())
|
||||
{
|
||||
for (int i = 0; i < packet->ids.length; i++)
|
||||
{
|
||||
|
|
@ -1151,7 +1156,6 @@ void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packe
|
|||
shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
|
||||
if (player != nullptr)
|
||||
{
|
||||
// Match by gamertag in the IQNet array (XUID may be 0 on dedicated servers)
|
||||
for (int s = 1; s < MINECRAFT_NET_MAX_PLAYERS; ++s)
|
||||
{
|
||||
IQNetPlayer* qp = &IQNet::m_player[s];
|
||||
|
|
@ -3900,6 +3904,44 @@ void ClientConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> custo
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fork server identification: enables render-distance-independent player list
|
||||
if (CustomPayloadPacket::FORK_HELLO_CHANNEL.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
m_isForkServer = true;
|
||||
app.DebugPrintf("Client: Connected to fork server\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fork server player leave: clean up IQNet slot so player leaves Tab list
|
||||
if (CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
if (customPayloadPacket->data.data != nullptr && customPayloadPacket->length > 0)
|
||||
{
|
||||
int nameLen = customPayloadPacket->length / static_cast<int>(sizeof(wchar_t));
|
||||
wstring leavingName(reinterpret_cast<const wchar_t*>(customPayloadPacket->data.data), nameLen);
|
||||
|
||||
for (int s = 1; s < MINECRAFT_NET_MAX_PLAYERS; ++s)
|
||||
{
|
||||
IQNetPlayer* qp = &IQNet::m_player[s];
|
||||
if (qp->GetCustomDataValue() != 0 &&
|
||||
_wcsicmp(qp->m_gamertag, leavingName.c_str()) == 0)
|
||||
{
|
||||
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
|
||||
g_pPlatformNetworkManager->NotifyPlayerLeaving(qp);
|
||||
qp->m_smallId = 0;
|
||||
qp->m_isRemote = false;
|
||||
qp->m_isHostPlayer = false;
|
||||
qp->m_resolvedXuid = INVALID_XUID;
|
||||
qp->m_gamertag[0] = 0;
|
||||
qp->SetCustomDataValue(0);
|
||||
app.DebugPrintf("Client: Player \"%ls\" left fork server, cleared IQNet slot %d\n", leavingName.c_str(), s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CustomPayloadPacket::TRADER_LIST_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ private:
|
|||
|
||||
std::unordered_set<int> m_trackedEntityIds;
|
||||
std::unordered_set<int64_t> m_visibleChunks;
|
||||
bool m_isForkServer; // true when connected to a fork server (received MC|ForkHello)
|
||||
|
||||
static int64_t chunkKey(int x, int z) { return ((int64_t)x << 32) | ((int64_t)z & 0xFFFFFFFF); }
|
||||
|
||||
|
|
|
|||
|
|
@ -290,6 +290,12 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
|
|||
playerConnection->send(std::make_shared<SetCarriedItemPacket>(player->inventory->selected));
|
||||
delete spawnPos;
|
||||
|
||||
// Identify this server as a fork so the client can enable extended
|
||||
// features (e.g. render-distance-independent player list). Upstream
|
||||
// clients will silently ignore the unknown channel.
|
||||
playerConnection->send(std::make_shared<CustomPayloadPacket>(
|
||||
CustomPayloadPacket::FORK_HELLO_CHANNEL, byteArray()));
|
||||
|
||||
updateEntireScoreboard(reinterpret_cast<ServerScoreboard *>(level->getScoreboard()), player);
|
||||
|
||||
sendLevelInfo(player, level);
|
||||
|
|
@ -654,7 +660,17 @@ if (player->riding != nullptr)
|
|||
{
|
||||
players.erase(it);
|
||||
}
|
||||
//broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(player->name, false, 9999) ) );
|
||||
// Notify fork clients that this player has left the server so they can
|
||||
// clean up IQNet/Tab list entries. Uses a custom payload channel so the
|
||||
// wire format of existing packets is unchanged (upstream clients simply
|
||||
// ignore the unknown channel).
|
||||
{
|
||||
const wstring& name = player->getName();
|
||||
byteArray payload(static_cast<int>(name.size() * sizeof(wchar_t)));
|
||||
memcpy(payload.data, name.c_str(), payload.length);
|
||||
broadcastAll(std::make_shared<CustomPayloadPacket>(
|
||||
CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL, payload));
|
||||
}
|
||||
|
||||
removePlayerFromReceiving(player);
|
||||
player->connection = nullptr; // Must remove reference to connection, or else there is a circular dependency
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = L"MC|CTIssue";
|
|||
const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = L"MC|CTChallenge";
|
||||
const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = L"MC|CTResponse";
|
||||
|
||||
const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = L"MC|ForkHello";
|
||||
const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = L"MC|ForkPLeave";
|
||||
|
||||
CustomPayloadPacket::CustomPayloadPacket()
|
||||
: length(0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ public:
|
|||
static const wstring IDENTITY_TOKEN_CHALLENGE; // server->client: request stored token
|
||||
static const wstring IDENTITY_TOKEN_RESPONSE; // client->server: present stored token
|
||||
|
||||
// Fork extensions: server capability and player lifecycle
|
||||
static const wstring FORK_HELLO_CHANNEL; // server->client: identifies fork server (empty payload)
|
||||
static const wstring FORK_PLAYER_LEAVE_CHANNEL; // server->client: player disconnected (payload: UTF gamertag)
|
||||
|
||||
wstring identifier;
|
||||
int length;
|
||||
byteArray data;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ This project is based on source code of Minecraft Legacy Console Edition v1.6.05
|
|||
|
||||
## Latest:
|
||||
|
||||
### Render-Distance-Independent Player List
|
||||
|
||||
- The Tab player list now shows all players on the server regardless of render distance. Previously, the player list was tied to entity tracking -- players only appeared in Tab when their entity was within render distance, and disappeared when they moved out of range
|
||||
- The fix uses custom payload channels (`MC|ForkHello`, `MC|ForkPLeave`) to signal server identity and player disconnects, so the client can distinguish "player went out of range" from "player left the server." Entity despawns no longer clear the IQNet slot; only an explicit disconnect signal does
|
||||
- Fully backwards-compatible: no existing packet wire formats are changed. Upstream clients ignore the unknown channels, and fork clients connecting to upstream servers fall back to the old entity-tracking-based behavior
|
||||
|
||||
### Graphics Settings Menu Fixes
|
||||
|
||||
- Fixed keyboard/gamepad navigation skipping VSync, Fullscreen, and Render Distance options. The SWF focus chain only linked the original console controls; added C++ post-init rewiring of `m_objNavDown`/`m_objNavUp` via Iggy so all controls are reachable
|
||||
|
|
@ -120,7 +126,7 @@ proxy-protocol=true
|
|||
|
||||
- The Tab player list now correctly shows all connected players on dedicated servers. Previously only the local player was visible because remote players were never registered in the client's network player tracking when their `AddPlayerPacket` arrived
|
||||
- The dedicated server's phantom host entry (slot 0, empty name) is now filtered from the list
|
||||
- Players are properly removed from the list when they disconnect, using gamertag matching since dedicated server XUIDs are not available on the client
|
||||
- ~~Players are properly removed from the list when they disconnect, using gamertag matching since dedicated server XUIDs are not available on the client~~ This initial implementation incorrectly tied IQNet cleanup to entity removal (`RemoveEntitiesPacket`), which is sent both when a player goes out of render distance and when they disconnect. This caused players to disappear from the Tab list whenever they moved out of tracking range. Fixed in the "Render-Distance-Independent Player List" update above
|
||||
|
||||
### SRV Record Support and Async Join Refactor
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue