mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-05-10 15:59:58 +00:00
Merge 898d4352a3 into d4ac3cd645
This commit is contained in:
commit
2015e0c6f2
|
|
@ -66,6 +66,30 @@
|
|||
#include "../Minecraft.World/GenericStats.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
char mapIconToFrame(char iconSlot)
|
||||
{
|
||||
if (iconSlot >= 8) return iconSlot - 4;
|
||||
return iconSlot;
|
||||
}
|
||||
|
||||
// Same hash as getRandomPlayerMapIcon in MapItemSavedData.cpp, returning
|
||||
// the Iggy/SWF frame index (0-7) instead of the raw icon slot.
|
||||
char computePlayerMapFrame(int entityId, int playerIndex)
|
||||
{
|
||||
static const char PLAYER_MAP_ICON_SLOTS[] = { 0, 1, 2, 3, 8, 9, 10, 11 };
|
||||
unsigned int seed = static_cast<unsigned int>(entityId);
|
||||
seed ^= static_cast<unsigned int>(playerIndex * 0x9E3779B9u);
|
||||
seed ^= (seed >> 16);
|
||||
seed *= 0x7FEB352Du;
|
||||
seed ^= (seed >> 15);
|
||||
seed *= 0x846CA68Bu;
|
||||
seed ^= (seed >> 16);
|
||||
return mapIconToFrame(PLAYER_MAP_ICON_SLOTS[seed % 8]);
|
||||
}
|
||||
}
|
||||
|
||||
ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port)
|
||||
{
|
||||
// 4J Stu - No longer used as we use the socket version below.
|
||||
|
|
@ -377,6 +401,7 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
|||
|
||||
BYTE networkSmallId = getSocket()->getSmallId();
|
||||
app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges);
|
||||
app.SetPlayerMapIcon(minecraft->player->getName().c_str(), computePlayerMapFrame(packet->clientVersion, packet->m_playerIndex));
|
||||
minecraft->player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
|
||||
|
||||
// Assume all privileges are on, so that the first message we see only indicates things that have been turned off
|
||||
|
|
@ -447,6 +472,7 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
|||
|
||||
BYTE networkSmallId = getSocket()->getSmallId();
|
||||
app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges);
|
||||
app.SetPlayerMapIcon(player->getName().c_str(), computePlayerMapFrame(packet->clientVersion, packet->m_playerIndex));
|
||||
player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
|
||||
|
||||
// Assume all privileges are on, so that the first message we see only indicates things that have been turned off
|
||||
|
|
@ -917,6 +943,36 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
|
|||
}
|
||||
}
|
||||
|
||||
// Client-side registration: if we still have no IQNet entry for this remote
|
||||
// player, create one so they appear in the Tab player list.
|
||||
// Find the first available IQNet slot (customData == 0, skip slot 0 which
|
||||
// is the host). We can't use packet->m_playerIndex directly because on
|
||||
// dedicated servers the game-level player index starts at 0 for real
|
||||
// players, conflicting with the IQNet host slot.
|
||||
if (matchedQNetPlayer == nullptr)
|
||||
{
|
||||
for (int s = 1; s < MINECRAFT_NET_MAX_PLAYERS; ++s)
|
||||
{
|
||||
IQNetPlayer* qp = &IQNet::m_player[s];
|
||||
if (qp->GetCustomDataValue() == 0 && qp->m_gamertag[0] == 0)
|
||||
{
|
||||
BYTE smallId = static_cast<BYTE>(s);
|
||||
qp->m_smallId = smallId;
|
||||
qp->m_isRemote = true;
|
||||
qp->m_isHostPlayer = false;
|
||||
qp->m_resolvedXuid = pktXuid;
|
||||
wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE);
|
||||
if (smallId >= IQNet::s_playerCount)
|
||||
IQNet::s_playerCount = smallId + 1;
|
||||
|
||||
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
|
||||
g_pPlatformNetworkManager->NotifyPlayerJoined(qp);
|
||||
matchedQNetPlayer = qp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedQNetPlayer != nullptr)
|
||||
{
|
||||
// Store packet-authoritative XUID on this network slot so later lookups by XUID
|
||||
|
|
@ -946,6 +1002,7 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
|
|||
player->setPlayerIndex( packet->m_playerIndex );
|
||||
player->setCustomSkin( packet->m_skinId );
|
||||
player->setCustomCape( packet->m_capeId );
|
||||
app.SetPlayerMapIcon(packet->name.c_str(), computePlayerMapFrame(packet->id, packet->m_playerIndex));
|
||||
player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
|
||||
|
||||
if (!player->customTextureUrl.empty() && player->customTextureUrl.substr(0, 3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl))
|
||||
|
|
@ -1088,28 +1145,27 @@ void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packe
|
|||
for (int i = 0; i < packet->ids.length; i++)
|
||||
{
|
||||
shared_ptr<Entity> entity = getEntity(packet->ids[i]);
|
||||
if (entity != nullptr && entity->GetType() == eTYPE_PLAYER)
|
||||
if (entity != nullptr)
|
||||
{
|
||||
shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
|
||||
if (player != nullptr)
|
||||
{
|
||||
PlayerUID xuid = player->getXuid();
|
||||
INetworkPlayer* np = g_NetworkManager.GetPlayerByXuid(xuid);
|
||||
if (np != 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)
|
||||
{
|
||||
NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np;
|
||||
IQNetPlayer* qp = npx->GetQNetPlayer();
|
||||
if (qp != nullptr)
|
||||
IQNetPlayer* qp = &IQNet::m_player[s];
|
||||
if (qp->GetCustomDataValue() != 0 &&
|
||||
_wcsicmp(qp->m_gamertag, player->getName().c_str()) == 0)
|
||||
{
|
||||
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
|
||||
g_pPlatformNetworkManager->NotifyPlayerLeaving(qp);
|
||||
qp->m_smallId = 0;
|
||||
qp->m_isRemote = false;
|
||||
qp->m_isHostPlayer = false;
|
||||
// Clear resolved id to avoid stale XUID -> player matches after disconnect.
|
||||
qp->m_resolvedXuid = INVALID_XUID;
|
||||
qp->m_gamertag[0] = 0;
|
||||
qp->SetCustomDataValue(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ CMinecraftApp::CMinecraftApp()
|
|||
#endif
|
||||
|
||||
ZeroMemory(m_playerColours,MINECRAFT_NET_MAX_PLAYERS);
|
||||
ZeroMemory(m_playerMapIcons,MINECRAFT_NET_MAX_PLAYERS);
|
||||
|
||||
m_iDLCOfferC=0;
|
||||
m_bAllDLCContentRetrieved=true;
|
||||
|
|
@ -8555,6 +8556,39 @@ short CMinecraftApp::GetPlayerColour(BYTE networkSmallId)
|
|||
return index;
|
||||
}
|
||||
|
||||
void CMinecraftApp::SetPlayerMapIcon(const wchar_t* name, char icon)
|
||||
{
|
||||
if (name == nullptr) return;
|
||||
// Update existing entry or use first empty slot
|
||||
int emptySlot = -1;
|
||||
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
|
||||
{
|
||||
if (m_playerMapIcons[i].name[0] != 0 && _wcsicmp(m_playerMapIcons[i].name, name) == 0)
|
||||
{
|
||||
m_playerMapIcons[i].icon = icon;
|
||||
return;
|
||||
}
|
||||
if (emptySlot < 0 && m_playerMapIcons[i].name[0] == 0)
|
||||
emptySlot = i;
|
||||
}
|
||||
if (emptySlot >= 0)
|
||||
{
|
||||
wcsncpy_s(m_playerMapIcons[emptySlot].name, 32, name, _TRUNCATE);
|
||||
m_playerMapIcons[emptySlot].icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
char CMinecraftApp::GetPlayerMapIconByName(const wchar_t* name)
|
||||
{
|
||||
if (name == nullptr) return 0;
|
||||
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
|
||||
{
|
||||
if (m_playerMapIcons[i].name[0] != 0 && _wcsicmp(m_playerMapIcons[i].name, name) == 0)
|
||||
return m_playerMapIcons[i].icon;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int CMinecraftApp::GetPlayerPrivileges(BYTE networkSmallId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -749,10 +749,14 @@ public:
|
|||
private:
|
||||
BYTE m_playerColours[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's
|
||||
unsigned int m_playerGamePrivileges[MINECRAFT_NET_MAX_PLAYERS];
|
||||
struct PlayerMapIconEntry { wchar_t name[32]; char icon; };
|
||||
PlayerMapIconEntry m_playerMapIcons[MINECRAFT_NET_MAX_PLAYERS];
|
||||
|
||||
public:
|
||||
void UpdatePlayerInfo(BYTE networkSmallId, SHORT playerColourIndex, unsigned int playerGamePrivileges);
|
||||
short GetPlayerColour(BYTE networkSmallId);
|
||||
void SetPlayerMapIcon(const wchar_t* name, char icon);
|
||||
char GetPlayerMapIconByName(const wchar_t* name);
|
||||
unsigned int GetPlayerPrivileges(BYTE networkSmallId);
|
||||
|
||||
wstring getEntityName(eINSTANCEOF type);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,15 @@ UIScene_InGameInfoMenu::UIScene_InGameInfoMenu(int iPad, void *initData, UILayer
|
|||
{
|
||||
PlayerInfo *info = BuildPlayerInfo(player);
|
||||
|
||||
// Skip the dedicated server's phantom host entry (slot 0, empty name)
|
||||
if (info->m_smallId == 0 && info->m_name.empty())
|
||||
{
|
||||
delete info;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_players.push_back(info);
|
||||
m_playerList.addItem(info->m_name, info->m_colorState, info->m_voiceStatus);
|
||||
m_playerList.addItem(info->m_name, info->m_colorState, info->m_voiceStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,8 +181,15 @@ void UIScene_InGameInfoMenu::handleReload()
|
|||
{
|
||||
PlayerInfo *info = BuildPlayerInfo(player);
|
||||
|
||||
// Skip the dedicated server's phantom host entry (slot 0, empty name)
|
||||
if (info->m_smallId == 0 && info->m_name.empty())
|
||||
{
|
||||
delete info;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_players.push_back(info);
|
||||
m_playerList.addItem(info->m_name, info->m_colorState, info->m_voiceStatus);
|
||||
m_playerList.addItem(info->m_name, info->m_colorState, info->m_voiceStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,23 +216,22 @@ void UIScene_InGameInfoMenu::tick()
|
|||
{
|
||||
UIScene::tick();
|
||||
|
||||
// Update players by index
|
||||
// Update players by their stored smallId (not sequential index, which can mismatch
|
||||
// when entries like the dedicated server host are filtered from the UI list)
|
||||
for(DWORD i = 0; i < m_players.size(); ++i)
|
||||
{
|
||||
INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i );
|
||||
INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId( m_players[i]->m_smallId );
|
||||
|
||||
if(player != nullptr)
|
||||
{
|
||||
PlayerInfo *info = BuildPlayerInfo(player);
|
||||
|
||||
m_players[i]->m_smallId = info->m_smallId;
|
||||
|
||||
if(info->m_voiceStatus != m_players[i]->m_voiceStatus)
|
||||
{
|
||||
m_players[i]->m_voiceStatus = info->m_voiceStatus;
|
||||
m_playerList.setVOIPIcon(i, info->m_voiceStatus);
|
||||
}
|
||||
|
||||
|
||||
if(info->m_colorState != m_players[i]->m_colorState)
|
||||
{
|
||||
m_players[i]->m_colorState = info->m_colorState;
|
||||
|
|
@ -424,11 +437,19 @@ void UIScene_InGameInfoMenu::OnPlayerChanged(void *callbackParam, INetworkPlayer
|
|||
// If the player is joining
|
||||
if(!leaving)
|
||||
{
|
||||
PlayerInfo *info = scene->BuildPlayerInfo(pPlayer);
|
||||
|
||||
// Skip the dedicated server's phantom host entry (slot 0, empty name)
|
||||
if (pPlayer->GetSmallId() == 0 && info->m_name.empty())
|
||||
{
|
||||
delete info;
|
||||
return;
|
||||
}
|
||||
|
||||
app.DebugPrintf("<UIScene_InGameInfoMenu::OnPlayerChanged> Player \"%ls\" not found, adding\n", pPlayer->GetOnlineName());
|
||||
|
||||
PlayerInfo *info = scene->BuildPlayerInfo(pPlayer);
|
||||
scene->m_players.push_back(info);
|
||||
|
||||
|
||||
// Note that the tick updates buttons every tick so it's only really important that we
|
||||
// add the button (not the order or content)
|
||||
scene->m_playerList.addItem(info->m_name, info->m_colorState, info->m_voiceStatus);
|
||||
|
|
@ -491,7 +512,7 @@ UIScene_InGameInfoMenu::PlayerInfo *UIScene_InGameInfoMenu::BuildPlayerInfo(INet
|
|||
}
|
||||
|
||||
info->m_voiceStatus = voiceStatus;
|
||||
info->m_colorState = app.GetPlayerColour(info->m_smallId);
|
||||
info->m_colorState = app.GetPlayerMapIconByName(player->GetOnlineName());
|
||||
info->m_name = playerName;
|
||||
|
||||
return info;
|
||||
|
|
|
|||
|
|
@ -193,12 +193,12 @@ void UIScene_TeleportMenu::tick()
|
|||
{
|
||||
m_players[i] = player->GetSmallId();
|
||||
|
||||
short icon = app.GetPlayerColour( m_players[i] );
|
||||
short icon = static_cast<short>(app.GetPlayerMapIconByName(player->GetOnlineName()));
|
||||
|
||||
if(icon != m_playersColourState[i])
|
||||
{
|
||||
m_playersColourState[i] = icon;
|
||||
m_playerList.setPlayerIcon( i, (int)app.GetPlayerColour( m_players[i] ) );
|
||||
m_playerList.setPlayerIcon( i, (int)icon );
|
||||
}
|
||||
|
||||
wstring playerName = L"";
|
||||
|
|
|
|||
|
|
@ -496,6 +496,50 @@ void PlayerList::add(shared_ptr<ServerPlayer> 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<AddPlayerPacket>(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<ServerPlayer> 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<AddPlayerPacket>(op, opXuid, opOnlineXuid, oxp, oyp, ozp, oyRotp, oxRotp, oyHeadRotp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(level->isAtLeastOnePlayerSleeping())
|
||||
{
|
||||
shared_ptr<ServerPlayer> 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<RemoveEntitiesPacket>(ids));
|
||||
}
|
||||
level->getTracker()->removeEntity(player);
|
||||
level->removeEntity(player);
|
||||
level->getChunkMap()->remove(player);
|
||||
|
|
|
|||
Loading…
Reference in a new issue