mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Merge branch 'smartcmd:main' into main
This commit is contained in:
commit
4476b4df8e
|
|
@ -142,6 +142,13 @@ ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUs
|
|||
deferredEntityLinkPackets = vector<DeferredEntityLinkPacket>();
|
||||
}
|
||||
|
||||
bool ClientConnection::isPrimaryConnection() const
|
||||
{
|
||||
// On host, all connections are primary (server is authoritative).
|
||||
// On non-host, only the primary pad processes shared entity state.
|
||||
return g_NetworkManager.IsHost() || m_userIndex == ProfileManager.GetPrimaryPad();
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
delete connection;
|
||||
|
|
@ -304,6 +311,10 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
|||
level->isClientSide = true;
|
||||
minecraft->setLevel(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
level = (MultiPlayerLevel *)dimensionLevel;
|
||||
}
|
||||
|
||||
minecraft->player->setPlayerIndex( packet->m_playerIndex );
|
||||
minecraft->player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) );
|
||||
|
|
@ -705,6 +716,7 @@ void ClientConnection::handleAddExperienceOrb(shared_ptr<AddExperienceOrbPacket>
|
|||
|
||||
void ClientConnection::handleAddGlobalEntity(shared_ptr<AddGlobalEntityPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
double x = packet->x / 32.0;
|
||||
double y = packet->y / 32.0;
|
||||
double z = packet->z / 32.0;
|
||||
|
|
@ -730,6 +742,13 @@ void ClientConnection::handleAddPainting(shared_ptr<AddPaintingPacket> packet)
|
|||
|
||||
void ClientConnection::handleSetEntityMotion(shared_ptr<SetEntityMotionPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection())
|
||||
{
|
||||
// Secondary connection: only accept motion for our own local player (knockback)
|
||||
if (minecraft->localplayers[m_userIndex] == NULL ||
|
||||
packet->id != minecraft->localplayers[m_userIndex]->entityId)
|
||||
return;
|
||||
}
|
||||
shared_ptr<Entity> e = getEntity(packet->id);
|
||||
if (e == NULL) return;
|
||||
e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0);
|
||||
|
|
@ -953,6 +972,7 @@ void ClientConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> pac
|
|||
|
||||
void ClientConnection::handleMoveEntity(shared_ptr<MoveEntityPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
shared_ptr<Entity> e = getEntity(packet->id);
|
||||
if (e == NULL) return;
|
||||
e->xp += packet->xa;
|
||||
|
|
@ -982,6 +1002,7 @@ void ClientConnection::handleRotateMob(shared_ptr<RotateHeadPacket> packet)
|
|||
|
||||
void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
shared_ptr<Entity> e = getEntity(packet->id);
|
||||
if (e == NULL) return;
|
||||
e->xp += packet->xa;
|
||||
|
|
@ -1106,6 +1127,7 @@ void ClientConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
|
|||
// 4J Added
|
||||
void ClientConnection::handleChunkVisibilityArea(shared_ptr<ChunkVisibilityAreaPacket> packet)
|
||||
{
|
||||
if (level == NULL) return;
|
||||
for(int z = packet->m_minZ; z <= packet->m_maxZ; ++z)
|
||||
for(int x = packet->m_minX; x <= packet->m_maxX; ++x)
|
||||
level->setChunkVisible(x, z, true);
|
||||
|
|
@ -1113,11 +1135,13 @@ void ClientConnection::handleChunkVisibilityArea(shared_ptr<ChunkVisibilityAreaP
|
|||
|
||||
void ClientConnection::handleChunkVisibility(shared_ptr<ChunkVisibilityPacket> packet)
|
||||
{
|
||||
if (level == NULL) return;
|
||||
level->setChunkVisible(packet->x, packet->z, packet->visible);
|
||||
}
|
||||
|
||||
void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
// 4J - changed to encode level in packet
|
||||
MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
|
||||
if( dimensionLevel )
|
||||
|
|
@ -1187,16 +1211,29 @@ void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket>
|
|||
|
||||
void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
// 4J - changed to encode level in packet
|
||||
MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
|
||||
if( dimensionLevel )
|
||||
{
|
||||
PIXBeginNamedEvent(0,"Handle block region update");
|
||||
|
||||
if(packet->bIsFullChunk && packet->ys == 0)
|
||||
{
|
||||
app.DebugPrintf("[BRUP-CLIENT] *** EMPTY FULL CHUNK received at (%d,%d)! Buffer length=%d\n",
|
||||
packet->x>>4, packet->z>>4, packet->buffer.length);
|
||||
}
|
||||
|
||||
int y1 = packet->y + packet->ys;
|
||||
if(packet->bIsFullChunk)
|
||||
{
|
||||
y1 = Level::maxBuildHeight;
|
||||
|
||||
// Ensure the chunk exists in the cache before writing data.
|
||||
// The ChunkVisibilityAreaPacket that creates chunks can arrive AFTER the first BRUP,
|
||||
// causing getChunk() to return EmptyLevelChunk (whose setBlocksAndData is a no-op).
|
||||
dimensionLevel->setChunkVisible(packet->x >> 4, packet->z >> 4, true);
|
||||
|
||||
if(packet->buffer.length > 0)
|
||||
{
|
||||
PIXBeginNamedEvent(0, "Reordering to XZY");
|
||||
|
|
@ -1235,6 +1272,7 @@ void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacke
|
|||
|
||||
void ClientConnection::handleTileUpdate(shared_ptr<TileUpdatePacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
// 4J added - using a block of 255 to signify that this is a packet for destroying a tile, where we need to inform the level renderer that we are about to do so.
|
||||
// This is used in creative mode as the point where a tile is first destroyed at the client end of things. Packets formed like this are potentially sent from
|
||||
// ServerPlayerGameMode::destroyBlock
|
||||
|
|
@ -1349,6 +1387,7 @@ void ClientConnection::send(shared_ptr<Packet> packet)
|
|||
|
||||
void ClientConnection::handleTakeItemEntity(shared_ptr<TakeItemEntityPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
shared_ptr<Entity> from = getEntity(packet->itemId);
|
||||
shared_ptr<LivingEntity> to = dynamic_pointer_cast<LivingEntity>(getEntity(packet->playerId));
|
||||
|
||||
|
|
@ -2847,31 +2886,34 @@ void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
|
|||
|
||||
void ClientConnection::handleExplosion(shared_ptr<ExplodePacket> packet)
|
||||
{
|
||||
if(!packet->m_bKnockbackOnly)
|
||||
// World modification (block destruction) must only happen once
|
||||
if (isPrimaryConnection())
|
||||
{
|
||||
//app.DebugPrintf("Received ExplodePacket with explosion data\n");
|
||||
PIXBeginNamedEvent(0,"Handling explosion");
|
||||
Explosion *e = new Explosion(minecraft->level, nullptr, packet->x, packet->y, packet->z, packet->r);
|
||||
PIXBeginNamedEvent(0,"Finalizing");
|
||||
if(!packet->m_bKnockbackOnly)
|
||||
{
|
||||
//app.DebugPrintf("Received ExplodePacket with explosion data\n");
|
||||
PIXBeginNamedEvent(0,"Handling explosion");
|
||||
Explosion *e = new Explosion(minecraft->level, nullptr, packet->x, packet->y, packet->z, packet->r);
|
||||
PIXBeginNamedEvent(0,"Finalizing");
|
||||
|
||||
// Fix for #81758 - TCR 006 BAS Non-Interactive Pause: TU9: Performance: Gameplay: After detonating bunch of TNT, game enters unresponsive state for couple of seconds.
|
||||
// The changes we are making here have been decided by the server, so we don't need to add them to the vector that resets tiles changes made
|
||||
// on the client as we KNOW that the server is matching these changes
|
||||
MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)minecraft->level;
|
||||
mpLevel->enableResetChanges(false);
|
||||
// 4J - now directly pass a pointer to the toBlow array in the packet rather than copying around
|
||||
e->finalizeExplosion(true, &packet->toBlow);
|
||||
mpLevel->enableResetChanges(true);
|
||||
PIXEndNamedEvent();
|
||||
PIXEndNamedEvent();
|
||||
delete e;
|
||||
}
|
||||
else
|
||||
{
|
||||
//app.DebugPrintf("Received ExplodePacket with knockback only data\n");
|
||||
// Fix for #81758 - TCR 006 BAS Non-Interactive Pause: TU9: Performance: Gameplay: After detonating bunch of TNT, game enters unresponsive state for couple of seconds.
|
||||
// The changes we are making here have been decided by the server, so we don't need to add them to the vector that resets tiles changes made
|
||||
// on the client as we KNOW that the server is matching these changes
|
||||
MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)minecraft->level;
|
||||
mpLevel->enableResetChanges(false);
|
||||
// 4J - now directly pass a pointer to the toBlow array in the packet rather than copying around
|
||||
e->finalizeExplosion(true, &packet->toBlow);
|
||||
mpLevel->enableResetChanges(true);
|
||||
PIXEndNamedEvent();
|
||||
PIXEndNamedEvent();
|
||||
delete e;
|
||||
}
|
||||
}
|
||||
|
||||
// Per-player knockback — each connection applies to its own local player
|
||||
//app.DebugPrintf("Adding knockback (%f,%f,%f) for player %d\n", packet->getKnockbackX(), packet->getKnockbackY(), packet->getKnockbackZ(), m_userIndex);
|
||||
if (minecraft->localplayers[m_userIndex] == NULL)
|
||||
return;
|
||||
minecraft->localplayers[m_userIndex]->xd += packet->getKnockbackX();
|
||||
minecraft->localplayers[m_userIndex]->yd += packet->getKnockbackY();
|
||||
minecraft->localplayers[m_userIndex]->zd += packet->getKnockbackZ();
|
||||
|
|
@ -2881,6 +2923,8 @@ void ClientConnection::handleContainerOpen(shared_ptr<ContainerOpenPacket> packe
|
|||
{
|
||||
bool failed = false;
|
||||
shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
|
||||
if (player == NULL)
|
||||
return;
|
||||
switch(packet->type)
|
||||
{
|
||||
case ContainerOpenPacket::BONUS_CHEST:
|
||||
|
|
@ -3187,6 +3231,7 @@ void ClientConnection::handleTileEditorOpen(shared_ptr<TileEditorOpenPacket> pac
|
|||
|
||||
void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
app.DebugPrintf("ClientConnection::handleSignUpdate - ");
|
||||
if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
|
||||
{
|
||||
|
|
@ -3220,6 +3265,7 @@ void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
|
|||
|
||||
void ClientConnection::handleTileEntityData(shared_ptr<TileEntityDataPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
|
||||
{
|
||||
shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z);
|
||||
|
|
@ -3272,6 +3318,7 @@ void ClientConnection::handleContainerClose(shared_ptr<ContainerClosePacket> pac
|
|||
|
||||
void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
PIXBeginNamedEvent(0,"Handle tile event\n");
|
||||
minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->tile, packet->b0, packet->b1);
|
||||
PIXEndNamedEvent();
|
||||
|
|
@ -3279,6 +3326,7 @@ void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet)
|
|||
|
||||
void ClientConnection::handleTileDestruction(shared_ptr<TileDestructionPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
minecraft->level->destroyTileProgress(packet->getEntityId(), packet->getX(), packet->getY(), packet->getZ(), packet->getState());
|
||||
}
|
||||
|
||||
|
|
@ -3360,6 +3408,7 @@ void ClientConnection::handleGameEvent(shared_ptr<GameEventPacket> gameEventPack
|
|||
|
||||
void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
if (packet->itemType == Item::map->id)
|
||||
{
|
||||
MapItem::getSavedData(packet->itemId, minecraft->level)->handleComplexItemData(packet->data);
|
||||
|
|
@ -3374,6 +3423,7 @@ void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> p
|
|||
|
||||
void ClientConnection::handleLevelEvent(shared_ptr<LevelEventPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
if (packet->type == LevelEvent::SOUND_DRAGON_DEATH)
|
||||
{
|
||||
for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
|
||||
|
|
@ -3597,6 +3647,7 @@ void ClientConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> p
|
|||
|
||||
void ClientConnection::handleSoundEvent(shared_ptr<LevelSoundPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
minecraft->level->playLocalSound(packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), packet->getVolume(), packet->getPitch(), false);
|
||||
}
|
||||
|
||||
|
|
@ -3909,6 +3960,7 @@ void ClientConnection::handleSetPlayerTeamPacket(shared_ptr<SetPlayerTeamPacket>
|
|||
|
||||
void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet)
|
||||
{
|
||||
if (!isPrimaryConnection()) return;
|
||||
for (int i = 0; i < packet->getCount(); i++)
|
||||
{
|
||||
double xVarience = random->nextGaussian() * packet->getXDist();
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public:
|
|||
|
||||
private:
|
||||
DWORD m_userIndex; // 4J Added
|
||||
bool isPrimaryConnection() const;
|
||||
public:
|
||||
SavedDataStorage *savedDataStorage;
|
||||
ClientConnection(Minecraft *minecraft, const wstring& ip, int port);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@
|
|||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\..\Windows64\Network\WinsockNetLayer.h"
|
||||
#include "..\..\Windows64\Windows64_Xuid.h"
|
||||
#endif
|
||||
|
||||
// Global instance
|
||||
CGameNetworkManager g_NetworkManager;
|
||||
CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager;
|
||||
|
|
@ -1501,6 +1506,45 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
|
|||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
// Non-host split-screen: open a dedicated TCP connection for this pad
|
||||
if (localPlayer && !g_NetworkManager.IsHost() && g_NetworkManager.IsInGameplay())
|
||||
{
|
||||
int padIdx = pNetworkPlayer->GetUserIndex();
|
||||
BYTE assignedSmallId = 0;
|
||||
|
||||
if (!WinsockNetLayer::JoinSplitScreen(padIdx, &assignedSmallId))
|
||||
{
|
||||
app.DebugPrintf("Split-screen pad %d: failed to open TCP to host\n", padIdx);
|
||||
pMinecraft->connectionDisconnected(padIdx, DisconnectPacket::eDisconnect_ConnectionCreationFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the local IQNetPlayer (at pad index) with the host-assigned smallId.
|
||||
// The NetworkPlayerXbox created by NotifyPlayerJoined already points to
|
||||
// m_player[padIdx], so we just set the smallId for network routing.
|
||||
IQNet::m_player[padIdx].m_smallId = assignedSmallId;
|
||||
IQNet::m_player[padIdx].m_resolvedXuid = Win64Xuid::DeriveXuidForPad(Win64Xuid::ResolvePersistentXuid(), padIdx);
|
||||
|
||||
// Network socket (not hostLocal) — data goes through TCP via GetLocalSocket
|
||||
socket = new Socket(pNetworkPlayer, false, false);
|
||||
pNetworkPlayer->SetSocket(socket);
|
||||
|
||||
ClientConnection* connection = new ClientConnection(pMinecraft, socket, padIdx);
|
||||
if (connection->createdOk)
|
||||
{
|
||||
connection->send(shared_ptr<PreLoginPacket>(new PreLoginPacket(pNetworkPlayer->GetOnlineName())));
|
||||
pMinecraft->addPendingLocalConnection(padIdx, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMinecraft->connectionDisconnected(padIdx, DisconnectPacket::eDisconnect_ConnectionCreationFailed);
|
||||
delete connection;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
socket = new Socket( pNetworkPlayer, g_NetworkManager.IsHost(), g_NetworkManager.IsHost() && localPlayer );
|
||||
pNetworkPlayer->SetSocket( socket );
|
||||
|
||||
|
|
|
|||
|
|
@ -243,10 +243,16 @@ void CPlatformNetworkManagerStub::DoWork()
|
|||
if (IQNet::s_playerCount > 1)
|
||||
IQNet::s_playerCount--;
|
||||
}
|
||||
// Always return smallId to the free pool so it can be reused (game may have already cleared the slot).
|
||||
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
|
||||
// Clear O(1) socket lookup so GetSocketForSmallId stays fast (s_connections never shrinks).
|
||||
WinsockNetLayer::ClearSocketForSmallId(disconnectedSmallId);
|
||||
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
|
||||
// write thread may still be alive (it dies in PlayerList::tick when
|
||||
// m_smallIdsToClose is processed). If we recycle the smallId now,
|
||||
// AcceptThread can reuse it for a new connection, and the old write
|
||||
// thread's getPlayer() lookup will resolve to the NEW player, sending
|
||||
// stale game packets to the new client's TCP socket — corrupting its
|
||||
// login handshake (bad packet id crash). PushFreeSmallId and
|
||||
// ClearSocketForSmallId are called from PlayerList::tick after the
|
||||
// old Connection threads are dead.
|
||||
//
|
||||
// Clear chunk visibility flags for this system so rejoin gets fresh chunk state.
|
||||
SystemFlagRemoveBySmallId((int)disconnectedSmallId);
|
||||
}
|
||||
|
|
@ -289,12 +295,40 @@ int CPlatformNetworkManagerStub::GetLocalPlayerMask(int playerIndex)
|
|||
|
||||
bool CPlatformNetworkManagerStub::AddLocalPlayerByUserIndex( int userIndex )
|
||||
{
|
||||
NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(userIndex));
|
||||
return ( m_pIQNet->AddLocalPlayerByUserIndex(userIndex) == S_OK );
|
||||
if ( m_pIQNet->AddLocalPlayerByUserIndex(userIndex) != S_OK )
|
||||
return false;
|
||||
// Player is now registered in IQNet — get a pointer and notify the network layer.
|
||||
// Use the static array directly: GetLocalPlayerByUserIndex checks customData which
|
||||
// isn't set until addNetworkPlayer runs inside NotifyPlayerJoined.
|
||||
NotifyPlayerJoined(&IQNet::m_player[userIndex]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::RemoveLocalPlayerByUserIndex( int userIndex )
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (userIndex > 0 && userIndex < XUSER_MAX_COUNT && !m_pIQNet->IsHost())
|
||||
{
|
||||
IQNetPlayer* qp = &IQNet::m_player[userIndex];
|
||||
|
||||
// Notify the network layer before clearing the slot
|
||||
if (qp->GetCustomDataValue() != 0)
|
||||
{
|
||||
NotifyPlayerLeaving(qp);
|
||||
}
|
||||
|
||||
// Close the split-screen TCP connection and reset WinsockNetLayer state
|
||||
WinsockNetLayer::CloseSplitScreenConnection(userIndex);
|
||||
|
||||
// Clear the IQNet slot so it can be reused on rejoin
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -481,25 +481,15 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
|
|||
#endif
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
if (iPad == 0 && !g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
|
||||
{
|
||||
int deltaX = g_KBMInput.GetMouseDeltaX();
|
||||
int deltaY = g_KBMInput.GetMouseDeltaY();
|
||||
|
||||
extern HWND g_hWnd;
|
||||
RECT rc;
|
||||
GetClientRect(g_hWnd, &rc);
|
||||
int winW = rc.right - rc.left;
|
||||
int winH = rc.bottom - rc.top;
|
||||
|
||||
if (winW > 0 && winH > 0)
|
||||
{
|
||||
float scaleX = (float)getMovieWidth() / (float)winW;
|
||||
float scaleY = (float)getMovieHeight() / (float)winH;
|
||||
|
||||
vPointerPos.x += (float)deltaX * scaleX;
|
||||
vPointerPos.y += (float)deltaY * scaleY;
|
||||
}
|
||||
float scaleX, scaleY;
|
||||
getMouseToSWFScale(scaleX, scaleY);
|
||||
vPointerPos.x += (float)deltaX * scaleX;
|
||||
vPointerPos.y += (float)deltaY * scaleY;
|
||||
|
||||
if (deltaX != 0 || deltaY != 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -277,4 +277,5 @@ public:
|
|||
virtual int getPad() = 0;
|
||||
virtual int getMovieWidth() = 0;
|
||||
virtual int getMovieHeight() = 0;
|
||||
virtual void getMouseToSWFScale(float &scaleX, float &scaleY) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -250,15 +250,22 @@ rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacte
|
|||
|
||||
// Choose a reasonable glyph scale.
|
||||
float glyphScale = 1.0f, truePixelScale = 1.0f / m_cFontData->getFontData()->m_fAdvPerPixel;
|
||||
F32 targetPixelScale = pixel_scale;
|
||||
//if(!RenderManager.IsWidescreen())
|
||||
//{
|
||||
// // Fix for different scales in 480
|
||||
// targetPixelScale = pixel_scale*2/3;
|
||||
//}
|
||||
while ( (0.5f + glyphScale) * truePixelScale < targetPixelScale)
|
||||
while ( (0.5f + glyphScale) * truePixelScale < pixel_scale)
|
||||
glyphScale++;
|
||||
|
||||
// Debug: log each unique (font, pixel_scale) pair
|
||||
{
|
||||
static std::unordered_set<int> s_loggedScaleKeys;
|
||||
// Encode font pointer + quantized scale into a key to log each combo once
|
||||
int scaleKey = (int)(pixel_scale * 100.0f) ^ (int)(uintptr_t)m_cFontData;
|
||||
if (s_loggedScaleKeys.find(scaleKey) == s_loggedScaleKeys.end() && s_loggedScaleKeys.size() < 50) {
|
||||
s_loggedScaleKeys.insert(scaleKey);
|
||||
float tps = truePixelScale;
|
||||
app.DebugPrintf("[FONT-DBG] GetGlyphBitmap: font=%s glyph=%d pixel_scale=%.3f truePixelScale=%.1f glyphScale=%.0f\n",
|
||||
m_cFontData->getFontName().c_str(), glyph, pixel_scale, tps, glyphScale);
|
||||
}
|
||||
}
|
||||
|
||||
// 4J-JEV: Debug code to check which font sizes are being used.
|
||||
#if (!defined _CONTENT_PACKAGE) && (VERBOSE_FONT_OUTPUT > 0)
|
||||
|
||||
|
|
@ -303,9 +310,6 @@ rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacte
|
|||
}
|
||||
#endif
|
||||
|
||||
//app.DebugPrintf("Request glyph_%d (U+%.4X) at %f, converted to %f (%f)\n",
|
||||
// glyph, GetUnicode(glyph), pixel_scale, targetPixelScale, glyphScale);
|
||||
|
||||
// It is not necessary to shrink the glyph width here
|
||||
// as its already been done in 'GetGlyphMetrics' by:
|
||||
// > metrics->x1 = m_kerningTable[glyph] * ratio;
|
||||
|
|
@ -324,27 +328,57 @@ rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacte
|
|||
bitmap->top_left_y = -((S32) m_cFontData->getFontData()->m_uiGlyphHeight) * m_cFontData->getFontData()->m_fAscent;
|
||||
|
||||
bitmap->oversample = 0;
|
||||
bitmap->point_sample = true;
|
||||
|
||||
// 4J-JEV:
|
||||
// pixel_scale == font size chosen in flash.
|
||||
// bitmap->pixel_scale_correct = (float) m_glyphHeight; // Scales the glyph to desired size.
|
||||
// bitmap->pixel_scale_correct = pixel_scale; // Always the same size (not desired size).
|
||||
// bitmap->pixel_scale_correct = pixel_scale * 0.5; // Doubles original size.
|
||||
// bitmap->pixel_scale_correct = pixel_scale * 2; // Halves original size.
|
||||
|
||||
// Actual scale, and possible range of scales.
|
||||
bitmap->pixel_scale_correct = pixel_scale / glyphScale;
|
||||
bitmap->pixel_scale_max = 99.0f;
|
||||
bitmap->pixel_scale_min = 0.0f;
|
||||
|
||||
/* 4J-JEV: Some of Sean's code.
|
||||
int glyphScaleMin = 1;
|
||||
int glyphScaleMax = 3;
|
||||
float actualScale = pixel_scale / glyphScale;
|
||||
bitmap->pixel_scale_correct = actualScale;
|
||||
bitmap->pixel_scale_min = actualScale * glyphScaleMin * 0.999f;
|
||||
bitmap->pixel_scale_max = actualScale * glyphScaleMax * 1.001f; */
|
||||
#ifdef _WINDOWS64
|
||||
// On Windows64 the window can be any size, producing fractional
|
||||
// pixel_scale values that don't align to integer multiples of
|
||||
// truePixelScale. The original console code cached glyphs with a
|
||||
// broad [truePixelScale, 99] range in the "normal" branch, which
|
||||
// works on consoles (fixed 1080p — font sizes are exact multiples)
|
||||
// but causes cache pollution on Windows: the first glyph cached in
|
||||
// that range sets pixel_scale_correct for ALL subsequent requests,
|
||||
// so different font sizes get scaled by wrong ratios, producing
|
||||
// mixed letter sizes on screen.
|
||||
//
|
||||
// Fix: always use pixel_scale_correct = truePixelScale so every
|
||||
// cache entry is consistent. Two ranges: downscale (bilinear for
|
||||
// smooth reduction) and upscale (point_sample for crisp pixel-art).
|
||||
bitmap->pixel_scale_correct = truePixelScale;
|
||||
if (pixel_scale < truePixelScale)
|
||||
{
|
||||
bitmap->pixel_scale_min = 0.0f;
|
||||
bitmap->pixel_scale_max = truePixelScale;
|
||||
bitmap->point_sample = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap->pixel_scale_min = truePixelScale;
|
||||
bitmap->pixel_scale_max = 99.0f;
|
||||
bitmap->point_sample = true;
|
||||
}
|
||||
#else
|
||||
if (glyphScale <= 1 && pixel_scale < truePixelScale)
|
||||
{
|
||||
// Small display: pixel_scale is less than the native glyph size.
|
||||
// Report the bitmap at its true native scale so Iggy downscales it
|
||||
// to match the layout metrics (bilinear for smooth downscaling).
|
||||
bitmap->pixel_scale_correct = truePixelScale;
|
||||
bitmap->pixel_scale_min = 0.0f;
|
||||
bitmap->pixel_scale_max = truePixelScale * 1.001f;
|
||||
bitmap->point_sample = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal/upscale case: integer-multiple scaling for pixel-art look.
|
||||
// Console-only — fixed resolution means pixel_scale values are exact
|
||||
// integer multiples of truePixelScale, so cache sharing is safe.
|
||||
float actualScale = pixel_scale / glyphScale;
|
||||
bitmap->pixel_scale_correct = actualScale;
|
||||
bitmap->pixel_scale_min = truePixelScale;
|
||||
bitmap->pixel_scale_max = 99.0f;
|
||||
bitmap->point_sample = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 4J-JEV: Nothing to do with glyph placement,
|
||||
// entirely to do with cropping your glyph out of an archive.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIComponent_Chat.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
#include "..\..\Minecraft.h"
|
||||
#include "..\..\Gui.h"
|
||||
|
||||
|
|
@ -120,6 +121,7 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi
|
|||
S32 tileWidth = width;
|
||||
S32 tileHeight = height;
|
||||
|
||||
bool needsYTile = false;
|
||||
switch( viewport )
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
|
|
@ -127,25 +129,30 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi
|
|||
tileHeight = (S32)(ui.getScreenHeight());
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
|
||||
F32 scale;
|
||||
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
|
||||
|
||||
S32 contentOffX, contentOffY;
|
||||
ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY);
|
||||
xPos += contentOffX;
|
||||
yPos += contentOffY;
|
||||
ui.setupRenderPosition(xPos, yPos);
|
||||
|
||||
IggyPlayerDrawTilesStart ( getMovie() );
|
||||
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
IggyPlayerDrawTile ( getMovie() ,
|
||||
|
|
@ -153,7 +160,7 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi
|
|||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -68,24 +68,29 @@ void UIComponent_MenuBackground::render(S32 width, S32 height, C4JRender::eViewp
|
|||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
tileYStart = (S32)(ui.getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
tileYStart = (S32)(ui.getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
tileYStart = (S32)(ui.getScreenHeight() / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
|
||||
F32 scaleW = (F32)(tileXStart + tileWidth) / (F32)m_movieWidth;
|
||||
F32 scaleH = (F32)(tileYStart + tileHeight) / (F32)m_movieHeight;
|
||||
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
|
||||
if(scale < 1.0f) scale = 1.0f;
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
|
||||
|
||||
IggyPlayerDrawTilesStart ( getMovie() );
|
||||
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
IggyPlayerDrawTile ( getMovie() ,
|
||||
|
|
@ -93,11 +98,15 @@ void UIComponent_MenuBackground::render(S32 width, S32 height, C4JRender::eViewp
|
|||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
}
|
||||
else
|
||||
{
|
||||
UIScene::render(width, height, viewport);
|
||||
if(m_bIsReloading) return;
|
||||
if(!m_hasTickedOnce || !getMovie()) return;
|
||||
ui.setupRenderPosition(0, 0);
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)ui.getScreenWidth(), (S32)ui.getScreenHeight() );
|
||||
IggyPlayerDraw( getMovie() );
|
||||
}
|
||||
}
|
||||
|
|
@ -93,38 +93,47 @@ void UIComponent_Panorama::render(S32 width, S32 height, C4JRender::eViewportTyp
|
|||
}
|
||||
ui.setupRenderPosition(xPos, yPos);
|
||||
|
||||
if((viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT))
|
||||
S32 tileXStart = 0;
|
||||
S32 tileYStart = 0;
|
||||
S32 tileWidth = width;
|
||||
S32 tileHeight = height;
|
||||
|
||||
if((viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT))
|
||||
{
|
||||
// Need to render at full height, but only the left side of the scene
|
||||
S32 tileXStart = 0;
|
||||
S32 tileYStart = 0;
|
||||
S32 tileWidth = width;
|
||||
S32 tileHeight = (S32)(ui.getScreenHeight());
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
|
||||
|
||||
IggyPlayerDrawTilesStart ( getMovie() );
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
IggyPlayerDrawTile ( getMovie() ,
|
||||
tileXStart ,
|
||||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
tileHeight = (S32)(ui.getScreenHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Need to render at full height, and full width. But compressed into the viewport
|
||||
IggyPlayerSetDisplaySize( getMovie(), ui.getScreenWidth(), ui.getScreenHeight()/2 );
|
||||
IggyPlayerDraw( getMovie() );
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(ui.getScreenHeight() / 2);
|
||||
}
|
||||
|
||||
F32 scaleW = (F32)(tileXStart + tileWidth) / (F32)m_movieWidth;
|
||||
F32 scaleH = (F32)(tileYStart + tileHeight) / (F32)m_movieHeight;
|
||||
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
|
||||
if(scale < 1.0f) scale = 1.0f;
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
|
||||
|
||||
IggyPlayerDrawTilesStart ( getMovie() );
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
IggyPlayerDrawTile ( getMovie() ,
|
||||
tileXStart ,
|
||||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
}
|
||||
else
|
||||
{
|
||||
UIScene::render(width, height, viewport);
|
||||
if(m_bIsReloading) return;
|
||||
if(!m_hasTickedOnce || !getMovie()) return;
|
||||
ui.setupRenderPosition(0, 0);
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)ui.getScreenWidth(), (S32)ui.getScreenHeight() );
|
||||
IggyPlayerDraw( getMovie() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIComponent_Tooltips.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
|
||||
UIComponent_Tooltips::UIComponent_Tooltips(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
|
||||
{
|
||||
|
|
@ -224,6 +225,7 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp
|
|||
S32 tileWidth = width;
|
||||
S32 tileHeight = height;
|
||||
|
||||
bool needsYTile = false;
|
||||
switch( viewport )
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
|
|
@ -231,25 +233,30 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp
|
|||
tileHeight = (S32)(ui.getScreenHeight());
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
|
||||
F32 scale;
|
||||
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
|
||||
|
||||
S32 contentOffX, contentOffY;
|
||||
ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY);
|
||||
xPos += contentOffX;
|
||||
yPos += contentOffY;
|
||||
ui.setupRenderPosition(xPos, yPos);
|
||||
|
||||
IggyPlayerDrawTilesStart ( getMovie() );
|
||||
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
IggyPlayerDrawTile ( getMovie() ,
|
||||
|
|
@ -257,7 +264,7 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp
|
|||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "..\..\EnderDragonRenderer.h"
|
||||
#include "..\..\MultiPlayerLocalPlayer.h"
|
||||
#include "UIFontData.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
#ifdef _WINDOWS64
|
||||
#include "..\..\Windows64\KeyboardMouseInput.h"
|
||||
#endif
|
||||
|
|
@ -57,6 +58,8 @@ bool UIController::ms_bReloadSkinCSInitialised = false;
|
|||
|
||||
DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME;
|
||||
|
||||
// GetViewportRect and Fit16x9 are now in UISplitScreenHelpers.h
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
static UIControl_Slider *FindSliderById(UIScene *pScene, int sliderId)
|
||||
{
|
||||
|
|
@ -806,13 +809,16 @@ void UIController::tickInput()
|
|||
eUILayer_Fullscreen,
|
||||
eUILayer_Scene,
|
||||
};
|
||||
for (int l = 0; l < _countof(mouseLayers) && !pScene; ++l)
|
||||
// Only check the fullscreen group and the primary (KBM) player's group.
|
||||
// Other splitscreen players use controllers — mouse must not affect them.
|
||||
const int mouseGroups[] = { (int)eUIGroup_Fullscreen, ProfileManager.GetPrimaryPad() + 1 };
|
||||
for (int l = 0; l < _countof(mouseLayers) && !pScene; ++l)
|
||||
{
|
||||
for (int g = 0; g < _countof(mouseGroups) && !pScene; ++g)
|
||||
{
|
||||
for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp)
|
||||
{
|
||||
pScene = m_groups[grp]->GetTopScene(mouseLayers[l]);
|
||||
}
|
||||
pScene = m_groups[mouseGroups[g]]->GetTopScene(mouseLayers[l]);
|
||||
}
|
||||
}
|
||||
if (pScene && pScene->getMovie())
|
||||
{
|
||||
int rawMouseX = g_KBMInput.GetMouseX();
|
||||
|
|
@ -825,7 +831,12 @@ void UIController::tickInput()
|
|||
m_lastHoverMouseX = rawMouseX;
|
||||
m_lastHoverMouseY = rawMouseY;
|
||||
|
||||
// Convert mouse to scene/movie coordinates
|
||||
// Convert mouse window-pixel coords to Flash/SWF authoring coords.
|
||||
// In split-screen the scene is rendered at a tile-origin offset
|
||||
// and at a smaller display size, so we must:
|
||||
// 1. Map window pixels -> UIController screen space
|
||||
// 2. Subtract the viewport tile origin
|
||||
// 3. Scale from display dimensions to SWF authoring dimensions
|
||||
F32 sceneMouseX = (F32)rawMouseX;
|
||||
F32 sceneMouseY = (F32)rawMouseY;
|
||||
{
|
||||
|
|
@ -837,8 +848,30 @@ void UIController::tickInput()
|
|||
int winH = rc.bottom - rc.top;
|
||||
if (winW > 0 && winH > 0)
|
||||
{
|
||||
sceneMouseX = sceneMouseX * ((F32)pScene->getRenderWidth() / (F32)winW);
|
||||
sceneMouseY = sceneMouseY * ((F32)pScene->getRenderHeight() / (F32)winH);
|
||||
// Step 1: window pixels -> screen space
|
||||
F32 screenX = sceneMouseX * (getScreenWidth() / (F32)winW);
|
||||
F32 screenY = sceneMouseY * (getScreenHeight() / (F32)winH);
|
||||
|
||||
// Step 2 & 3: account for split-screen viewport
|
||||
C4JRender::eViewportType vp = pScene->GetParentLayer()->getViewport();
|
||||
S32 displayW = 0, displayH = 0;
|
||||
getRenderDimensions(vp, displayW, displayH);
|
||||
|
||||
F32 vpOriginX, vpOriginY, vpW, vpH;
|
||||
GetViewportRect(getScreenWidth(), getScreenHeight(), vp, vpOriginX, vpOriginY, vpW, vpH);
|
||||
// All viewports use Fit16x9 for menu scenes
|
||||
S32 fitW, fitH, fitOffsetX, fitOffsetY;
|
||||
Fit16x9(vpW, vpH, fitW, fitH, fitOffsetX, fitOffsetY);
|
||||
S32 originX = (S32)vpOriginX + fitOffsetX;
|
||||
S32 originY = (S32)vpOriginY + fitOffsetY;
|
||||
displayW = fitW;
|
||||
displayH = fitH;
|
||||
|
||||
if (displayW > 0 && displayH > 0)
|
||||
{
|
||||
sceneMouseX = (screenX - originX) * ((F32)pScene->getRenderWidth() / (F32)displayW);
|
||||
sceneMouseY = (screenY - originY) * ((F32)pScene->getRenderHeight() / (F32)displayH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1566,73 +1599,48 @@ void UIController::renderScenes()
|
|||
|
||||
void UIController::getRenderDimensions(C4JRender::eViewportType viewport, S32 &width, S32 &height)
|
||||
{
|
||||
switch( viewport )
|
||||
F32 originX, originY, viewW, viewH;
|
||||
GetViewportRect(getScreenWidth(), getScreenHeight(), viewport, originX, originY, viewW, viewH);
|
||||
|
||||
if(viewport == C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
|
||||
width = (S32)(getScreenWidth());
|
||||
height = (S32)(getScreenHeight());
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
width = (S32)(getScreenWidth() / 2);
|
||||
height = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
|
||||
width = (S32)(getScreenWidth() / 2);
|
||||
height = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
width = (S32)(getScreenWidth() / 2);
|
||||
height = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
S32 offsetX, offsetY;
|
||||
Fit16x9(viewW, viewH, width, height, offsetX, offsetY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split-screen: use raw viewport dims — the SWF tiling code handles non-16:9
|
||||
width = (S32)viewW;
|
||||
height = (S32)viewH;
|
||||
}
|
||||
}
|
||||
|
||||
void UIController::setupRenderPosition(C4JRender::eViewportType viewport)
|
||||
{
|
||||
if(m_bCustomRenderPosition || m_currentRenderViewport != viewport)
|
||||
m_currentRenderViewport = viewport;
|
||||
m_bCustomRenderPosition = false;
|
||||
|
||||
F32 vpOriginX, vpOriginY, vpW, vpH;
|
||||
GetViewportRect(getScreenWidth(), getScreenHeight(), viewport, vpOriginX, vpOriginY, vpW, vpH);
|
||||
|
||||
S32 xPos, yPos;
|
||||
if(viewport == C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
{
|
||||
m_currentRenderViewport = viewport;
|
||||
m_bCustomRenderPosition = false;
|
||||
S32 xPos = 0;
|
||||
S32 yPos = 0;
|
||||
switch( viewport )
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
xPos = (S32)(getScreenWidth() / 4);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
xPos = (S32)(getScreenWidth() / 4);
|
||||
yPos = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
yPos = (S32)(getScreenHeight() / 4);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
|
||||
xPos = (S32)(getScreenWidth() / 2);
|
||||
yPos = (S32)(getScreenHeight() / 4);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
xPos = (S32)(getScreenWidth() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
yPos = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
xPos = (S32)(getScreenWidth() / 2);
|
||||
yPos = (S32)(getScreenHeight() / 2);
|
||||
break;
|
||||
}
|
||||
m_tileOriginX = xPos;
|
||||
m_tileOriginY = yPos;
|
||||
setTileOrigin(xPos, yPos);
|
||||
S32 fitW, fitH, fitOffsetX, fitOffsetY;
|
||||
Fit16x9(vpW, vpH, fitW, fitH, fitOffsetX, fitOffsetY);
|
||||
xPos = (S32)vpOriginX + fitOffsetX;
|
||||
yPos = (S32)vpOriginY + fitOffsetY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split-screen: position at viewport origin, no 16:9 fitting
|
||||
xPos = (S32)vpOriginX;
|
||||
yPos = (S32)vpOriginY;
|
||||
}
|
||||
|
||||
m_tileOriginX = xPos;
|
||||
m_tileOriginY = yPos;
|
||||
setTileOrigin(xPos, yPos);
|
||||
}
|
||||
|
||||
void UIController::setupRenderPosition(S32 xOrigin, S32 yOrigin)
|
||||
|
|
@ -1840,8 +1848,11 @@ void RADLINK UIController::TextureSubstitutionDestroyCallback ( void * user_call
|
|||
|
||||
ui.destroySubstitutionTexture(user_callback_data, handle);
|
||||
|
||||
Textures *t = Minecraft::GetInstance()->textures;
|
||||
t->releaseTexture( id );
|
||||
Minecraft* mc = Minecraft::GetInstance();
|
||||
if (mc && mc->textures)
|
||||
{
|
||||
mc->textures->releaseTexture( id );
|
||||
}
|
||||
}
|
||||
|
||||
void UIController::registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength)
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ public:
|
|||
// RENDERING
|
||||
float getScreenWidth() { return m_fScreenWidth; }
|
||||
float getScreenHeight() { return m_fScreenHeight; }
|
||||
void updateScreenSize(S32 w, S32 h) { m_fScreenWidth = (float)w; m_fScreenHeight = (float)h; app.DebugPrintf("[UI-INIT] updateScreenSize: %d x %d\n", w, h); }
|
||||
|
||||
virtual void render() = 0;
|
||||
void getRenderDimensions(C4JRender::eViewportType viewport, S32 &width, S32 &height);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIScene.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
|
||||
#include "..\..\Lighting.h"
|
||||
#include "..\..\LocalPlayer.h"
|
||||
|
|
@ -285,26 +286,8 @@ void UIScene::loadMovie()
|
|||
moviePath.append(L"Vita.swf");
|
||||
m_loadedResolution = eSceneResolution_Vita;
|
||||
#elif defined _WINDOWS64
|
||||
if(ui.getScreenHeight() == 720)
|
||||
{
|
||||
moviePath.append(L"720.swf");
|
||||
m_loadedResolution = eSceneResolution_720;
|
||||
}
|
||||
else if(ui.getScreenHeight() == 480)
|
||||
{
|
||||
moviePath.append(L"480.swf");
|
||||
m_loadedResolution = eSceneResolution_480;
|
||||
}
|
||||
else if(ui.getScreenHeight() < 720)
|
||||
{
|
||||
moviePath.append(L"Vita.swf");
|
||||
m_loadedResolution = eSceneResolution_Vita;
|
||||
}
|
||||
else
|
||||
{
|
||||
moviePath.append(L"1080.swf");
|
||||
m_loadedResolution = eSceneResolution_1080;
|
||||
}
|
||||
moviePath.append(L"1080.swf");
|
||||
m_loadedResolution = eSceneResolution_1080;
|
||||
#else
|
||||
moviePath.append(L"1080.swf");
|
||||
m_loadedResolution = eSceneResolution_1080;
|
||||
|
|
@ -332,8 +315,6 @@ void UIScene::loadMovie()
|
|||
int64_t beforeLoad = ui.iggyAllocCount;
|
||||
swf = IggyPlayerCreateFromMemory ( baFile.data , baFile.length, NULL);
|
||||
int64_t afterLoad = ui.iggyAllocCount;
|
||||
IggyPlayerInitializeAndTickRS ( swf );
|
||||
int64_t afterTick = ui.iggyAllocCount;
|
||||
|
||||
if(!swf)
|
||||
{
|
||||
|
|
@ -343,17 +324,44 @@ void UIScene::loadMovie()
|
|||
#endif
|
||||
app.FatalLoadError();
|
||||
}
|
||||
app.DebugPrintf( app.USER_SR, "Loaded iggy movie %ls\n", moviePath.c_str() );
|
||||
|
||||
// Read movie dimensions from the SWF header (available immediately after
|
||||
// CreateFromMemory, no init tick needed).
|
||||
IggyProperties *properties = IggyPlayerProperties ( swf );
|
||||
m_movieHeight = properties->movie_height_in_pixels;
|
||||
m_movieWidth = properties->movie_width_in_pixels;
|
||||
|
||||
m_renderWidth = m_movieWidth;
|
||||
m_renderHeight = m_movieHeight;
|
||||
|
||||
S32 width, height;
|
||||
m_parentLayer->getRenderDimensions(width, height);
|
||||
IggyPlayerSetDisplaySize( swf, width, height );
|
||||
// Set display size BEFORE the init tick to match what render() will use.
|
||||
// InitializeAndTickRS runs ActionScript that creates text fields. If the
|
||||
// display size here differs from what render() passes to SetDisplaySize,
|
||||
// Iggy can cache glyph rasterizations at one scale during init and then
|
||||
// reuse them at a different scale during draw, producing mixed glyph sizes.
|
||||
#ifdef _WINDOWS64
|
||||
{
|
||||
S32 fitW, fitH, fitOffX, fitOffY;
|
||||
Fit16x9(ui.getScreenWidth(), ui.getScreenHeight(), fitW, fitH, fitOffX, fitOffY);
|
||||
IggyPlayerSetDisplaySize( swf, fitW, fitH );
|
||||
}
|
||||
#else
|
||||
IggyPlayerSetDisplaySize( swf, m_movieWidth, m_movieHeight );
|
||||
#endif
|
||||
|
||||
IggyPlayerInitializeAndTickRS ( swf );
|
||||
int64_t afterTick = ui.iggyAllocCount;
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
// Flush Iggy's internal font caches so all glyphs get rasterized fresh
|
||||
// at the current display scale on the first Draw. Without this, stale
|
||||
// cache entries from a previous scene (loaded at a different display size)
|
||||
// cause mixed glyph sizes. ResizeD3D already calls this, which is why
|
||||
// fonts look correct after a resize but break when a scene reloads
|
||||
// without one.
|
||||
IggyFlushInstalledFonts();
|
||||
#endif
|
||||
|
||||
app.DebugPrintf( app.USER_SR, "Loaded iggy movie %ls\n", moviePath.c_str() );
|
||||
|
||||
IggyPlayerSetUserdata(swf,this);
|
||||
|
||||
|
|
@ -685,9 +693,23 @@ void UIScene::render(S32 width, S32 height, C4JRender::eViewportType viewport)
|
|||
{
|
||||
if(m_bIsReloading) return;
|
||||
if(!m_hasTickedOnce || !swf) return;
|
||||
ui.setupRenderPosition(viewport);
|
||||
IggyPlayerSetDisplaySize( swf, width, height );
|
||||
IggyPlayerDraw( swf );
|
||||
|
||||
if(viewport != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
{
|
||||
F32 originX, originY, viewW, viewH;
|
||||
GetViewportRect(ui.getScreenWidth(), ui.getScreenHeight(), viewport, originX, originY, viewW, viewH);
|
||||
S32 fitW, fitH, offsetX, offsetY;
|
||||
Fit16x9(viewW, viewH, fitW, fitH, offsetX, offsetY);
|
||||
ui.setupRenderPosition((S32)originX + offsetX, (S32)originY + offsetY);
|
||||
IggyPlayerSetDisplaySize( swf, fitW, fitH );
|
||||
IggyPlayerDraw( swf );
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.setupRenderPosition(viewport);
|
||||
IggyPlayerSetDisplaySize( swf, width, height );
|
||||
IggyPlayerDraw( swf );
|
||||
}
|
||||
}
|
||||
|
||||
void UIScene::setOpacity(float percent)
|
||||
|
|
@ -1331,11 +1353,17 @@ bool UIScene::hasRegisteredSubstitutionTexture(const wstring &textureName)
|
|||
|
||||
void UIScene::_handleFocusChange(F64 controlId, F64 childId)
|
||||
{
|
||||
m_iFocusControl = (int)controlId;
|
||||
m_iFocusChild = (int)childId;
|
||||
int newControl = (int)controlId;
|
||||
int newChild = (int)childId;
|
||||
|
||||
handleFocusChange(controlId, childId);
|
||||
ui.PlayUISFX(eSFX_Focus);
|
||||
if (newControl != m_iFocusControl || newChild != m_iFocusChild)
|
||||
{
|
||||
m_iFocusControl = newControl;
|
||||
m_iFocusChild = newChild;
|
||||
|
||||
handleFocusChange(controlId, childId);
|
||||
ui.PlayUISFX(eSFX_Focus);
|
||||
}
|
||||
}
|
||||
|
||||
void UIScene::_handleInitFocus(F64 controlId, F64 childId)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIScene_AbstractContainerMenu.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
|
||||
#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h"
|
||||
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
|
||||
|
|
@ -187,14 +188,19 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex)
|
|||
IggyEvent mouseEvent;
|
||||
S32 width, height;
|
||||
m_parentLayer->getRenderDimensions(width, height);
|
||||
|
||||
C4JRender::eViewportType vp = m_parentLayer->getViewport();
|
||||
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
Fit16x9(width, height);
|
||||
|
||||
S32 x = m_pointerPos.x*((float)width/m_movieWidth);
|
||||
S32 y = m_pointerPos.y*((float)height/m_movieHeight);
|
||||
S32 y = m_pointerPos.y*((float)height/m_movieHeight);
|
||||
IggyMakeEventMouseMove( &mouseEvent, x, y);
|
||||
|
||||
IggyEventResult result;
|
||||
IggyPlayerDispatchEventRS ( getMovie() , &mouseEvent , &result );
|
||||
|
||||
#ifdef USE_POINTER_ACCEL
|
||||
#ifdef USE_POINTER_ACCEL
|
||||
m_fPointerVelX = 0.0f;
|
||||
m_fPointerVelY = 0.0f;
|
||||
m_fPointerAccelX = 0.0f;
|
||||
|
|
@ -212,6 +218,10 @@ void UIScene_AbstractContainerMenu::tick()
|
|||
S32 width, height;
|
||||
m_parentLayer->getRenderDimensions(width, height);
|
||||
|
||||
C4JRender::eViewportType vp = m_parentLayer->getViewport();
|
||||
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
Fit16x9(width, height);
|
||||
|
||||
S32 x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth));
|
||||
S32 y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight));
|
||||
|
||||
|
|
@ -251,6 +261,27 @@ void UIScene_AbstractContainerMenu::render(S32 width, S32 height, C4JRender::eVi
|
|||
m_needsCacheRendered = false;
|
||||
}
|
||||
|
||||
void UIScene_AbstractContainerMenu::getMouseToSWFScale(float &scaleX, float &scaleY)
|
||||
{
|
||||
extern HWND g_hWnd;
|
||||
RECT rc;
|
||||
GetClientRect(g_hWnd, &rc);
|
||||
int winW = rc.right - rc.left;
|
||||
int winH = rc.bottom - rc.top;
|
||||
if(winW <= 0 || winH <= 0) { scaleX = 1.0f; scaleY = 1.0f; return; }
|
||||
|
||||
S32 renderW, renderH;
|
||||
C4JRender::eViewportType vp = GetParentLayer()->getViewport();
|
||||
ui.getRenderDimensions(vp, renderW, renderH);
|
||||
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
|
||||
Fit16x9(renderW, renderH);
|
||||
|
||||
float screenW = (float)ui.getScreenWidth();
|
||||
float screenH = (float)ui.getScreenHeight();
|
||||
scaleX = (float)m_movieWidth * screenW / ((float)renderW * (float)winW);
|
||||
scaleY = (float)m_movieHeight * screenH / ((float)renderH * (float)winH);
|
||||
}
|
||||
|
||||
void UIScene_AbstractContainerMenu::customDraw(IggyCustomDrawCallbackRegion *region)
|
||||
{
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public:
|
|||
int getPad() { return m_iPad; }
|
||||
int getMovieWidth() { return m_movieWidth; }
|
||||
int getMovieHeight() { return m_movieHeight; }
|
||||
void getMouseToSWFScale(float &scaleX, float &scaleY);
|
||||
bool getIgnoreInput() { return m_bIgnoreInput; }
|
||||
void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "UI.h"
|
||||
#include "UIScene_HUD.h"
|
||||
#include "UISplitScreenHelpers.h"
|
||||
#include "BossMobGuiInfo.h"
|
||||
#include "..\..\Minecraft.h"
|
||||
#include "..\..\MultiplayerLocalPlayer.h"
|
||||
|
|
@ -266,8 +267,6 @@ void UIScene_HUD::handleReload()
|
|||
|
||||
SetDisplayName(ProfileManager.GetDisplayName(m_iPad));
|
||||
|
||||
repositionHud();
|
||||
|
||||
SetTooltipsEnabled(((ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad())) || (app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Tooltips) != 0)));
|
||||
}
|
||||
|
||||
|
|
@ -697,6 +696,7 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
|
|||
S32 tileWidth = width;
|
||||
S32 tileHeight = height;
|
||||
|
||||
bool needsYTile = false;
|
||||
switch( viewport )
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
|
|
@ -704,23 +704,25 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
|
|||
tileHeight = (S32)(ui.getScreenHeight());
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
tileWidth = (S32)(ui.getScreenWidth());
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
tileYStart = (S32)(m_movieHeight / 2);
|
||||
needsYTile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
|
||||
|
||||
F32 scale;
|
||||
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
|
||||
|
||||
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
|
||||
|
||||
repositionHud(tileWidth, tileHeight, scale);
|
||||
|
||||
m_renderWidth = tileWidth;
|
||||
m_renderHeight = tileHeight;
|
||||
|
||||
|
|
@ -730,7 +732,7 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
|
|||
tileYStart ,
|
||||
tileXStart + tileWidth ,
|
||||
tileYStart + tileHeight ,
|
||||
0 );
|
||||
0 );
|
||||
IggyPlayerDrawTilesEnd ( getMovie() );
|
||||
}
|
||||
else
|
||||
|
|
@ -790,34 +792,24 @@ void UIScene_HUD::handleTimerComplete(int id)
|
|||
//setVisible(anyVisible);
|
||||
}
|
||||
|
||||
void UIScene_HUD::repositionHud()
|
||||
void UIScene_HUD::repositionHud(S32 tileWidth, S32 tileHeight, F32 scale)
|
||||
{
|
||||
if(!m_bSplitscreen) return;
|
||||
|
||||
S32 width = 0;
|
||||
S32 height = 0;
|
||||
m_parentLayer->getRenderDimensions( width, height );
|
||||
// Pass the visible tile area in SWF coordinates so ActionScript
|
||||
// positions elements (crosshair, hotbar, etc.) centered in the
|
||||
// actually visible region, not the raw viewport.
|
||||
S32 visibleW = (S32)(tileWidth / scale);
|
||||
S32 visibleH = (S32)(tileHeight / scale);
|
||||
|
||||
switch( m_parentLayer->getViewport() )
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
|
||||
height = (S32)(ui.getScreenHeight());
|
||||
break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
width = (S32)(ui.getScreenWidth());
|
||||
break;
|
||||
}
|
||||
|
||||
app.DebugPrintf(app.USER_SR, "Reposition HUD with dims %d, %d\n", width, height );
|
||||
app.DebugPrintf(app.USER_SR, "Reposition HUD: tile %dx%d, scale %.3f, visible SWF %dx%d\n", tileWidth, tileHeight, scale, visibleW, visibleH );
|
||||
|
||||
IggyDataValue result;
|
||||
IggyDataValue value[2];
|
||||
value[0].type = IGGY_DATATYPE_number;
|
||||
value[0].number = width;
|
||||
value[0].number = visibleW;
|
||||
value[1].type = IGGY_DATATYPE_number;
|
||||
value[1].number = height;
|
||||
value[1].number = visibleH;
|
||||
IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcRepositionHud , 2 , value );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,5 +176,5 @@ protected:
|
|||
#endif
|
||||
|
||||
private:
|
||||
void repositionHud();
|
||||
void repositionHud(S32 tileWidth, S32 tileHeight, F32 scale);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ UIScene_Keyboard::UIScene_Keyboard(int iPad, void *initData, UILayer *parentLaye
|
|||
}
|
||||
|
||||
m_win64TextBuffer = defaultText;
|
||||
m_iCursorPos = (int)m_win64TextBuffer.length();
|
||||
|
||||
m_EnterTextLabel.init(titleText);
|
||||
m_KeyboardTextInput.init(defaultText, -1);
|
||||
|
|
@ -111,6 +112,9 @@ UIScene_Keyboard::UIScene_Keyboard(int iPad, void *initData, UILayer *parentLaye
|
|||
if (IggyValuePathMakeNameRef(&keyPath, root, s_keyNames[i]))
|
||||
IggyValueSetBooleanRS(&keyPath, nameVisible, NULL, false);
|
||||
}
|
||||
|
||||
m_KeyboardTextInput.setCaretVisible(true);
|
||||
m_KeyboardTextInput.setCaretIndex(m_iCursorPos);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -165,9 +169,13 @@ void UIScene_Keyboard::tick()
|
|||
|
||||
// Sync our buffer from Flash so we pick up changes made via controller/on-screen buttons.
|
||||
// Without this, switching between controller and keyboard would use stale text.
|
||||
const wchar_t* flashText = m_KeyboardTextInput.getLabel();
|
||||
if (flashText)
|
||||
m_win64TextBuffer = flashText;
|
||||
// In PC mode we own the buffer — skip sync to preserve cursor position.
|
||||
if (!m_bPCMode)
|
||||
{
|
||||
const wchar_t* flashText = m_KeyboardTextInput.getLabel();
|
||||
if (flashText)
|
||||
m_win64TextBuffer = flashText;
|
||||
}
|
||||
|
||||
// Accumulate physical keyboard chars into our own buffer, then push to Flash via setLabel.
|
||||
// This bypasses Iggy's focus system (char events only route to the focused element).
|
||||
|
|
@ -178,7 +186,16 @@ void UIScene_Keyboard::tick()
|
|||
{
|
||||
if (ch == 0x08) // backspace
|
||||
{
|
||||
if (!m_win64TextBuffer.empty())
|
||||
if (m_bPCMode)
|
||||
{
|
||||
if (m_iCursorPos > 0)
|
||||
{
|
||||
m_win64TextBuffer.erase(m_iCursorPos - 1, 1);
|
||||
m_iCursorPos--;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (!m_win64TextBuffer.empty())
|
||||
{
|
||||
m_win64TextBuffer.pop_back();
|
||||
changed = true;
|
||||
|
|
@ -194,13 +211,45 @@ void UIScene_Keyboard::tick()
|
|||
}
|
||||
else if ((int)m_win64TextBuffer.length() < m_win64MaxChars)
|
||||
{
|
||||
m_win64TextBuffer += ch;
|
||||
if (m_bPCMode)
|
||||
{
|
||||
m_win64TextBuffer.insert(m_iCursorPos, 1, ch);
|
||||
m_iCursorPos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_win64TextBuffer += ch;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bPCMode)
|
||||
{
|
||||
// Arrow keys, Home, End, Delete for cursor movement
|
||||
if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0)
|
||||
m_iCursorPos--;
|
||||
if (g_KBMInput.IsKeyPressed(VK_RIGHT) && m_iCursorPos < (int)m_win64TextBuffer.length())
|
||||
m_iCursorPos++;
|
||||
if (g_KBMInput.IsKeyPressed(VK_HOME))
|
||||
m_iCursorPos = 0;
|
||||
if (g_KBMInput.IsKeyPressed(VK_END))
|
||||
m_iCursorPos = (int)m_win64TextBuffer.length();
|
||||
if (g_KBMInput.IsKeyPressed(VK_DELETE) && m_iCursorPos < (int)m_win64TextBuffer.length())
|
||||
{
|
||||
m_win64TextBuffer.erase(m_iCursorPos, 1);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
m_KeyboardTextInput.setLabel(m_win64TextBuffer.c_str(), true /*instant*/);
|
||||
|
||||
if (m_bPCMode)
|
||||
{
|
||||
m_KeyboardTextInput.setCaretVisible(true);
|
||||
m_KeyboardTextInput.setCaretIndex(m_iCursorPos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -286,7 +335,10 @@ void UIScene_Keyboard::handleInput(int iPad, int key, bool repeat, bool pressed,
|
|||
case ACTION_MENU_RIGHT:
|
||||
case ACTION_MENU_UP:
|
||||
case ACTION_MENU_DOWN:
|
||||
sendInputToMovie(key, repeat, pressed, released);
|
||||
#ifdef _WINDOWS64
|
||||
if (!m_bPCMode)
|
||||
#endif
|
||||
sendInputToMovie(key, repeat, pressed, released);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ private:
|
|||
wstring m_win64TextBuffer;
|
||||
int m_win64MaxChars;
|
||||
bool m_bPCMode; // Hides on-screen keyboard buttons; physical keyboard only
|
||||
int m_iCursorPos;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
|
|
|||
114
Minecraft.Client/Common/UI/UISplitScreenHelpers.h
Normal file
114
Minecraft.Client/Common/UI/UISplitScreenHelpers.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
// Shared split-screen UI helpers to avoid duplicating viewport math
|
||||
// across HUD, Chat, Tooltips, and container menus.
|
||||
|
||||
// Compute the raw viewport rectangle for a given viewport type.
|
||||
inline void GetViewportRect(F32 screenW, F32 screenH, C4JRender::eViewportType viewport,
|
||||
F32 &originX, F32 &originY, F32 &viewW, F32 &viewH)
|
||||
{
|
||||
originX = originY = 0;
|
||||
viewW = screenW;
|
||||
viewH = screenH;
|
||||
switch(viewport)
|
||||
{
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
|
||||
viewH = screenH * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
|
||||
originY = screenH * 0.5f; viewH = screenH * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
|
||||
viewW = screenW * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
|
||||
originX = screenW * 0.5f; viewW = screenW * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
|
||||
viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
|
||||
originX = screenW * 0.5f; viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
|
||||
originY = screenH * 0.5f; viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
|
||||
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
|
||||
originX = screenW * 0.5f; originY = screenH * 0.5f;
|
||||
viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fit a 16:9 rectangle inside the given dimensions.
|
||||
inline void Fit16x9(F32 viewW, F32 viewH, S32 &fitW, S32 &fitH, S32 &offsetX, S32 &offsetY)
|
||||
{
|
||||
const F32 kAspect = 16.0f / 9.0f;
|
||||
if(viewW / viewH > kAspect)
|
||||
{
|
||||
fitH = (S32)viewH;
|
||||
fitW = (S32)(viewH * kAspect);
|
||||
}
|
||||
else
|
||||
{
|
||||
fitW = (S32)viewW;
|
||||
fitH = (S32)(viewW / kAspect);
|
||||
}
|
||||
offsetX = (S32)((viewW - fitW) * 0.5f);
|
||||
offsetY = (S32)((viewH - fitH) * 0.5f);
|
||||
}
|
||||
|
||||
// Convenience: just fit 16:9 dimensions, ignore offsets.
|
||||
inline void Fit16x9(S32 &width, S32 &height)
|
||||
{
|
||||
S32 offX, offY;
|
||||
Fit16x9((F32)width, (F32)height, width, height, offX, offY);
|
||||
}
|
||||
|
||||
// Compute the uniform scale and tileYStart for split-screen tile rendering.
|
||||
// Used by HUD, Chat, and Tooltips to scale the SWF movie to cover the viewport tile.
|
||||
inline void ComputeTileScale(S32 tileWidth, S32 tileHeight, S32 movieWidth, S32 movieHeight,
|
||||
bool needsYTile, F32 &outScale, S32 &outTileYStart)
|
||||
{
|
||||
F32 scaleW = (F32)tileWidth / (F32)movieWidth;
|
||||
F32 scaleH = (F32)tileHeight / (F32)movieHeight;
|
||||
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
|
||||
if(scale < 1.0f) scale = 1.0f;
|
||||
|
||||
outTileYStart = 0;
|
||||
if(needsYTile)
|
||||
{
|
||||
S32 dispH = (S32)(movieHeight * scale);
|
||||
outTileYStart = dispH - tileHeight;
|
||||
if(outTileYStart < 0) outTileYStart = 0;
|
||||
scaleH = (F32)(outTileYStart + tileHeight) / (F32)movieHeight;
|
||||
scale = (scaleW > scaleH) ? scaleW : scaleH;
|
||||
if(scale < 1.0f) scale = 1.0f;
|
||||
}
|
||||
|
||||
outScale = scale;
|
||||
}
|
||||
|
||||
// Compute the render offset to center split-screen SWF content in the viewport.
|
||||
// Used by Chat and Tooltips (HUD uses repositionHud instead).
|
||||
inline void ComputeSplitContentOffset(C4JRender::eViewportType viewport, S32 movieWidth, S32 movieHeight,
|
||||
F32 scale, S32 tileWidth, S32 tileHeight, S32 tileYStart,
|
||||
S32 &outXOffset, S32 &outYOffset)
|
||||
{
|
||||
S32 contentCenterX, contentCenterY;
|
||||
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT || viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT)
|
||||
{
|
||||
contentCenterX = (S32)(movieWidth * scale / 4);
|
||||
contentCenterY = (S32)(movieHeight * scale / 2);
|
||||
}
|
||||
else if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_TOP || viewport == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM)
|
||||
{
|
||||
contentCenterX = (S32)(movieWidth * scale / 2);
|
||||
contentCenterY = (S32)(movieHeight * scale * 3 / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
contentCenterX = (S32)(movieWidth * scale / 4);
|
||||
contentCenterY = (S32)(movieHeight * scale * 3 / 4);
|
||||
}
|
||||
|
||||
outXOffset = 0;
|
||||
outYOffset = 0;
|
||||
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT)
|
||||
outXOffset = -(tileWidth / 2 - contentCenterX);
|
||||
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_TOP || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT)
|
||||
outYOffset = -(tileHeight / 2 - (contentCenterY - tileYStart));
|
||||
}
|
||||
|
|
@ -234,6 +234,7 @@ void EntityRenderDispatcher::prepare(Level *level, Textures *textures, Font *fon
|
|||
if (pl->ThirdPersonView() == 2)
|
||||
{
|
||||
playerRotY += 180;
|
||||
playerRotX = -playerRotX;
|
||||
}
|
||||
|
||||
xPlayer = player->xOld + (player->x - player->xOld) * a;
|
||||
|
|
|
|||
|
|
@ -192,7 +192,16 @@ void IQNetPlayer::SendData(IQNetPlayer * player, const void* pvData, DWORD dwDat
|
|||
{
|
||||
if (WinsockNetLayer::IsActive())
|
||||
{
|
||||
WinsockNetLayer::SendToSmallId(player->m_smallId, pvData, dwDataSize);
|
||||
if (!WinsockNetLayer::IsHosting() && !m_isRemote)
|
||||
{
|
||||
SOCKET sock = WinsockNetLayer::GetLocalSocket(m_smallId);
|
||||
if (sock != INVALID_SOCKET)
|
||||
WinsockNetLayer::SendOnSocket(sock, pvData, dwDataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
WinsockNetLayer::SendToSmallId(player->m_smallId, pvData, dwDataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool IQNetPlayer::IsSameSystem(IQNetPlayer * player) { return (this == player) || (!m_isRemote && !player->m_isRemote); }
|
||||
|
|
@ -243,7 +252,20 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost
|
|||
|
||||
static bool Win64_IsActivePlayer(IQNetPlayer* p, DWORD index);
|
||||
|
||||
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) { return S_OK; }
|
||||
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) {
|
||||
if (dwUserIndex >= MINECRAFT_NET_MAX_PLAYERS) return E_FAIL;
|
||||
m_player[dwUserIndex].m_isRemote = false;
|
||||
m_player[dwUserIndex].m_isHostPlayer = false;
|
||||
// Give the joining player a distinct gamertag
|
||||
extern wchar_t g_Win64UsernameW[17];
|
||||
if (dwUserIndex == 0)
|
||||
wcscpy_s(m_player[0].m_gamertag, 32, g_Win64UsernameW);
|
||||
else
|
||||
swprintf_s(m_player[dwUserIndex].m_gamertag, 32, L"%s(%d)", g_Win64UsernameW, dwUserIndex + 1);
|
||||
if (dwUserIndex >= s_playerCount)
|
||||
s_playerCount = dwUserIndex + 1;
|
||||
return S_OK;
|
||||
}
|
||||
IQNetPlayer* IQNet::GetHostPlayer() { return &m_player[0]; }
|
||||
IQNetPlayer* IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex)
|
||||
{
|
||||
|
|
@ -255,13 +277,31 @@ IQNetPlayer* IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex)
|
|||
return &m_player[dwUserIndex];
|
||||
return NULL;
|
||||
}
|
||||
if (dwUserIndex != 0)
|
||||
return NULL;
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
if (dwUserIndex == 0)
|
||||
{
|
||||
if (!m_player[i].m_isRemote && Win64_IsActivePlayer(&m_player[i], i))
|
||||
return &m_player[i];
|
||||
// Primary pad: use direct index when networking is active (smallId may not be 0)
|
||||
if (WinsockNetLayer::IsActive())
|
||||
{
|
||||
DWORD idx = WinsockNetLayer::GetLocalSmallId();
|
||||
if (idx < MINECRAFT_NET_MAX_PLAYERS &&
|
||||
!m_player[idx].m_isRemote &&
|
||||
Win64_IsActivePlayer(&m_player[idx], idx))
|
||||
return &m_player[idx];
|
||||
return NULL;
|
||||
}
|
||||
// Offline: scan for first local player
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
{
|
||||
if (!m_player[i].m_isRemote && Win64_IsActivePlayer(&m_player[i], i))
|
||||
return &m_player[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// Split-screen pads 1-3: the player is at m_player[dwUserIndex] with isRemote=false
|
||||
if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS &&
|
||||
!m_player[dwUserIndex].m_isRemote &&
|
||||
Win64_IsActivePlayer(&m_player[dwUserIndex], dwUserIndex))
|
||||
return &m_player[dwUserIndex];
|
||||
return NULL;
|
||||
}
|
||||
static bool Win64_IsActivePlayer(IQNetPlayer * p, DWORD index)
|
||||
|
|
@ -582,7 +622,7 @@ void C_4JProfile::SetTrialTextStringTable(CXuiStringTable * pStringTable, int
|
|||
void C_4JProfile::SetTrialAwardText(eAwardType AwardType, int iTitle, int iText) {}
|
||||
int C_4JProfile::GetLockedProfile() { return 0; }
|
||||
void C_4JProfile::SetLockedProfile(int iProf) {}
|
||||
bool C_4JProfile::IsSignedIn(int iQuadrant) { return (iQuadrant == 0); }
|
||||
bool C_4JProfile::IsSignedIn(int iQuadrant) { return (iQuadrant == 0) || InputManager.IsPadConnected(iQuadrant); }
|
||||
bool C_4JProfile::IsSignedInLive(int iProf) { return true; }
|
||||
bool C_4JProfile::IsGuest(int iQuadrant) { return false; }
|
||||
UINT C_4JProfile::RequestSignInUI(bool bFromInvite, bool bLocalGame, bool bNoGuestsAllowed, bool bMultiplayerSignIn, bool bAddUser, int(*Func)(LPVOID, const bool, const int iPad), LPVOID lpParam, int iQuadrant) { return 0; }
|
||||
|
|
@ -593,18 +633,10 @@ bool C_4JProfile::QuerySigninStatus(void) { return true; }
|
|||
void C_4JProfile::GetXUID(int iPad, PlayerUID * pXuid, bool bOnlineXuid)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (iPad != 0)
|
||||
{
|
||||
*pXuid = INVALID_XUID;
|
||||
return;
|
||||
}
|
||||
// LoginPacket reads this value as client identity:
|
||||
// - host keeps legacy host XUID for world compatibility
|
||||
// - non-host uses persistent uid.dat-backed XUID
|
||||
if (IQNet::s_isHosting)
|
||||
*pXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
|
||||
else
|
||||
*pXuid = Win64Xuid::ResolvePersistentXuid();
|
||||
// Each pad gets a unique XUID derived from the persistent uid.dat value.
|
||||
// Pad 0 uses the base XUID directly. Pads 1-3 get a deterministic hash
|
||||
// of (base + pad) to produce fully independent IDs with no overlap risk.
|
||||
*pXuid = Win64Xuid::DeriveXuidForPad(Win64Xuid::ResolvePersistentXuid(), iPad);
|
||||
#else
|
||||
* pXuid = 0xe000d45248242f2e + iPad;
|
||||
#endif
|
||||
|
|
@ -634,8 +666,24 @@ void C_4JProfile::SetPrimaryPad(int iPad) {}
|
|||
char fakeGamerTag[32] = "PlayerName";
|
||||
void SetFakeGamertag(char* name) { strcpy_s(fakeGamerTag, name); }
|
||||
#else
|
||||
char* C_4JProfile::GetGamertag(int iPad) { extern char g_Win64Username[17]; return g_Win64Username; }
|
||||
wstring C_4JProfile::GetDisplayName(int iPad) { extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; }
|
||||
char* C_4JProfile::GetGamertag(int iPad) {
|
||||
extern char g_Win64Username[17];
|
||||
if (iPad > 0 && iPad < XUSER_MAX_COUNT && IQNet::m_player[iPad].m_gamertag[0] != 0 &&
|
||||
!IQNet::m_player[iPad].m_isRemote)
|
||||
{
|
||||
static char s_padGamertag[XUSER_MAX_COUNT][17];
|
||||
WideCharToMultiByte(CP_ACP, 0, IQNet::m_player[iPad].m_gamertag, -1, s_padGamertag[iPad], 17, NULL, NULL);
|
||||
return s_padGamertag[iPad];
|
||||
}
|
||||
return g_Win64Username;
|
||||
}
|
||||
wstring C_4JProfile::GetDisplayName(int iPad) {
|
||||
extern wchar_t g_Win64UsernameW[17];
|
||||
if (iPad > 0 && iPad < XUSER_MAX_COUNT && IQNet::m_player[iPad].m_gamertag[0] != 0 &&
|
||||
!IQNet::m_player[iPad].m_isRemote)
|
||||
return IQNet::m_player[iPad].m_gamertag;
|
||||
return g_Win64UsernameW;
|
||||
}
|
||||
#endif
|
||||
bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; }
|
||||
void C_4JProfile::SetSignInChangeCallback(void (*Func)(LPVOID, bool, unsigned int), LPVOID lpParam) {}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ void Font::renderStyleLine(float x0, float y0, float x1, float y1)
|
|||
void Font::addCharacterQuad(wchar_t c)
|
||||
{
|
||||
float xOff = c % m_cols * m_charWidth;
|
||||
float yOff = c / m_cols * m_charWidth;
|
||||
float yOff = c / m_cols * m_charHeight; // was m_charWidth — wrong when glyphs aren't square
|
||||
float width = charWidths[c] - .01f;
|
||||
float height = m_charHeight - .01f;
|
||||
float fontWidth = m_cols * m_charWidth;
|
||||
|
|
@ -187,7 +187,7 @@ void Font::addCharacterQuad(wchar_t c)
|
|||
void Font::renderCharacter(wchar_t c)
|
||||
{
|
||||
float xOff = c % m_cols * m_charWidth;
|
||||
float yOff = c / m_cols * m_charWidth;
|
||||
float yOff = c / m_cols * m_charHeight; // was m_charWidth — wrong when glyphs aren't square
|
||||
|
||||
float width = charWidths[c] - .01f;
|
||||
float height = m_charHeight - .01f;
|
||||
|
|
|
|||
|
|
@ -593,9 +593,10 @@ void GameRenderer::unZoomRegion()
|
|||
// 4J added as we have more complex adjustments to make for fov & aspect on account of viewports
|
||||
void GameRenderer::getFovAndAspect(float& fov, float& aspect, float a, bool applyEffects)
|
||||
{
|
||||
// 4J - split out aspect ratio and fov here so we can adjust for viewports - we might need to revisit these as
|
||||
// they are maybe be too generous for performance.
|
||||
aspect = mc->width / (float) mc->height;
|
||||
// Use the real window dimensions so the perspective updates on resize.
|
||||
extern int g_rScreenWidth;
|
||||
extern int g_rScreenHeight;
|
||||
aspect = g_rScreenWidth / static_cast<float>(g_rScreenHeight);
|
||||
fov = getFov(a, applyEffects);
|
||||
|
||||
if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP ) ||
|
||||
|
|
@ -968,6 +969,10 @@ void GameRenderer::CachePlayerGammas()
|
|||
|
||||
bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport) const
|
||||
{
|
||||
// Use the actual backbuffer dimensions so viewports adapt to window resize.
|
||||
extern int g_rScreenWidth;
|
||||
extern int g_rScreenHeight;
|
||||
|
||||
int active = 0;
|
||||
int indexMap[NUM_LIGHT_TEXTURES] = {-1, -1, -1, -1};
|
||||
for (int i = 0; i < XUSER_MAX_COUNT && i < NUM_LIGHT_TEXTURES; ++i)
|
||||
|
|
@ -980,8 +985,8 @@ bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport)
|
|||
{
|
||||
outViewport.TopLeftX = 0.0f;
|
||||
outViewport.TopLeftY = 0.0f;
|
||||
outViewport.Width = static_cast<FLOAT>(mc->width);
|
||||
outViewport.Height = static_cast<FLOAT>(mc->height);
|
||||
outViewport.Width = static_cast<FLOAT>(g_rScreenWidth);
|
||||
outViewport.Height = static_cast<FLOAT>(g_rScreenHeight);
|
||||
outViewport.MinDepth = 0.0f;
|
||||
outViewport.MaxDepth = 1.0f;
|
||||
return true;
|
||||
|
|
@ -997,8 +1002,8 @@ bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport)
|
|||
if (k < 0)
|
||||
return false;
|
||||
|
||||
const float width = static_cast<float>(mc->width);
|
||||
const float height = static_cast<float>(mc->height);
|
||||
const float width = static_cast<float>(g_rScreenWidth);
|
||||
const float height = static_cast<float>(g_rScreenHeight);
|
||||
|
||||
if (active == 2)
|
||||
{
|
||||
|
|
@ -1171,7 +1176,7 @@ void GameRenderer::render(float a, bool bFirst)
|
|||
if (mc->noRender) return;
|
||||
GameRenderer::anaglyph3d = mc->options->anaglyph3d;
|
||||
|
||||
glViewport(0, 0, mc->width, mc->height); // 4J - added
|
||||
glViewport(0, 0, mc->width, mc->height); // 4J - added (no-op on Win64, viewport set by StateSetViewport)
|
||||
ScreenSizeCalculator ssc(mc->options, mc->width, mc->height);
|
||||
int screenWidth = ssc.getWidth();
|
||||
int screenHeight = ssc.getHeight();
|
||||
|
|
|
|||
|
|
@ -895,14 +895,14 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
|||
int zBlockPos = Mth::floor(minecraft->player->z);
|
||||
|
||||
// Chunk player is in
|
||||
int xChunkPos = minecraft->player->xChunk;
|
||||
int yChunkPos = minecraft->player->yChunk;
|
||||
int zChunkPos = minecraft->player->zChunk;
|
||||
int xChunkPos = xBlockPos >> 4;
|
||||
int yChunkPos = yBlockPos >> 4;
|
||||
int zChunkPos = zBlockPos >> 4;
|
||||
|
||||
// Players offset within the chunk
|
||||
int xChunkOffset = xBlockPos - xChunkPos * 16;
|
||||
int yChunkOffset = yBlockPos - yChunkPos * 16;
|
||||
int zChunkOffset = zBlockPos - zChunkPos * 16;
|
||||
int xChunkOffset = xBlockPos & 15;
|
||||
int yChunkOffset = yBlockPos & 15;
|
||||
int zChunkOffset = zBlockPos & 15;
|
||||
|
||||
// Format the position like java with limited decumal places
|
||||
WCHAR posString[44]; // Allows upto 7 digit positions (+-9_999_999)
|
||||
|
|
@ -951,18 +951,20 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
|||
if (minecraft->level != NULL && minecraft->level->hasChunkAt(xBlockPos, fmod(yBlockPos, 256), zBlockPos))
|
||||
{
|
||||
LevelChunk *chunkAt = minecraft->level->getChunkAt(xBlockPos, zBlockPos);
|
||||
if (chunkAt != NULL)
|
||||
{
|
||||
int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset);
|
||||
int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset);
|
||||
int maxLight = fmax(skyLight, blockLight);
|
||||
lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)");
|
||||
|
||||
int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset);
|
||||
int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset);
|
||||
int maxLight = fmax(skyLight, blockLight);
|
||||
lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)");
|
||||
lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset)));
|
||||
|
||||
lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset)));
|
||||
Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource());
|
||||
lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")");
|
||||
|
||||
Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource());
|
||||
lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")");
|
||||
|
||||
lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")");
|
||||
lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -930,6 +930,14 @@ void ItemInHandRenderer::tick()
|
|||
|
||||
}
|
||||
|
||||
void ItemInHandRenderer::reset()
|
||||
{
|
||||
selectedItem = nullptr;
|
||||
lastSlot = -1;
|
||||
height = 0.0f;
|
||||
oHeight = 0.0f;
|
||||
}
|
||||
|
||||
void ItemInHandRenderer::itemPlaced()
|
||||
{
|
||||
height = 0;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ private:
|
|||
int lastSlot;
|
||||
public:
|
||||
void tick();
|
||||
void reset();
|
||||
void itemPlaced();
|
||||
void itemUsed();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "Minecraft.h"
|
||||
#include "Common/UI/UIScene.h"
|
||||
#include "GameMode.h"
|
||||
#include "Timer.h"
|
||||
#include "ProgressRenderer.h"
|
||||
|
|
@ -9,6 +10,7 @@
|
|||
#include "User.h"
|
||||
#include "Textures.h"
|
||||
#include "GameRenderer.h"
|
||||
#include "ItemInHandRenderer.h"
|
||||
#include "HumanoidModel.h"
|
||||
#include "Options.h"
|
||||
#include "TexturePackRepository.h"
|
||||
|
|
@ -216,6 +218,7 @@ Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet
|
|||
m_pendingLocalConnections[i] = NULL;
|
||||
m_connectionFailed[i] = false;
|
||||
localgameModes[i]=NULL;
|
||||
localitemInHandRenderers[i] = NULL;
|
||||
}
|
||||
|
||||
animateTickLevel = NULL; // 4J added
|
||||
|
|
@ -1479,9 +1482,22 @@ void Minecraft::run_middle()
|
|||
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_RIGHT))
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_USE;
|
||||
|
||||
bool isClosableByEitherKey = ui.IsSceneInStack(i, eUIScene_FurnaceMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_ContainerMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_DispenserMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_EnchantingMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_BrewingStandMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_TradingMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_AnvilMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_HopperMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_BeaconMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_InventoryMenu) ||
|
||||
ui.IsSceneInStack(i, eUIScene_HorseMenu);
|
||||
bool isEditing = ui.GetTopScene(i) && ui.GetTopScene(i)->isDirectEditBlocking();
|
||||
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_INVENTORY))
|
||||
{
|
||||
if(ui.IsSceneInStack(i, eUIScene_InventoryMenu))
|
||||
if(isClosableByEitherKey && !isEditing)
|
||||
{
|
||||
ui.CloseUIScenes(i);
|
||||
}
|
||||
|
|
@ -1496,7 +1512,7 @@ void Minecraft::run_middle()
|
|||
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING) || g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_CRAFTING_ALT))
|
||||
{
|
||||
if(ui.IsSceneInStack(i, eUIScene_Crafting2x2Menu) || ui.IsSceneInStack(i, eUIScene_Crafting3x3Menu) || ui.IsSceneInStack(i, eUIScene_CreativeMenu))
|
||||
if((ui.IsSceneInStack(i, eUIScene_Crafting2x2Menu) || ui.IsSceneInStack(i, eUIScene_Crafting3x3Menu) || ui.IsSceneInStack(i, eUIScene_CreativeMenu) || isClosableByEitherKey) && !isEditing)
|
||||
{
|
||||
ui.CloseUIScenes(i);
|
||||
}
|
||||
|
|
@ -2357,16 +2373,21 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
|
|||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
if ((screen != NULL || ui.GetMenuDisplayed(iPad)) && g_KBMInput.IsMouseGrabbed())
|
||||
// Mouse grab/release only for the primary (KBM) player — splitscreen
|
||||
// players use controllers and must never fight over the cursor state.
|
||||
if (iPad == ProfileManager.GetPrimaryPad())
|
||||
{
|
||||
g_KBMInput.SetMouseGrabbed(false);
|
||||
if ((screen != NULL || ui.GetMenuDisplayed(iPad)) && g_KBMInput.IsMouseGrabbed())
|
||||
{
|
||||
g_KBMInput.SetMouseGrabbed(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (screen == NULL && !ui.GetMenuDisplayed(iPad) )
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused())
|
||||
if (iPad == ProfileManager.GetPrimaryPad() && !g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused())
|
||||
{
|
||||
g_KBMInput.SetMouseGrabbed(true);
|
||||
}
|
||||
|
|
@ -4226,6 +4247,17 @@ void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_pt
|
|||
// 4J - stop update thread from processing this level, which blocks until it is safe to move on - will be re-enabled if we set the level to be non-NULL
|
||||
gameRenderer->DisableUpdateThread();
|
||||
|
||||
if (level == NULL || player == NULL)
|
||||
{
|
||||
for (int i = 0; i < XUSER_MAX_COUNT; ++i)
|
||||
{
|
||||
if (localitemInHandRenderers[i] != NULL)
|
||||
{
|
||||
localitemInHandRenderers[i]->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < levels.length; ++i)
|
||||
{
|
||||
// 4J We only need to save out in multiplayer is we are setting the level to NULL
|
||||
|
|
@ -4642,8 +4674,14 @@ void Minecraft::startAndConnectTo(const wstring& name, const wstring& sid, const
|
|||
|
||||
Minecraft *minecraft;
|
||||
// 4J - was new Minecraft(frame, canvas, NULL, 854, 480, fullScreen);
|
||||
// Logical width is proportional to the real screen aspect ratio so that
|
||||
// the ortho projection and HUD layout match the viewport without stretching.
|
||||
extern int g_iScreenWidth;
|
||||
extern int g_iScreenHeight;
|
||||
int logicalH = 720;
|
||||
int logicalW = logicalH * g_iScreenWidth / g_iScreenHeight;
|
||||
|
||||
minecraft = new Minecraft(NULL, NULL, NULL, 1280, 720, fullScreen);
|
||||
minecraft = new Minecraft(NULL, NULL, NULL, logicalW, logicalH, fullScreen);
|
||||
|
||||
/* - 4J - removed
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2169,12 +2169,16 @@ void MinecraftServer::tick()
|
|||
}
|
||||
Entity::tickExtraWandering(); // 4J added
|
||||
|
||||
PIXBeginNamedEvent(0,"Connection tick");
|
||||
connection->tick();
|
||||
PIXEndNamedEvent();
|
||||
// Process player disconnect/kick queue BEFORE ticking connections.
|
||||
// PendingConnection::handleLogin rejects duplicate XUIDs, so the old
|
||||
// player must be removed from PlayerList before a reconnecting client's
|
||||
// LoginPacket is processed.
|
||||
PIXBeginNamedEvent(0,"Players tick");
|
||||
players->tick();
|
||||
PIXEndNamedEvent();
|
||||
PIXBeginNamedEvent(0,"Connection tick");
|
||||
connection->tick();
|
||||
PIXEndNamedEvent();
|
||||
|
||||
// 4J - removed
|
||||
#if 0
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ Dungeon!
|
|||
Exclusive!
|
||||
The bee's knees!
|
||||
Down with O.P.P.!
|
||||
Closed source!
|
||||
Closed source xD!
|
||||
Classy!
|
||||
Wow!
|
||||
Not on steam!
|
||||
|
|
|
|||
|
|
@ -190,9 +190,34 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
|||
}
|
||||
else if (duplicateXuid)
|
||||
{
|
||||
// if same XUID already in use by another player so disconnect this one.
|
||||
app.DebugPrintf("Rejecting duplicate xuid for name: %ls\n", name.c_str());
|
||||
disconnect(DisconnectPacket::eDisconnect_Banned);
|
||||
// The old player is still in PlayerList (disconnect hasn't been
|
||||
// processed yet). Force-close the stale connection so the
|
||||
// reconnecting client isn't rejected.
|
||||
app.DebugPrintf("RECONNECT: Duplicate xuid for name: %ls, forcing old connection closed\n", name.c_str());
|
||||
shared_ptr<ServerPlayer> stalePlayer = server->getPlayers()->getPlayer(loginXuid);
|
||||
if (stalePlayer == nullptr && packet->m_onlineXuid != INVALID_XUID)
|
||||
stalePlayer = server->getPlayers()->getPlayer(packet->m_onlineXuid);
|
||||
|
||||
if (stalePlayer != nullptr && stalePlayer->connection != nullptr)
|
||||
{
|
||||
BYTE oldSmallId = 0;
|
||||
if (stalePlayer->connection->connection != nullptr && stalePlayer->connection->connection->getSocket() != nullptr)
|
||||
oldSmallId = stalePlayer->connection->connection->getSocket()->getSmallId();
|
||||
app.DebugPrintf("RECONNECT: Force-disconnecting old player smallId=%d\n", oldSmallId);
|
||||
stalePlayer->connection->disconnect(DisconnectPacket::eDisconnect_Closed);
|
||||
|
||||
// Queue the old SmallId for recycling so it's not permanently leaked.
|
||||
// PlayerList::tick() will call PushFreeSmallId/ClearSocketForSmallId.
|
||||
if (oldSmallId != 0)
|
||||
server->getPlayers()->queueSmallIdForRecycle(oldSmallId);
|
||||
|
||||
app.DebugPrintf("RECONNECT: Old player force-disconnect complete\n");
|
||||
}
|
||||
|
||||
// Accept the login now that the old entry is removed.
|
||||
app.DebugPrintf("RECONNECT: Calling handleAcceptedLogin for new connection\n");
|
||||
handleAcceptedLogin(packet);
|
||||
app.DebugPrintf("RECONNECT: handleAcceptedLogin complete\n");
|
||||
}
|
||||
#ifdef _WINDOWS64
|
||||
else if (g_bRejectDuplicateNames)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
#include "..\Minecraft.World\net.minecraft.network.packet.h"
|
||||
#include "..\Minecraft.World\net.minecraft.network.h"
|
||||
#include "Windows64\Windows64_Xuid.h"
|
||||
#ifdef _WINDOWS64
|
||||
#include "Windows64\Network\WinsockNetLayer.h"
|
||||
#endif
|
||||
#include "..\Minecraft.World\Pos.h"
|
||||
#include "..\Minecraft.World\ProgressListener.h"
|
||||
#include "..\Minecraft.World\HellRandomLevelSource.h"
|
||||
|
|
@ -237,6 +240,14 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
|
|||
addPlayerToReceiving( player );
|
||||
|
||||
int maxPlayersForPacket = getMaxPlayers() > 255 ? 255 : getMaxPlayers();
|
||||
|
||||
BYTE newSmallId = 0;
|
||||
Socket *sock = connection->getSocket();
|
||||
INetworkPlayer *np = sock ? sock->getPlayer() : nullptr;
|
||||
if (np) newSmallId = np->GetSmallId();
|
||||
app.DebugPrintf("RECONNECT: placeNewPlayer smallId=%d entityId=%d dim=%d\n",
|
||||
newSmallId, player->entityId, level->dimension->id);
|
||||
|
||||
playerConnection->send( shared_ptr<LoginPacket>( new LoginPacket(L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(),
|
||||
(byte) level->dimension->id, (byte) level->getMaxBuildHeight(), (byte) maxPlayersForPacket,
|
||||
level->difficulty, TelemetryManager->GetMultiplayerInstanceID(), (BYTE)playerIndex, level->useNewSeaLevel(), player->getAllPlayerGamePrivileges(),
|
||||
|
|
@ -979,6 +990,14 @@ void PlayerList::tick()
|
|||
{
|
||||
player->connection->disconnect( DisconnectPacket::eDisconnect_Closed );
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
// The old Connection's read/write threads are now dead (disconnect waits
|
||||
// for them). Safe to recycle the smallId — no stale write thread can
|
||||
// resolve getPlayer() to a new connection that reuses this slot.
|
||||
WinsockNetLayer::PushFreeSmallId(smallId);
|
||||
WinsockNetLayer::ClearSocketForSmallId(smallId);
|
||||
#endif
|
||||
}
|
||||
LeaveCriticalSection(&m_closePlayersCS);
|
||||
|
||||
|
|
@ -1618,6 +1637,13 @@ void PlayerList::closePlayerConnectionBySmallId(BYTE networkSmallId)
|
|||
LeaveCriticalSection(&m_closePlayersCS);
|
||||
}
|
||||
|
||||
void PlayerList::queueSmallIdForRecycle(BYTE smallId)
|
||||
{
|
||||
EnterCriticalSection(&m_closePlayersCS);
|
||||
m_smallIdsToClose.push_back(smallId);
|
||||
LeaveCriticalSection(&m_closePlayersCS);
|
||||
}
|
||||
|
||||
bool PlayerList::isXuidBanned(PlayerUID xuid)
|
||||
{
|
||||
if( xuid == INVALID_XUID ) return false;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ public:
|
|||
// 4J Added
|
||||
void kickPlayerByShortId(BYTE networkSmallId);
|
||||
void closePlayerConnectionBySmallId(BYTE networkSmallId);
|
||||
void queueSmallIdForRecycle(BYTE smallId);
|
||||
bool isXuidBanned(PlayerUID xuid);
|
||||
// AP added for Vita so the range can be increased once the level starts
|
||||
void setViewDistance(int newViewDistance);
|
||||
|
|
|
|||
|
|
@ -46,19 +46,30 @@ void ServerConnection::handleConnection(shared_ptr<PendingConnection> uc)
|
|||
|
||||
void ServerConnection::stop()
|
||||
{
|
||||
std::vector<shared_ptr<PendingConnection> > pendingSnapshot;
|
||||
EnterCriticalSection(&pending_cs);
|
||||
for (unsigned int i = 0; i < pending.size(); i++)
|
||||
{
|
||||
shared_ptr<PendingConnection> uc = pending[i];
|
||||
uc->connection->close(DisconnectPacket::eDisconnect_Closed);
|
||||
}
|
||||
pendingSnapshot = pending;
|
||||
LeaveCriticalSection(&pending_cs);
|
||||
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
for (unsigned int i = 0; i < pendingSnapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<PlayerConnection> player = players[i];
|
||||
player->connection->close(DisconnectPacket::eDisconnect_Closed);
|
||||
}
|
||||
shared_ptr<PendingConnection> uc = pendingSnapshot[i];
|
||||
if (uc != NULL && !uc->done)
|
||||
{
|
||||
uc->disconnect(DisconnectPacket::eDisconnect_Closed);
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot to avoid iterator invalidation if disconnect modifies the vector.
|
||||
std::vector<shared_ptr<PlayerConnection> > playerSnapshot = players;
|
||||
for (unsigned int i = 0; i < playerSnapshot.size(); i++)
|
||||
{
|
||||
shared_ptr<PlayerConnection> player = playerSnapshot[i];
|
||||
if (player != NULL && !player->done)
|
||||
{
|
||||
player->disconnect(DisconnectPacket::eDisconnect_Quitting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnection::tick()
|
||||
|
|
@ -107,7 +118,10 @@ void ServerConnection::tick()
|
|||
players.erase(players.begin()+i);
|
||||
i--;
|
||||
}
|
||||
player->connection->flush();
|
||||
else
|
||||
{
|
||||
player->connection->flush();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,7 +256,6 @@ void ServerLevel::tick()
|
|||
if (time % (saveInterval) == (dimension->id * dimension->id * (saveInterval/2)))
|
||||
#endif
|
||||
{
|
||||
//app.DebugPrintf("Incremental save\n");
|
||||
PIXBeginNamedEvent(0,"Incremental save");
|
||||
save(false, NULL);
|
||||
PIXEndNamedEvent();
|
||||
|
|
|
|||
|
|
@ -426,7 +426,6 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
|
|||
// unloaded on the client and so just gradually build up more and more of the finite set of chunks as the player moves
|
||||
if( !g_NetworkManager.SystemFlagGet(connection->getNetworkPlayer(),flagIndex) )
|
||||
{
|
||||
// app.DebugPrintf("Creating BRUP for %d %d\n",nearest.x, nearest.z);
|
||||
PIXBeginNamedEvent(0,"Creation BRUP for sending\n");
|
||||
int64_t before = System::currentTimeMillis();
|
||||
shared_ptr<BlockRegionUpdatePacket> packet = shared_ptr<BlockRegionUpdatePacket>( new BlockRegionUpdatePacket(nearest.x * 16, 0, nearest.z * 16, 16, Level::maxBuildHeight, 16, level) );
|
||||
|
|
|
|||
|
|
@ -118,9 +118,7 @@ void StringTable::ProcessStringTableData(void)
|
|||
else
|
||||
{
|
||||
app.DebugPrintf("Failed to get language\n");
|
||||
#ifdef _DEBUG
|
||||
__debugbreak();
|
||||
#endif
|
||||
|
||||
|
||||
isStatic = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -997,8 +997,15 @@ void Textures::replaceTextureDirect(shortArray rawPixels, int w, int h, int id)
|
|||
|
||||
void Textures::releaseTexture(int id)
|
||||
{
|
||||
if (id <= 0) return;
|
||||
loadedImages.erase(id);
|
||||
glDeleteTextures(id);
|
||||
// TextureFree() has no bounds check and crashes on stale IDs (e.g. after
|
||||
// RenderManager.Initialise() which memsets the texture table to zero).
|
||||
// TextureGetTexture() IS safe — returns NULL for invalid/unallocated IDs.
|
||||
if (RenderManager.TextureGetTexture(id) != NULL)
|
||||
{
|
||||
glDeleteTextures(id);
|
||||
}
|
||||
}
|
||||
|
||||
int Textures::loadHttpTexture(const wstring& url, const wstring& backup)
|
||||
|
|
|
|||
|
|
@ -996,6 +996,13 @@ void gdraw_D3D1X_(SetTileOrigin)(ID3D1X(RenderTargetView) *main_rt, ID3D1X(Depth
|
|||
|
||||
static void RADLINK gdraw_SetViewSizeAndWorldScale(S32 w, S32 h, F32 scalex, F32 scaley)
|
||||
{
|
||||
static S32 s_lastW = 0, s_lastH = 0;
|
||||
static F32 s_lastSx = 0, s_lastSy = 0;
|
||||
if (w != s_lastW || h != s_lastH || scalex != s_lastSx || scaley != s_lastSy) {
|
||||
app.DebugPrintf("[GDRAW] SetViewSize: fw=%d fh=%d scale=%.6f,%.6f frametex=%dx%d vx=%d vy=%d\n",
|
||||
w, h, scalex, scaley, gdraw->frametex_width, gdraw->frametex_height, gdraw->vx, gdraw->vy);
|
||||
s_lastW = w; s_lastH = h; s_lastSx = scalex; s_lastSy = scaley;
|
||||
}
|
||||
memset(gdraw->frame, 0, sizeof(gdraw->frame));
|
||||
gdraw->cur = gdraw->frame;
|
||||
gdraw->fw = w;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ bool WinsockNetLayer::s_initialized = false;
|
|||
|
||||
BYTE WinsockNetLayer::s_localSmallId = 0;
|
||||
BYTE WinsockNetLayer::s_hostSmallId = 0;
|
||||
unsigned int WinsockNetLayer::s_nextSmallId = 1;
|
||||
unsigned int WinsockNetLayer::s_nextSmallId = XUSER_MAX_COUNT;
|
||||
|
||||
CRITICAL_SECTION WinsockNetLayer::s_sendLock;
|
||||
CRITICAL_SECTION WinsockNetLayer::s_connectionsLock;
|
||||
|
|
@ -54,6 +54,10 @@ std::vector<BYTE> WinsockNetLayer::s_freeSmallIds;
|
|||
SOCKET WinsockNetLayer::s_smallIdToSocket[256];
|
||||
CRITICAL_SECTION WinsockNetLayer::s_smallIdToSocketLock;
|
||||
|
||||
SOCKET WinsockNetLayer::s_splitScreenSocket[XUSER_MAX_COUNT] = { INVALID_SOCKET, INVALID_SOCKET, INVALID_SOCKET, INVALID_SOCKET };
|
||||
BYTE WinsockNetLayer::s_splitScreenSmallId[XUSER_MAX_COUNT] = { 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
HANDLE WinsockNetLayer::s_splitScreenRecvThread[XUSER_MAX_COUNT] = { NULL, NULL, NULL, NULL };
|
||||
|
||||
bool g_Win64MultiplayerHost = false;
|
||||
bool g_Win64MultiplayerJoin = false;
|
||||
int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
|
||||
|
|
@ -111,6 +115,15 @@ void WinsockNetLayer::Shutdown()
|
|||
s_hostConnectionSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
// Stop accept loop first so no new RecvThread can be created while shutting down.
|
||||
if (s_acceptThread != NULL)
|
||||
{
|
||||
WaitForSingleObject(s_acceptThread, 2000);
|
||||
CloseHandle(s_acceptThread);
|
||||
s_acceptThread = NULL;
|
||||
}
|
||||
|
||||
std::vector<HANDLE> recvThreads;
|
||||
EnterCriticalSection(&s_connectionsLock);
|
||||
for (size_t i = 0; i < s_connections.size(); i++)
|
||||
{
|
||||
|
|
@ -118,18 +131,27 @@ void WinsockNetLayer::Shutdown()
|
|||
if (s_connections[i].tcpSocket != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(s_connections[i].tcpSocket);
|
||||
s_connections[i].tcpSocket = INVALID_SOCKET;
|
||||
}
|
||||
if (s_connections[i].recvThread != NULL)
|
||||
{
|
||||
recvThreads.push_back(s_connections[i].recvThread);
|
||||
s_connections[i].recvThread = NULL;
|
||||
}
|
||||
}
|
||||
s_connections.clear();
|
||||
LeaveCriticalSection(&s_connectionsLock);
|
||||
|
||||
if (s_acceptThread != NULL)
|
||||
// Wait for all host-side receive threads to exit before destroying state.
|
||||
for (size_t i = 0; i < recvThreads.size(); i++)
|
||||
{
|
||||
WaitForSingleObject(s_acceptThread, 2000);
|
||||
CloseHandle(s_acceptThread);
|
||||
s_acceptThread = NULL;
|
||||
WaitForSingleObject(recvThreads[i], 2000);
|
||||
CloseHandle(recvThreads[i]);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&s_connectionsLock);
|
||||
s_connections.clear();
|
||||
LeaveCriticalSection(&s_connectionsLock);
|
||||
|
||||
if (s_clientRecvThread != NULL)
|
||||
{
|
||||
WaitForSingleObject(s_clientRecvThread, 2000);
|
||||
|
|
@ -137,16 +159,38 @@ void WinsockNetLayer::Shutdown()
|
|||
s_clientRecvThread = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < XUSER_MAX_COUNT; i++)
|
||||
{
|
||||
if (s_splitScreenSocket[i] != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(s_splitScreenSocket[i]);
|
||||
s_splitScreenSocket[i] = INVALID_SOCKET;
|
||||
}
|
||||
if (s_splitScreenRecvThread[i] != NULL)
|
||||
{
|
||||
WaitForSingleObject(s_splitScreenRecvThread[i], 2000);
|
||||
CloseHandle(s_splitScreenRecvThread[i]);
|
||||
s_splitScreenRecvThread[i] = NULL;
|
||||
}
|
||||
s_splitScreenSmallId[i] = 0xFF;
|
||||
}
|
||||
|
||||
if (s_initialized)
|
||||
{
|
||||
EnterCriticalSection(&s_disconnectLock);
|
||||
s_disconnectedSmallIds.clear();
|
||||
LeaveCriticalSection(&s_disconnectLock);
|
||||
|
||||
EnterCriticalSection(&s_freeSmallIdLock);
|
||||
s_freeSmallIds.clear();
|
||||
LeaveCriticalSection(&s_freeSmallIdLock);
|
||||
|
||||
DeleteCriticalSection(&s_sendLock);
|
||||
DeleteCriticalSection(&s_connectionsLock);
|
||||
DeleteCriticalSection(&s_advertiseLock);
|
||||
DeleteCriticalSection(&s_discoveryLock);
|
||||
DeleteCriticalSection(&s_disconnectLock);
|
||||
s_disconnectedSmallIds.clear();
|
||||
DeleteCriticalSection(&s_freeSmallIdLock);
|
||||
s_freeSmallIds.clear();
|
||||
DeleteCriticalSection(&s_smallIdToSocketLock);
|
||||
WSACleanup();
|
||||
s_initialized = false;
|
||||
|
|
@ -160,7 +204,7 @@ bool WinsockNetLayer::HostGame(int port, const char* bindIp)
|
|||
s_isHost = true;
|
||||
s_localSmallId = 0;
|
||||
s_hostSmallId = 0;
|
||||
s_nextSmallId = 1;
|
||||
s_nextSmallId = XUSER_MAX_COUNT;
|
||||
s_hostGamePort = port;
|
||||
|
||||
EnterCriticalSection(&s_freeSmallIdLock);
|
||||
|
|
@ -249,6 +293,17 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port)
|
|||
s_hostConnectionSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
// Wait for old client recv thread to fully exit before starting a new connection.
|
||||
// Without this, the old thread can read from the new socket (s_hostConnectionSocket
|
||||
// is a global) and steal bytes from the new connection's TCP stream, causing
|
||||
// packet stream misalignment on reconnect.
|
||||
if (s_clientRecvThread != NULL)
|
||||
{
|
||||
WaitForSingleObject(s_clientRecvThread, 5000);
|
||||
CloseHandle(s_clientRecvThread);
|
||||
s_clientRecvThread = NULL;
|
||||
}
|
||||
|
||||
struct addrinfo hints = {};
|
||||
struct addrinfo* result = NULL;
|
||||
|
||||
|
|
@ -351,6 +406,13 @@ bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void* data, int dataSize)
|
|||
{
|
||||
if (sock == INVALID_SOCKET || dataSize <= 0) return false;
|
||||
|
||||
// TODO: s_sendLock is a single global lock for ALL sockets. If one client's
|
||||
// send() blocks (TCP window full, slow WiFi), every other write thread stalls
|
||||
// waiting for this lock — no data flows to any player until the slow send
|
||||
// completes. This scales badly with player count (8+ players = noticeable).
|
||||
// Fix: replace with per-socket locks indexed by smallId (s_perSocketSendLock[256]).
|
||||
// The lock only needs to prevent interleaving of header+payload on the SAME socket;
|
||||
// sends to different sockets are independent and should never block each other.
|
||||
EnterCriticalSection(&s_sendLock);
|
||||
|
||||
BYTE header[4];
|
||||
|
|
@ -450,19 +512,28 @@ void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsig
|
|||
INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId);
|
||||
INetworkPlayer* pPlayerTo = g_NetworkManager.GetPlayerBySmallId(toSmallId);
|
||||
|
||||
if (pPlayerFrom == NULL || pPlayerTo == NULL) return;
|
||||
if (pPlayerFrom == NULL || pPlayerTo == NULL)
|
||||
{
|
||||
app.DebugPrintf("NET RECV: DROPPED %u bytes from=%d to=%d (player NULL: from=%p to=%p)\n",
|
||||
dataSize, fromSmallId, toSmallId, pPlayerFrom, pPlayerTo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_isHost)
|
||||
{
|
||||
::Socket* pSocket = pPlayerFrom->GetSocket();
|
||||
if (pSocket != NULL)
|
||||
pSocket->pushDataToQueue(data, dataSize, false);
|
||||
else
|
||||
app.DebugPrintf("NET RECV: DROPPED %u bytes, host pSocket NULL for from=%d\n", dataSize, fromSmallId);
|
||||
}
|
||||
else
|
||||
{
|
||||
::Socket* pSocket = pPlayerTo->GetSocket();
|
||||
if (pSocket != NULL)
|
||||
pSocket->pushDataToQueue(data, dataSize, true);
|
||||
else
|
||||
app.DebugPrintf("NET RECV: DROPPED %u bytes, client pSocket NULL for to=%d\n", dataSize, toSmallId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -525,6 +596,7 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
|
|||
{
|
||||
app.DebugPrintf("Failed to send small ID to client\n");
|
||||
closesocket(clientSocket);
|
||||
PushFreeSmallId(assignedSmallId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -662,7 +734,16 @@ bool WinsockNetLayer::PopDisconnectedSmallId(BYTE* outSmallId)
|
|||
void WinsockNetLayer::PushFreeSmallId(BYTE smallId)
|
||||
{
|
||||
EnterCriticalSection(&s_freeSmallIdLock);
|
||||
s_freeSmallIds.push_back(smallId);
|
||||
// Guard against double-recycle: the reconnect path (queueSmallIdForRecycle) and
|
||||
// the DoWork disconnect path can both push the same smallId. If we allow duplicates,
|
||||
// AcceptThread will hand out the same smallId to two different connections.
|
||||
bool alreadyFree = false;
|
||||
for (size_t i = 0; i < s_freeSmallIds.size(); i++)
|
||||
{
|
||||
if (s_freeSmallIds[i] == smallId) { alreadyFree = true; break; }
|
||||
}
|
||||
if (!alreadyFree)
|
||||
s_freeSmallIds.push_back(smallId);
|
||||
LeaveCriticalSection(&s_freeSmallIdLock);
|
||||
}
|
||||
|
||||
|
|
@ -682,6 +763,171 @@ void WinsockNetLayer::CloseConnectionBySmallId(BYTE smallId)
|
|||
LeaveCriticalSection(&s_connectionsLock);
|
||||
}
|
||||
|
||||
BYTE WinsockNetLayer::GetSplitScreenSmallId(int padIndex)
|
||||
{
|
||||
if (padIndex <= 0 || padIndex >= XUSER_MAX_COUNT) return 0xFF;
|
||||
return s_splitScreenSmallId[padIndex];
|
||||
}
|
||||
|
||||
SOCKET WinsockNetLayer::GetLocalSocket(BYTE senderSmallId)
|
||||
{
|
||||
if (senderSmallId == s_localSmallId)
|
||||
return s_hostConnectionSocket;
|
||||
for (int i = 1; i < XUSER_MAX_COUNT; i++)
|
||||
{
|
||||
if (s_splitScreenSmallId[i] == senderSmallId && s_splitScreenSocket[i] != INVALID_SOCKET)
|
||||
return s_splitScreenSocket[i];
|
||||
}
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
bool WinsockNetLayer::JoinSplitScreen(int padIndex, BYTE* outSmallId)
|
||||
{
|
||||
if (!s_active || s_isHost || padIndex <= 0 || padIndex >= XUSER_MAX_COUNT)
|
||||
return false;
|
||||
|
||||
if (s_splitScreenSocket[padIndex] != INVALID_SOCKET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct addrinfo hints = {};
|
||||
struct addrinfo* result = NULL;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
char portStr[16];
|
||||
sprintf_s(portStr, "%d", g_Win64MultiplayerPort);
|
||||
if (getaddrinfo(g_Win64MultiplayerIP, portStr, &hints, &result) != 0 || result == NULL)
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen getaddrinfo failed for %s:%d\n", g_Win64MultiplayerIP, g_Win64MultiplayerPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
freeaddrinfo(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
int noDelay = 1;
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
|
||||
|
||||
if (connect(sock, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR)
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen connect() failed: %d\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
freeaddrinfo(result);
|
||||
return false;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
BYTE assignBuf[1];
|
||||
if (!RecvExact(sock, assignBuf, 1))
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen failed to receive smallId\n");
|
||||
closesocket(sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (assignBuf[0] == WIN64_SMALLID_REJECT)
|
||||
{
|
||||
BYTE rejectBuf[5];
|
||||
RecvExact(sock, rejectBuf, 5);
|
||||
app.DebugPrintf("Win64 LAN: Split-screen connection rejected\n");
|
||||
closesocket(sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE assignedSmallId = assignBuf[0];
|
||||
s_splitScreenSocket[padIndex] = sock;
|
||||
s_splitScreenSmallId[padIndex] = assignedSmallId;
|
||||
*outSmallId = assignedSmallId;
|
||||
|
||||
app.DebugPrintf("Win64 LAN: Split-screen pad %d connected, assigned smallId=%d\n", padIndex, assignedSmallId);
|
||||
|
||||
int* threadParam = new int;
|
||||
*threadParam = padIndex;
|
||||
s_splitScreenRecvThread[padIndex] = CreateThread(NULL, 0, SplitScreenRecvThreadProc, threadParam, 0, NULL);
|
||||
if (s_splitScreenRecvThread[padIndex] == NULL)
|
||||
{
|
||||
delete threadParam;
|
||||
closesocket(sock);
|
||||
s_splitScreenSocket[padIndex] = INVALID_SOCKET;
|
||||
s_splitScreenSmallId[padIndex] = 0xFF;
|
||||
app.DebugPrintf("Win64 LAN: CreateThread failed for split-screen pad %d\n", padIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WinsockNetLayer::CloseSplitScreenConnection(int padIndex)
|
||||
{
|
||||
if (padIndex <= 0 || padIndex >= XUSER_MAX_COUNT) return;
|
||||
|
||||
if (s_splitScreenSocket[padIndex] != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(s_splitScreenSocket[padIndex]);
|
||||
s_splitScreenSocket[padIndex] = INVALID_SOCKET;
|
||||
}
|
||||
s_splitScreenSmallId[padIndex] = 0xFF;
|
||||
if (s_splitScreenRecvThread[padIndex] != NULL)
|
||||
{
|
||||
WaitForSingleObject(s_splitScreenRecvThread[padIndex], 2000);
|
||||
CloseHandle(s_splitScreenRecvThread[padIndex]);
|
||||
s_splitScreenRecvThread[padIndex] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI WinsockNetLayer::SplitScreenRecvThreadProc(LPVOID param)
|
||||
{
|
||||
int padIndex = *(int*)param;
|
||||
delete (int*)param;
|
||||
|
||||
SOCKET sock = s_splitScreenSocket[padIndex];
|
||||
BYTE localSmallId = s_splitScreenSmallId[padIndex];
|
||||
std::vector<BYTE> recvBuf;
|
||||
recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE);
|
||||
|
||||
while (s_active && s_splitScreenSocket[padIndex] != INVALID_SOCKET)
|
||||
{
|
||||
BYTE header[4];
|
||||
if (!RecvExact(sock, header, 4))
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen pad %d disconnected from host\n", padIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
int packetSize = ((uint32_t)header[0] << 24) | ((uint32_t)header[1] << 16) |
|
||||
((uint32_t)header[2] << 8) | ((uint32_t)header[3]);
|
||||
if (packetSize <= 0 || packetSize > WIN64_NET_MAX_PACKET_SIZE)
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen pad %d invalid packet size %d\n", padIndex, packetSize);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((int)recvBuf.size() < packetSize)
|
||||
recvBuf.resize(packetSize);
|
||||
|
||||
if (!RecvExact(sock, &recvBuf[0], packetSize))
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Split-screen pad %d disconnected from host (body)\n", padIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
HandleDataReceived(s_hostSmallId, localSmallId, &recvBuf[0], packetSize);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&s_disconnectLock);
|
||||
s_disconnectedSmallIds.push_back(localSmallId);
|
||||
LeaveCriticalSection(&s_disconnectLock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI WinsockNetLayer::ClientRecvThreadProc(LPVOID param)
|
||||
{
|
||||
std::vector<BYTE> recvBuf;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,12 @@ public:
|
|||
static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize);
|
||||
static bool SendOnSocket(SOCKET sock, const void* data, int dataSize);
|
||||
|
||||
// Non-host split-screen: additional TCP connections to host, one per pad
|
||||
static bool JoinSplitScreen(int padIndex, BYTE* outSmallId);
|
||||
static void CloseSplitScreenConnection(int padIndex);
|
||||
static SOCKET GetLocalSocket(BYTE senderSmallId);
|
||||
static BYTE GetSplitScreenSmallId(int padIndex);
|
||||
|
||||
static bool IsHosting() { return s_isHost; }
|
||||
static bool IsConnected() { return s_connected; }
|
||||
static bool IsActive() { return s_active; }
|
||||
|
|
@ -103,6 +109,7 @@ private:
|
|||
static DWORD WINAPI AcceptThreadProc(LPVOID param);
|
||||
static DWORD WINAPI RecvThreadProc(LPVOID param);
|
||||
static DWORD WINAPI ClientRecvThreadProc(LPVOID param);
|
||||
static DWORD WINAPI SplitScreenRecvThreadProc(LPVOID param);
|
||||
static DWORD WINAPI AdvertiseThreadProc(LPVOID param);
|
||||
static DWORD WINAPI DiscoveryThreadProc(LPVOID param);
|
||||
|
||||
|
|
@ -147,6 +154,11 @@ private:
|
|||
static SOCKET s_smallIdToSocket[256];
|
||||
static CRITICAL_SECTION s_smallIdToSocketLock;
|
||||
|
||||
// Per-pad split-screen TCP connections (client-side, non-host only)
|
||||
static SOCKET s_splitScreenSocket[XUSER_MAX_COUNT];
|
||||
static BYTE s_splitScreenSmallId[XUSER_MAX_COUNT];
|
||||
static HANDLE s_splitScreenRecvThread[XUSER_MAX_COUNT];
|
||||
|
||||
public:
|
||||
static void ClearSocketForSmallId(BYTE smallId);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@
|
|||
#include "Network\WinsockNetLayer.h"
|
||||
#include "Windows64_Xuid.h"
|
||||
|
||||
// Forward-declare the internal Renderer class and its global instance from 4J_Render_PC_d.lib.
|
||||
// C4JRender (RenderManager) is a stateless wrapper — all D3D state lives in InternalRenderManager.
|
||||
class Renderer;
|
||||
extern Renderer InternalRenderManager;
|
||||
|
||||
#include "Xbox/resource.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
@ -91,10 +96,20 @@ DWORD dwProfileSettingsA[NUM_PROFILE_VALUES]=
|
|||
|
||||
BOOL g_bWidescreen = TRUE;
|
||||
|
||||
// Screen resolution — auto-detected from the monitor at startup.
|
||||
// The 3D world renders at native resolution; Flash UI is 16:9-fitted and centered
|
||||
// within each viewport (pillarboxed on ultrawide, letterboxed on tall displays).
|
||||
// ApplyScreenMode() can still override these for debug/test resolutions via launch args.
|
||||
int g_iScreenWidth = 1920;
|
||||
int g_iScreenHeight = 1080;
|
||||
|
||||
// Real window dimensions — updated on every WM_SIZE so the 3D perspective
|
||||
// always matches the current window, even after a resize.
|
||||
int g_rScreenWidth = 1920;
|
||||
int g_rScreenHeight = 1080;
|
||||
|
||||
float g_iAspectRatio = static_cast<float>(g_iScreenWidth) / g_iScreenHeight;
|
||||
static bool g_bResizeReady = false;
|
||||
|
||||
char g_Win64Username[17] = { 0 };
|
||||
wchar_t g_Win64UsernameW[17] = { 0 };
|
||||
|
|
@ -103,14 +118,6 @@ wchar_t g_Win64UsernameW[17] = { 0 };
|
|||
static bool g_isFullscreen = false;
|
||||
static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Update the Aspect Ratio to support Any Aspect Ratio
|
||||
//--------------------------------------------------------------------------------------
|
||||
void UpdateAspectRatio(int width, int height)
|
||||
{
|
||||
g_iAspectRatio = static_cast<float>(width) / height;
|
||||
}
|
||||
|
||||
struct Win64LaunchOptions
|
||||
{
|
||||
int screenMode;
|
||||
|
|
@ -531,6 +538,11 @@ ID3D11Texture2D* g_pDepthStencilBuffer = NULL;
|
|||
// WM_SIZE - handle resizing logic to support Any Aspect Ratio
|
||||
//
|
||||
//
|
||||
static bool ResizeD3D(int newW, int newH); // forward declaration
|
||||
static bool g_bInSizeMove = false; // true while the user is dragging the window border
|
||||
static int g_pendingResizeW = 0; // deferred resize dimensions
|
||||
static int g_pendingResizeH = 0;
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
int wmId, wmEvent;
|
||||
|
|
@ -668,9 +680,40 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case WM_ENTERSIZEMOVE:
|
||||
g_bInSizeMove = true;
|
||||
break;
|
||||
|
||||
case WM_EXITSIZEMOVE:
|
||||
g_bInSizeMove = false;
|
||||
if (g_pendingResizeW > 0 && g_pendingResizeH > 0)
|
||||
{
|
||||
// g_rScreenWidth/Height updated inside ResizeD3D to backbuffer dims
|
||||
ResizeD3D(g_pendingResizeW, g_pendingResizeH);
|
||||
g_pendingResizeW = 0;
|
||||
g_pendingResizeH = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
{
|
||||
UpdateAspectRatio(LOWORD(lParam), HIWORD(lParam));
|
||||
int newW = LOWORD(lParam);
|
||||
int newH = HIWORD(lParam);
|
||||
if (newW > 0 && newH > 0)
|
||||
{
|
||||
if (g_bInSizeMove)
|
||||
{
|
||||
// Just store the latest size, resize when dragging ends
|
||||
g_pendingResizeW = newW;
|
||||
g_pendingResizeH = newH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Immediate resize (maximize, programmatic resize, etc.)
|
||||
// g_rScreenWidth/Height updated inside ResizeD3D to backbuffer dims
|
||||
ResizeD3D(newW, newH);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -719,7 +762,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
|
|||
{
|
||||
g_hInst = hInstance; // Store instance handle in our global variable
|
||||
|
||||
RECT wr = {0, 0, g_iScreenWidth, g_iScreenHeight}; // set the size, but not the position
|
||||
RECT wr = {0, 0, g_rScreenWidth, g_rScreenHeight}; // set the size, but not the position
|
||||
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size
|
||||
|
||||
g_hWnd = CreateWindow( "MinecraftClass",
|
||||
|
|
@ -805,8 +848,8 @@ HRESULT InitDevice()
|
|||
UINT width = rc.right - rc.left;
|
||||
UINT height = rc.bottom - rc.top;
|
||||
//app.DebugPrintf("width: %d, height: %d\n", width, height);
|
||||
width = g_iScreenWidth;
|
||||
height = g_iScreenHeight;
|
||||
width = g_rScreenWidth;
|
||||
height = g_rScreenHeight;
|
||||
//app.DebugPrintf("width: %d, height: %d\n", width, height);
|
||||
|
||||
UINT createDeviceFlags = 0;
|
||||
|
|
@ -838,7 +881,7 @@ HRESULT InitDevice()
|
|||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
sd.BufferDesc.RefreshRate.Numerator = 60;
|
||||
sd.BufferDesc.RefreshRate.Denominator = 1;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
|
||||
sd.OutputWindow = g_hWnd;
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
|
|
@ -922,6 +965,281 @@ void Render()
|
|||
g_pSwapChain->Present( 0, 0 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Rebuild D3D11 resources after a window resize
|
||||
//--------------------------------------------------------------------------------------
|
||||
static bool ResizeD3D(int newW, int newH)
|
||||
{
|
||||
if (newW <= 0 || newH <= 0) return false;
|
||||
if (!g_pSwapChain) return false;
|
||||
if (!g_bResizeReady) return false;
|
||||
|
||||
int bbW = newW;
|
||||
int bbH = newH;
|
||||
|
||||
// InternalRenderManager member offsets (from decompiled Renderer.h):
|
||||
// 0x10 m_pDevice (ID3D11Device*)
|
||||
// 0x18 m_pDeviceContext (ID3D11DeviceContext*)
|
||||
// 0x20 m_pSwapChain (IDXGISwapChain*)
|
||||
// 0x28 renderTargetView (ID3D11RenderTargetView*) — backbuffer RTV
|
||||
// 0x50 renderTargetShaderResourceView (ID3D11ShaderResourceView*)
|
||||
// 0x98 depthStencilView (ID3D11DepthStencilView*)
|
||||
// 0x5138 backBufferWidth (DWORD) — used by StartFrame() for viewport
|
||||
// 0x513C backBufferHeight (DWORD) — used by StartFrame() for viewport
|
||||
//
|
||||
// Strategy: destroy old swap chain, create new one, patch Renderer's internal
|
||||
// pointers directly. This avoids both ResizeBuffers (outstanding ref issues)
|
||||
// and Initialise() (which wipes the texture table via memset).
|
||||
// The Renderer's old RTV/SRV/DSV are intentionally NOT released — they become
|
||||
// orphaned with the old swap chain. Tiny leak, but avoids fighting unknown refs.
|
||||
char* pRM = (char*)&InternalRenderManager;
|
||||
ID3D11RenderTargetView** ppRM_RTV = (ID3D11RenderTargetView**)(pRM + 0x28);
|
||||
ID3D11ShaderResourceView** ppRM_SRV = (ID3D11ShaderResourceView**)(pRM + 0x50);
|
||||
ID3D11DepthStencilView** ppRM_DSV = (ID3D11DepthStencilView**)(pRM + 0x98);
|
||||
IDXGISwapChain** ppRM_SC = (IDXGISwapChain**)(pRM + 0x20);
|
||||
DWORD* pRM_BBWidth = (DWORD*)(pRM + 0x5138);
|
||||
DWORD* pRM_BBHeight = (DWORD*)(pRM + 0x513C);
|
||||
|
||||
// Verify offsets by checking device and swap chain pointers
|
||||
ID3D11Device** ppRM_Device = (ID3D11Device**)(pRM + 0x10);
|
||||
if (*ppRM_Device != g_pd3dDevice || *ppRM_SC != g_pSwapChain)
|
||||
{
|
||||
app.DebugPrintf("[RESIZE] ERROR: RenderManager offset verification failed! "
|
||||
"device=%p (expected %p) swapchain=%p (expected %p)\n",
|
||||
*ppRM_Device, g_pd3dDevice, *ppRM_SC, g_pSwapChain);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cross-check backbuffer dimension offsets against swap chain desc
|
||||
DXGI_SWAP_CHAIN_DESC oldScDesc;
|
||||
g_pSwapChain->GetDesc(&oldScDesc);
|
||||
bool bbDimsValid = (*pRM_BBWidth == oldScDesc.BufferDesc.Width &&
|
||||
*pRM_BBHeight == oldScDesc.BufferDesc.Height);
|
||||
if (!bbDimsValid)
|
||||
{
|
||||
app.DebugPrintf("[RESIZE] WARNING: backBuffer dim offsets wrong: "
|
||||
"stored=%ux%u, swapchain=%ux%u\n",
|
||||
*pRM_BBWidth, *pRM_BBHeight, oldScDesc.BufferDesc.Width, oldScDesc.BufferDesc.Height);
|
||||
}
|
||||
|
||||
RenderManager.Suspend();
|
||||
while (!RenderManager.Suspended()) { Sleep(1); }
|
||||
|
||||
PostProcesser::GetInstance().Cleanup();
|
||||
|
||||
g_pImmediateContext->ClearState();
|
||||
g_pImmediateContext->Flush();
|
||||
|
||||
// Release OUR views and depth buffer
|
||||
if (g_pRenderTargetView) { g_pRenderTargetView->Release(); g_pRenderTargetView = NULL; }
|
||||
if (g_pDepthStencilView) { g_pDepthStencilView->Release(); g_pDepthStencilView = NULL; }
|
||||
if (g_pDepthStencilBuffer) { g_pDepthStencilBuffer->Release(); g_pDepthStencilBuffer = NULL; }
|
||||
|
||||
gdraw_D3D11_PreReset();
|
||||
|
||||
// Get IDXGIFactory from the existing device BEFORE destroying the old swap chain.
|
||||
// If anything fails before we have a new swap chain, we abort without destroying
|
||||
// the old one — leaving the Renderer in a valid (old-size) state.
|
||||
IDXGISwapChain* pOldSwapChain = g_pSwapChain;
|
||||
bool success = false;
|
||||
HRESULT hr;
|
||||
|
||||
IDXGIDevice* dxgiDevice = NULL;
|
||||
IDXGIAdapter* dxgiAdapter = NULL;
|
||||
IDXGIFactory* dxgiFactory = NULL;
|
||||
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
|
||||
if (FAILED(hr)) goto postReset;
|
||||
hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
|
||||
if (FAILED(hr)) { dxgiDevice->Release(); goto postReset; }
|
||||
hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
|
||||
dxgiAdapter->Release();
|
||||
dxgiDevice->Release();
|
||||
if (FAILED(hr)) goto postReset;
|
||||
|
||||
// Create new swap chain at backbuffer size
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC sd = {};
|
||||
sd.BufferCount = 1;
|
||||
sd.BufferDesc.Width = bbW;
|
||||
sd.BufferDesc.Height = bbH;
|
||||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
sd.BufferDesc.RefreshRate.Numerator = 60;
|
||||
sd.BufferDesc.RefreshRate.Denominator = 1;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
|
||||
sd.OutputWindow = g_hWnd;
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
sd.Windowed = TRUE;
|
||||
|
||||
IDXGISwapChain* pNewSwapChain = NULL;
|
||||
hr = dxgiFactory->CreateSwapChain(g_pd3dDevice, &sd, &pNewSwapChain);
|
||||
dxgiFactory->Release();
|
||||
if (FAILED(hr) || pNewSwapChain == NULL)
|
||||
{
|
||||
app.DebugPrintf("[RESIZE] CreateSwapChain FAILED hr=0x%08X — keeping old swap chain\n", (unsigned)hr);
|
||||
goto postReset;
|
||||
}
|
||||
|
||||
// New swap chain created successfully — NOW destroy the old one.
|
||||
// The Renderer's internal RTV/SRV still reference the old backbuffer —
|
||||
// those COM objects become orphaned (tiny leak, harmless). We DON'T
|
||||
// release them because unknown code may also hold refs.
|
||||
pOldSwapChain->Release();
|
||||
g_pSwapChain = pNewSwapChain;
|
||||
}
|
||||
|
||||
// Patch Renderer's swap chain pointer
|
||||
*ppRM_SC = g_pSwapChain;
|
||||
|
||||
// Create render target views from new backbuffer
|
||||
{
|
||||
ID3D11Texture2D* pBackBuffer = NULL;
|
||||
hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
|
||||
if (FAILED(hr)) goto postReset;
|
||||
|
||||
// Our RTV
|
||||
hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);
|
||||
if (FAILED(hr)) { pBackBuffer->Release(); goto postReset; }
|
||||
|
||||
// Renderer's internal RTV (offset 0x28)
|
||||
hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, ppRM_RTV);
|
||||
if (FAILED(hr)) { pBackBuffer->Release(); goto postReset; }
|
||||
|
||||
// Renderer's SRV: separate texture matching backbuffer dims (used by CaptureThumbnail)
|
||||
D3D11_TEXTURE2D_DESC backDesc = {};
|
||||
pBackBuffer->GetDesc(&backDesc);
|
||||
pBackBuffer->Release();
|
||||
|
||||
D3D11_TEXTURE2D_DESC srvDesc = backDesc;
|
||||
srvDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
||||
ID3D11Texture2D* srvTexture = NULL;
|
||||
hr = g_pd3dDevice->CreateTexture2D(&srvDesc, NULL, &srvTexture);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = g_pd3dDevice->CreateShaderResourceView(srvTexture, NULL, ppRM_SRV);
|
||||
srvTexture->Release();
|
||||
}
|
||||
if (FAILED(hr)) goto postReset;
|
||||
}
|
||||
|
||||
// Recreate depth stencil at backbuffer size
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC descDepth = {};
|
||||
descDepth.Width = bbW;
|
||||
descDepth.Height = bbH;
|
||||
descDepth.MipLevels = 1;
|
||||
descDepth.ArraySize = 1;
|
||||
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
||||
descDepth.SampleDesc.Count = 1;
|
||||
descDepth.SampleDesc.Quality = 0;
|
||||
descDepth.Usage = D3D11_USAGE_DEFAULT;
|
||||
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
||||
hr = g_pd3dDevice->CreateTexture2D(&descDepth, NULL, &g_pDepthStencilBuffer);
|
||||
if (FAILED(hr)) goto postReset;
|
||||
|
||||
D3D11_DEPTH_STENCIL_VIEW_DESC descDSView = {};
|
||||
descDSView.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
||||
descDSView.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
|
||||
hr = g_pd3dDevice->CreateDepthStencilView(g_pDepthStencilBuffer, &descDSView, &g_pDepthStencilView);
|
||||
if (FAILED(hr)) goto postReset;
|
||||
}
|
||||
|
||||
// Patch Renderer's DSV (AddRef because both we and the Renderer reference it)
|
||||
g_pDepthStencilView->AddRef();
|
||||
*ppRM_DSV = g_pDepthStencilView;
|
||||
|
||||
// Update Renderer's cached backbuffer dimensions (StartFrame uses these for viewport)
|
||||
if (bbDimsValid)
|
||||
{
|
||||
*pRM_BBWidth = (DWORD)bbW;
|
||||
*pRM_BBHeight = (DWORD)bbH;
|
||||
}
|
||||
|
||||
// Rebind render targets and viewport
|
||||
g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
|
||||
{
|
||||
D3D11_VIEWPORT vp = {};
|
||||
vp.Width = (FLOAT)bbW;
|
||||
vp.Height = (FLOAT)bbH;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
g_pImmediateContext->RSSetViewports(1, &vp);
|
||||
}
|
||||
|
||||
ui.updateRenderTargets(g_pRenderTargetView, g_pDepthStencilView);
|
||||
ui.updateScreenSize(bbW, bbH);
|
||||
|
||||
// Track actual backbuffer dimensions for the rest of the engine
|
||||
g_rScreenWidth = bbW;
|
||||
g_rScreenHeight = bbH;
|
||||
|
||||
success = true;
|
||||
|
||||
postReset:
|
||||
if (!success && g_pSwapChain != NULL)
|
||||
{
|
||||
// Failure recovery: recreate our views from whatever swap chain survived
|
||||
// so ui.m_pRenderTargetView / m_pDepthStencilView don't dangle.
|
||||
DXGI_SWAP_CHAIN_DESC recoveryDesc;
|
||||
g_pSwapChain->GetDesc(&recoveryDesc);
|
||||
int recW = (int)recoveryDesc.BufferDesc.Width;
|
||||
int recH = (int)recoveryDesc.BufferDesc.Height;
|
||||
|
||||
if (g_pRenderTargetView == NULL)
|
||||
{
|
||||
ID3D11Texture2D* pBB = NULL;
|
||||
if (SUCCEEDED(g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBB)))
|
||||
{
|
||||
g_pd3dDevice->CreateRenderTargetView(pBB, NULL, &g_pRenderTargetView);
|
||||
pBB->Release();
|
||||
}
|
||||
}
|
||||
if (g_pDepthStencilView == NULL)
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC dd = {};
|
||||
dd.Width = recW; dd.Height = recH; dd.MipLevels = 1; dd.ArraySize = 1;
|
||||
dd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
||||
dd.SampleDesc.Count = 1; dd.Usage = D3D11_USAGE_DEFAULT;
|
||||
dd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
||||
if (g_pDepthStencilBuffer == NULL)
|
||||
g_pd3dDevice->CreateTexture2D(&dd, NULL, &g_pDepthStencilBuffer);
|
||||
if (g_pDepthStencilBuffer != NULL)
|
||||
{
|
||||
D3D11_DEPTH_STENCIL_VIEW_DESC dsvd = {};
|
||||
dsvd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
||||
dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
|
||||
g_pd3dDevice->CreateDepthStencilView(g_pDepthStencilBuffer, &dsvd, &g_pDepthStencilView);
|
||||
}
|
||||
}
|
||||
if (g_pRenderTargetView != NULL)
|
||||
g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
|
||||
|
||||
ui.updateRenderTargets(g_pRenderTargetView, g_pDepthStencilView);
|
||||
|
||||
// If the surviving swap chain is the OLD one, dims are unchanged.
|
||||
// If it's the NEW one (partial failure after swap), update to new dims.
|
||||
if (g_pSwapChain != pOldSwapChain)
|
||||
{
|
||||
g_rScreenWidth = recW;
|
||||
g_rScreenHeight = recH;
|
||||
ui.updateScreenSize(recW, recH);
|
||||
}
|
||||
|
||||
app.DebugPrintf("[RESIZE] FAILED but recovered views at %dx%d\n", g_rScreenWidth, g_rScreenHeight);
|
||||
}
|
||||
|
||||
gdraw_D3D11_PostReset();
|
||||
gdraw_D3D11_SetRendertargetSize(g_rScreenWidth, g_rScreenHeight);
|
||||
if (success)
|
||||
IggyFlushInstalledFonts();
|
||||
RenderManager.Resume();
|
||||
|
||||
if (success)
|
||||
PostProcesser::GetInstance().Init();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Toggle borderless fullscreen
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
|
@ -963,6 +1281,8 @@ void CleanupDevice()
|
|||
{
|
||||
if( g_pImmediateContext ) g_pImmediateContext->ClearState();
|
||||
|
||||
if( g_pDepthStencilView ) g_pDepthStencilView->Release();
|
||||
if( g_pDepthStencilBuffer ) g_pDepthStencilBuffer->Release();
|
||||
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
|
||||
if( g_pSwapChain ) g_pSwapChain->Release();
|
||||
if( g_pImmediateContext ) g_pImmediateContext->Release();
|
||||
|
|
@ -976,7 +1296,7 @@ static Minecraft* InitialiseMinecraftRuntime()
|
|||
RenderManager.Initialise(g_pd3dDevice, g_pSwapChain);
|
||||
|
||||
app.loadStringTable();
|
||||
ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight);
|
||||
ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_rScreenWidth, g_rScreenHeight);
|
||||
|
||||
InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU);
|
||||
g_KBMInput.Init();
|
||||
|
|
@ -1203,8 +1523,12 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|||
|
||||
// Declare DPI awareness so GetSystemMetrics returns physical pixels
|
||||
SetProcessDPIAware();
|
||||
g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN);
|
||||
g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
|
||||
// Use the native monitor resolution for the window and swap chain,
|
||||
// but keep g_iScreenWidth/Height at 1920x1080 for logical resolution
|
||||
// (SWF selection, ortho projection, game logic). The real window
|
||||
// dimensions are tracked by g_rScreenWidth/g_rScreenHeight.
|
||||
g_rScreenWidth = GetSystemMetrics(SM_CXSCREEN);
|
||||
g_rScreenHeight = GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
// Load username from username.txt
|
||||
char exePath[MAX_PATH] = {};
|
||||
|
|
@ -1411,6 +1735,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|||
CleanupDevice();
|
||||
return 1;
|
||||
}
|
||||
g_bResizeReady = true;
|
||||
|
||||
//app.TemporaryCreateGameStart();
|
||||
|
||||
|
|
@ -1450,6 +1775,14 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|||
}
|
||||
if (msg.message == WM_QUIT) break;
|
||||
|
||||
// When the window is minimized (e.g. "Show Desktop"), skip rendering entirely
|
||||
// to avoid pegging the GPU at 100% presenting to a non-visible swap chain.
|
||||
if (IsIconic(g_hWnd))
|
||||
{
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderManager.StartFrame();
|
||||
#if 0
|
||||
if(pMinecraft->soundEngine->isStreamingWavebankReady() &&
|
||||
|
|
|
|||
|
|
@ -143,6 +143,12 @@ void ConsoleUIController::endCustomDraw(IggyCustomDrawCallbackRegion *region)
|
|||
PIXEndNamedEvent();
|
||||
}
|
||||
|
||||
void ConsoleUIController::updateRenderTargets(ID3D11RenderTargetView* rtv, ID3D11DepthStencilView* dsv)
|
||||
{
|
||||
m_pRenderTargetView = rtv;
|
||||
m_pDepthStencilView = dsv;
|
||||
}
|
||||
|
||||
void ConsoleUIController::setTileOrigin(S32 xPos, S32 yPos)
|
||||
{
|
||||
gdraw_D3D11_SetTileOrigin( m_pRenderTargetView,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ public:
|
|||
virtual CustomDrawData *calculateCustomDraw(IggyCustomDrawCallbackRegion *region);
|
||||
virtual void endCustomDraw(IggyCustomDrawCallbackRegion *region);
|
||||
|
||||
void updateRenderTargets(ID3D11RenderTargetView* rtv, ID3D11DepthStencilView* dsv);
|
||||
|
||||
protected:
|
||||
virtual void setTileOrigin(S32 xPos, S32 yPos);
|
||||
|
||||
public:
|
||||
public:
|
||||
GDrawTexture *getSubstitutionTexture(int textureId);
|
||||
void destroySubstitutionTexture(void *destroyCallBackData, GDrawTexture *handle);
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,29 @@ namespace Win64Xuid
|
|||
return xuid;
|
||||
}
|
||||
|
||||
inline PlayerUID DeriveXuidForPad(PlayerUID baseXuid, int iPad)
|
||||
{
|
||||
if (iPad == 0)
|
||||
return baseXuid;
|
||||
|
||||
// Deterministic per-pad XUID: hash the base XUID with the pad number.
|
||||
// Produces a fully unique 64-bit value with no risk of overlap.
|
||||
// Suggested by rtm516 to avoid adjacent-integer collisions from the old "+ iPad" approach.
|
||||
uint64_t raw = Mix64((uint64_t)baseXuid + (uint64_t)iPad);
|
||||
raw |= 0x8000000000000000ULL; // keep high bit set like all our XUIDs
|
||||
|
||||
PlayerUID xuid = (PlayerUID)raw;
|
||||
if (!IsPersistedUidValid(xuid))
|
||||
{
|
||||
raw ^= 0x0100000000000001ULL;
|
||||
xuid = (PlayerUID)raw;
|
||||
}
|
||||
if (!IsPersistedUidValid(xuid))
|
||||
xuid = (PlayerUID)(0xD15EA5E000000001ULL + iPad);
|
||||
|
||||
return xuid;
|
||||
}
|
||||
|
||||
inline PlayerUID ResolvePersistentXuid()
|
||||
{
|
||||
// Process-local cache: uid.dat is immutable during runtime and this path is hot.
|
||||
|
|
|
|||
|
|
@ -48,11 +48,12 @@ void glLoadIdentity()
|
|||
RenderManager.MatrixSetIdentity();
|
||||
}
|
||||
|
||||
// AAR - Use calculated aspect ratio to support dynamic resizing
|
||||
extern float g_iAspectRatio;
|
||||
// AAR - Use the aspect ratio passed by the caller. For single-player this
|
||||
// equals g_iAspectRatio (screen width / height), but for split-screen
|
||||
// getFovAndAspect adjusts it to match the viewport dimensions.
|
||||
void gluPerspective(float fovy, float aspect, float zNear, float zFar)
|
||||
{
|
||||
RenderManager.MatrixPerspective(fovy, g_iAspectRatio, zNear, zFar);
|
||||
RenderManager.MatrixPerspective(fovy, aspect, zNear, zFar);
|
||||
}
|
||||
|
||||
void glOrtho(float left,float right,float bottom,float top,float zNear,float zFar)
|
||||
|
|
|
|||
|
|
@ -303,16 +303,20 @@ void Arrow::tick()
|
|||
damageSource = DamageSource::arrow(dynamic_pointer_cast<Arrow>(shared_from_this()), owner);
|
||||
}
|
||||
|
||||
if(!res->entity->isInvulnerable())
|
||||
{
|
||||
if (isOnFire() && res->entity->GetType() != eTYPE_ENDERMAN)
|
||||
{
|
||||
res->entity->setOnFire(5);
|
||||
}
|
||||
}
|
||||
|
||||
if(res->entity->hurt(damageSource, dmg))
|
||||
{
|
||||
// Firx for #67839 - Customer Encountered: Bows enchanted with "Flame" still set things on fire if pvp/attack animals is turned off
|
||||
// 4J Stu - We should not set the entity on fire unless we can cause some damage (this doesn't necessarily mean that the arrow hit lowered their health)
|
||||
// set targets on fire first because we want cooked
|
||||
// pork/chicken/steak
|
||||
if (isOnFire() && res->entity->GetType() != eTYPE_ENDERMAN)
|
||||
{
|
||||
res->entity->setOnFire(5);
|
||||
}
|
||||
|
||||
if (res->entity->instanceof(eTYPE_LIVINGENTITY))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ BlockRegionUpdatePacket::BlockRegionUpdatePacket(int x, int y, int z, int xs, in
|
|||
// TODO - we should be using compressed data directly here rather than decompressing first and then recompressing...
|
||||
byteArray rawBuffer;
|
||||
|
||||
if( xs == 16 && ys == Level::maxBuildHeight && zs == 16 && ( ( x & 15 ) == 0 ) && ( y == 0 ) && ( ( z & 15 ) == 0 ) )
|
||||
if( xs == 16 && ys == Level::maxBuildHeight && zs == 16 && ( ( x & 15 ) == 0 ) && ( y == 0 ) && ( ( z & 15 ) == 0 ) )
|
||||
{
|
||||
bIsFullChunk = true;
|
||||
|
||||
|
|
@ -66,6 +66,8 @@ BlockRegionUpdatePacket::BlockRegionUpdatePacket(int x, int y, int z, int xs, in
|
|||
{
|
||||
size = 0;
|
||||
buffer = byteArray();
|
||||
app.DebugPrintf("[BRUP-SERVER] *** EMPTY BUFFER for chunk(%d,%d) ys=%d bIsFullChunk=%d\n",
|
||||
x>>4, z>>4, this->ys, bIsFullChunk ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -96,7 +98,7 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException
|
|||
zs = dis->read() + 1;
|
||||
|
||||
bIsFullChunk = (chunkFlags & BLOCK_REGION_UPDATE_FULLCHUNK) ? true : false;
|
||||
if(chunkFlags & BLOCK_REGION_UPDATE_ZEROHEIGHT)
|
||||
if(chunkFlags & BLOCK_REGION_UPDATE_ZEROHEIGHT)
|
||||
ys = 0;
|
||||
|
||||
size = dis->readInt();
|
||||
|
|
@ -106,6 +108,10 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException
|
|||
if(size == 0)
|
||||
{
|
||||
buffer = byteArray();
|
||||
if(bIsFullChunk)
|
||||
{
|
||||
app.DebugPrintf("[BRUP-READ] *** ZERO-SIZE full chunk packet at (%d,%d)!\n", x>>4, z>>4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -131,6 +137,11 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException
|
|||
|
||||
|
||||
delete [] compressedBuffer.data;
|
||||
if(buffer.length != outputSize)
|
||||
{
|
||||
app.DebugPrintf("*** BlockRegionUpdatePacket DECOMPRESS MISMATCH: expected=%d got=%d xs=%d ys=%d zs=%d fullChunk=%d compressedSize=%d levelIdx=%d\n",
|
||||
buffer.length, outputSize, xs, ys, zs, bIsFullChunk ? 1 : 0, size, levelIdx);
|
||||
}
|
||||
assert(buffer.length == outputSize);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ CompressedTileStorage::CompressedTileStorage(bool isEmpty)
|
|||
bool CompressedTileStorage::isRenderChunkEmpty(int y) // y == 0, 16, 32... 112 (representing a 16 byte range)
|
||||
{
|
||||
int block;
|
||||
unsigned short *blockIndices = (unsigned short *)indicesAndData;
|
||||
unsigned char *localIndicesAndData = indicesAndData;
|
||||
if(!localIndicesAndData) return true;
|
||||
unsigned short *blockIndices = (unsigned short *)localIndicesAndData;
|
||||
|
||||
for( int x = 0; x < 16; x += 4 )
|
||||
for( int z = 0; z < 16; z += 4 )
|
||||
|
|
@ -533,8 +535,12 @@ void CompressedTileStorage::getData(byteArray retArray, unsigned int retOffset)
|
|||
// Gets all tile values into an array of length 32768.
|
||||
void CompressedTileStorage::getData(byteArray retArray, unsigned int retOffset)
|
||||
{
|
||||
unsigned short *blockIndices = (unsigned short *)indicesAndData;
|
||||
unsigned char *data = indicesAndData + 1024;
|
||||
// Snapshot pointer to avoid race with compress() swapping indicesAndData
|
||||
unsigned char *localIndicesAndData = indicesAndData;
|
||||
if(!localIndicesAndData) return;
|
||||
|
||||
unsigned short *blockIndices = (unsigned short *)localIndicesAndData;
|
||||
unsigned char *data = localIndicesAndData + 1024;
|
||||
|
||||
for( int i = 0; i < 512; i++ )
|
||||
{
|
||||
|
|
@ -588,10 +594,13 @@ void CompressedTileStorage::getData(byteArray retArray, unsigned int retOffset)
|
|||
// Get an individual tile value
|
||||
int CompressedTileStorage::get(int x, int y, int z)
|
||||
{
|
||||
if(!indicesAndData) return 0;
|
||||
// Take a local snapshot of indicesAndData to avoid a race with compress() which swaps the pointer.
|
||||
// Both blockIndices and data must reference the same buffer, otherwise index offsets won't match.
|
||||
unsigned char *localIndicesAndData = indicesAndData;
|
||||
if(!localIndicesAndData) return 0;
|
||||
|
||||
unsigned short *blockIndices = (unsigned short *)indicesAndData;
|
||||
unsigned char *data = indicesAndData + 1024;
|
||||
unsigned short *blockIndices = (unsigned short *)localIndicesAndData;
|
||||
unsigned char *data = localIndicesAndData + 1024;
|
||||
|
||||
int block, tile;
|
||||
getBlockAndTile( &block, &tile, x, y, z );
|
||||
|
|
@ -1223,7 +1232,10 @@ int CompressedTileStorage::getAllocatedSize(int *count0, int *count1, int *count
|
|||
*count4 = 0;
|
||||
*count8 = 0;
|
||||
|
||||
unsigned short *blockIndices = (unsigned short *)indicesAndData;
|
||||
// Volatile read: compress() can swap indicesAndData concurrently outside cs_write
|
||||
unsigned char *localIndicesAndData = *(unsigned char *volatile *)&indicesAndData;
|
||||
if(!localIndicesAndData) return 0;
|
||||
unsigned short *blockIndices = (unsigned short *)localIndicesAndData;
|
||||
for(int i = 0; i < 512; i++ )
|
||||
{
|
||||
unsigned short idxType = blockIndices[i] & INDEX_TYPE_MASK;
|
||||
|
|
@ -1256,7 +1268,9 @@ int CompressedTileStorage::getAllocatedSize(int *count0, int *count1, int *count
|
|||
|
||||
int CompressedTileStorage::getHighestNonEmptyY()
|
||||
{
|
||||
unsigned short *blockIndices = (unsigned short *)indicesAndData;
|
||||
unsigned char *localIndicesAndData = indicesAndData;
|
||||
if(!localIndicesAndData) return -1;
|
||||
unsigned short *blockIndices = (unsigned short *)localIndicesAndData;
|
||||
unsigned int highestYBlock = 0;
|
||||
bool found = false;
|
||||
|
||||
|
|
@ -1297,19 +1311,26 @@ int CompressedTileStorage::getHighestNonEmptyY()
|
|||
// Multiply by the number of vertical tiles in a block, and then add that again to be at the top of the block
|
||||
highestNonEmptyY = (highestYBlock * 4) + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
app.DebugPrintf("[CTS-WARN] getHighestNonEmptyY() returned -1! allocatedSize=%d indicesAndData=%p\n",
|
||||
allocatedSize, indicesAndData);
|
||||
}
|
||||
return highestNonEmptyY;
|
||||
}
|
||||
|
||||
void CompressedTileStorage::write(DataOutputStream *dos)
|
||||
{
|
||||
dos->writeInt(allocatedSize);
|
||||
if(indicesAndData)
|
||||
// Volatile read: compress() can swap indicesAndData concurrently outside cs_write
|
||||
unsigned char *localIndicesAndData = *(unsigned char *volatile *)&indicesAndData;
|
||||
if(localIndicesAndData)
|
||||
{
|
||||
if(LOCALSYTEM_ENDIAN == BIGENDIAN)
|
||||
{
|
||||
// The first 1024 bytes are an array of shorts, so we need to reverse the endianness
|
||||
byteArray indicesCopy(1024);
|
||||
memcpy(indicesCopy.data, indicesAndData, 1024);
|
||||
memcpy(indicesCopy.data, localIndicesAndData, 1024);
|
||||
reverseIndices(indicesCopy.data);
|
||||
dos->write(indicesCopy);
|
||||
delete [] indicesCopy.data;
|
||||
|
|
@ -1317,13 +1338,13 @@ void CompressedTileStorage::write(DataOutputStream *dos)
|
|||
// Write the rest of the data
|
||||
if(allocatedSize > 1024)
|
||||
{
|
||||
byteArray dataWrapper(indicesAndData + 1024, allocatedSize - 1024);
|
||||
byteArray dataWrapper(localIndicesAndData + 1024, allocatedSize - 1024);
|
||||
dos->write(dataWrapper);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byteArray wrapper(indicesAndData, allocatedSize);
|
||||
byteArray wrapper(localIndicesAndData, allocatedSize);
|
||||
dos->write(wrapper);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,6 +250,14 @@ bool Connection::writeTick()
|
|||
// Otherwise just buffer the packet with other outgoing packets as the java game did
|
||||
if(packet->shouldDelay)
|
||||
{
|
||||
// Flush any buffered data BEFORE writing directly to the socket.
|
||||
// bufferedDos and sos->writeWithFlags both write to the same underlying
|
||||
// socket stream. If bufferedDos has unflushed bytes (from packets written
|
||||
// via the outgoing queue above), writing directly to sos here would send
|
||||
// the delayed packet's bytes BEFORE the buffered bytes, desynchronizing
|
||||
// the TCP stream on the receiving end.
|
||||
bufferedDos->flush();
|
||||
|
||||
Packet::writePacket(packet, byteArrayDos);
|
||||
|
||||
// 4J Stu - Changed this so that rather than writing to the network stream through a buffered stream we want to:
|
||||
|
|
@ -341,7 +349,7 @@ bool Connection::readTick()
|
|||
// printf("Con:0x%x readTick close EOS\n",this);
|
||||
|
||||
// 4J Stu - Remove this line
|
||||
// Fix for #10410 - UI: If the player is removed from a splitscreened host’s game, the next game that player joins will produce a message stating that the host has left.
|
||||
// Fix for #10410 - UI: If the player is removed from a splitscreened host<EFBFBD>s game, the next game that player joins will produce a message stating that the host has left.
|
||||
//close(DisconnectPacket::eDisconnect_EndOfStream);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4240,6 +4240,10 @@ void Level::setBlocksAndData(int x, int y, int z, int xs, int ys, int zs, byteAr
|
|||
if (z0 < 0) z0 = 0;
|
||||
if (z1 > 16) z1 = 16;
|
||||
LevelChunk *lc = getChunk(xc, zc);
|
||||
if(lc->isEmpty())
|
||||
{
|
||||
app.DebugPrintf("[SETBLOCKS-BUG] getChunk(%d,%d) returned EmptyLevelChunk! Data will be LOST\n", xc, zc);
|
||||
}
|
||||
// 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us
|
||||
// Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water.
|
||||
// This is quite expensive so only actually do it if we are hosting, online, and the update will actually
|
||||
|
|
|
|||
|
|
@ -327,9 +327,30 @@ shared_ptr<Packet> Packet::readPacket(DataInputStream *dis, bool isServer) // th
|
|||
id = dis->read();
|
||||
if (id == -1) return nullptr;
|
||||
|
||||
// Track last few good packets for diagnosing TCP desync
|
||||
static thread_local int s_lastIds[8] = {};
|
||||
static thread_local int s_lastIdPos = 0;
|
||||
static thread_local int s_packetCount = 0;
|
||||
|
||||
if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end()))
|
||||
{
|
||||
//app.DebugPrintf("Bad packet id %d\n", id);
|
||||
app.DebugPrintf("*** BAD PACKET ID %d (0x%02X) isServer=%d totalPacketsRead=%d\n", id, id, isServer ? 1 : 0, s_packetCount);
|
||||
app.DebugPrintf("*** Last %d good packet IDs (oldest first): ", 8);
|
||||
for (int dbg = 0; dbg < 8; dbg++)
|
||||
{
|
||||
int idx = (s_lastIdPos + dbg) % 8;
|
||||
app.DebugPrintf("%d ", s_lastIds[idx]);
|
||||
}
|
||||
app.DebugPrintf("\n");
|
||||
// Dump the next 32 bytes from the stream to see what follows
|
||||
app.DebugPrintf("*** Next bytes in stream: ");
|
||||
for (int dbg = 0; dbg < 32; dbg++)
|
||||
{
|
||||
int b = dis->read();
|
||||
if (b == -1) { app.DebugPrintf("[EOS] "); break; }
|
||||
app.DebugPrintf("%02X ", b);
|
||||
}
|
||||
app.DebugPrintf("\n");
|
||||
__debugbreak();
|
||||
assert(false);
|
||||
// throw new IOException(wstring(L"Bad packet id ") + std::to_wstring(id));
|
||||
|
|
@ -338,7 +359,10 @@ shared_ptr<Packet> Packet::readPacket(DataInputStream *dis, bool isServer) // th
|
|||
packet = getPacket(id);
|
||||
if (packet == NULL) assert(false);//throw new IOException(wstring(L"Bad packet id ") + std::to_wstring(id));
|
||||
|
||||
//app.DebugPrintf("%s reading packet %d\n", isServer ? "Server" : "Client", packet->getId());
|
||||
s_lastIds[s_lastIdPos] = id;
|
||||
s_lastIdPos = (s_lastIdPos + 1) % 8;
|
||||
s_packetCount++;
|
||||
|
||||
packet->read(dis);
|
||||
// }
|
||||
// catch (EOFException e)
|
||||
|
|
@ -372,7 +396,7 @@ shared_ptr<Packet> Packet::readPacket(DataInputStream *dis, bool isServer) // th
|
|||
|
||||
void Packet::writePacket(shared_ptr<Packet> packet, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws?
|
||||
{
|
||||
//app.DebugPrintf("Writing packet %d\n", packet->getId());
|
||||
//app.DebugPrintf("NET WRITE: packet id=%d (0x%02X) estSize=%d\n", packet->getId(), packet->getId(), packet->getEstimatedSize());
|
||||
dos->write(packet->getId());
|
||||
packet->write(dos);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@ void Socket::pushDataToQueue(const BYTE * pbData, DWORD dwDataSize, bool fromHos
|
|||
return;
|
||||
}
|
||||
|
||||
//app.DebugPrintf("SOCKET PUSH: %u bytes to queue[%d] firstByte=0x%02X smallId=%d\n",
|
||||
// dwDataSize, queueIdx, dwDataSize > 0 ? pbData[0] : 0, networkPlayerSmallId);
|
||||
|
||||
EnterCriticalSection(&m_queueLockNetwork[queueIdx]);
|
||||
for( unsigned int i = 0; i < dwDataSize; i++ )
|
||||
{
|
||||
|
|
@ -311,7 +314,8 @@ void Socket::SocketInputStreamLocal::close()
|
|||
{
|
||||
m_streamOpen = false;
|
||||
EnterCriticalSection(&s_hostQueueLock[m_queueIdx]);
|
||||
s_hostQueue[m_queueIdx].empty();
|
||||
std::queue<byte> empty;
|
||||
std::swap(s_hostQueue[m_queueIdx], empty);
|
||||
LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]);
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +363,8 @@ void Socket::SocketOutputStreamLocal::close()
|
|||
{
|
||||
m_streamOpen = false;
|
||||
EnterCriticalSection(&s_hostQueueLock[m_queueIdx]);
|
||||
s_hostQueue[m_queueIdx].empty();
|
||||
std::queue<byte> empty;
|
||||
std::swap(s_hostQueue[m_queueIdx], empty);
|
||||
LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,9 +59,27 @@ HRESULT Compression::CompressLZXRLE(void *pDestination, unsigned int *pDestSize,
|
|||
EnterCriticalSection(&rleCompressLock);
|
||||
//static unsigned char rleBuf[1024*100];
|
||||
|
||||
// RLE can expand data (each 0xFF byte becomes 2 bytes), so worst case is 2*SrcSize.
|
||||
// Use the static buffer when it fits, dynamically allocate otherwise.
|
||||
static const unsigned int staticCompressSize = 1024*100;
|
||||
unsigned int rleBufSize = SrcSize * 2;
|
||||
unsigned char *dynamicRleBuf = NULL;
|
||||
unsigned char *rleBuf;
|
||||
|
||||
if(rleBufSize <= staticCompressSize)
|
||||
{
|
||||
rleBuf = rleCompressBuf;
|
||||
rleBufSize = staticCompressSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
dynamicRleBuf = new unsigned char[rleBufSize];
|
||||
rleBuf = dynamicRleBuf;
|
||||
}
|
||||
|
||||
unsigned char *pucIn = (unsigned char *)pSource;
|
||||
unsigned char *pucEnd = pucIn + SrcSize;
|
||||
unsigned char *pucOut = (unsigned char *)rleCompressBuf;
|
||||
unsigned char *pucOut = rleBuf;
|
||||
|
||||
// Compress with RLE first:
|
||||
// 0 - 254 - encodes a single byte
|
||||
|
|
@ -101,12 +119,15 @@ HRESULT Compression::CompressLZXRLE(void *pDestination, unsigned int *pDestSize,
|
|||
*pucOut++ = thisOne;
|
||||
}
|
||||
} while (pucIn != pucEnd);
|
||||
unsigned int rleSize = (unsigned int)(pucOut - rleCompressBuf);
|
||||
unsigned int rleSize = (unsigned int)(pucOut - rleBuf);
|
||||
PIXEndNamedEvent();
|
||||
|
||||
PIXBeginNamedEvent(0,"Secondary compression");
|
||||
Compress(pDestination, pDestSize, rleCompressBuf, rleSize);
|
||||
Compress(pDestination, pDestSize, rleBuf, rleSize);
|
||||
PIXEndNamedEvent();
|
||||
|
||||
if(dynamicRleBuf != NULL) delete [] dynamicRleBuf;
|
||||
|
||||
LeaveCriticalSection(&rleCompressLock);
|
||||
// printf("Compressed from %d to %d to %d\n",SrcSize,rleSize,*pDestSize);
|
||||
|
||||
|
|
@ -118,9 +139,26 @@ HRESULT Compression::CompressRLE(void *pDestination, unsigned int *pDestSize, vo
|
|||
EnterCriticalSection(&rleCompressLock);
|
||||
//static unsigned char rleBuf[1024*100];
|
||||
|
||||
// RLE can expand data (each 0xFF byte becomes 2 bytes), so worst case is 2*SrcSize.
|
||||
static const unsigned int staticCompressSize = 1024*100;
|
||||
unsigned int rleBufSize = SrcSize * 2;
|
||||
unsigned char *dynamicRleBuf = NULL;
|
||||
unsigned char *rleBuf;
|
||||
|
||||
if(rleBufSize <= staticCompressSize)
|
||||
{
|
||||
rleBuf = rleCompressBuf;
|
||||
rleBufSize = staticCompressSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
dynamicRleBuf = new unsigned char[rleBufSize];
|
||||
rleBuf = dynamicRleBuf;
|
||||
}
|
||||
|
||||
unsigned char *pucIn = (unsigned char *)pSource;
|
||||
unsigned char *pucEnd = pucIn + SrcSize;
|
||||
unsigned char *pucOut = (unsigned char *)rleCompressBuf;
|
||||
unsigned char *pucOut = rleBuf;
|
||||
|
||||
// Compress with RLE first:
|
||||
// 0 - 254 - encodes a single byte
|
||||
|
|
@ -160,15 +198,14 @@ HRESULT Compression::CompressRLE(void *pDestination, unsigned int *pDestSize, vo
|
|||
*pucOut++ = thisOne;
|
||||
}
|
||||
} while (pucIn != pucEnd);
|
||||
unsigned int rleSize = (unsigned int)(pucOut - rleCompressBuf);
|
||||
unsigned int rleSize = (unsigned int)(pucOut - rleBuf);
|
||||
PIXEndNamedEvent();
|
||||
LeaveCriticalSection(&rleCompressLock);
|
||||
|
||||
// Return
|
||||
if (rleSize <= *pDestSize)
|
||||
{
|
||||
*pDestSize = rleSize;
|
||||
memcpy(pDestination, rleCompressBuf, *pDestSize);
|
||||
memcpy(pDestination, rleBuf, *pDestSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -177,6 +214,9 @@ HRESULT Compression::CompressRLE(void *pDestination, unsigned int *pDestSize, vo
|
|||
#endif
|
||||
}
|
||||
|
||||
if(dynamicRleBuf != NULL) delete [] dynamicRleBuf;
|
||||
LeaveCriticalSection(&rleCompressLock);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -195,33 +235,47 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz
|
|||
//static unsigned char rleBuf[staticRleSize];
|
||||
unsigned int rleSize = staticRleSize;
|
||||
unsigned char *dynamicRleBuf = NULL;
|
||||
HRESULT decompressResult;
|
||||
|
||||
if(*pDestSize > rleSize)
|
||||
{
|
||||
rleSize = *pDestSize;
|
||||
dynamicRleBuf = new unsigned char[rleSize];
|
||||
Decompress(dynamicRleBuf, &rleSize, pSource, SrcSize);
|
||||
decompressResult = Decompress(dynamicRleBuf, &rleSize, pSource, SrcSize);
|
||||
pucIn = (unsigned char *)dynamicRleBuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
Decompress(rleDecompressBuf, &rleSize, pSource, SrcSize);
|
||||
decompressResult = Decompress(rleDecompressBuf, &rleSize, pSource, SrcSize);
|
||||
pucIn = (unsigned char *)rleDecompressBuf;
|
||||
}
|
||||
|
||||
if(decompressResult != S_OK)
|
||||
{
|
||||
app.DebugPrintf("*** DecompressLZXRLE: zlib Decompress FAILED hr=0x%08X srcSize=%u expectedDest=%u rleSize=%u\n",
|
||||
decompressResult, SrcSize, *pDestSize, rleSize);
|
||||
if(dynamicRleBuf != NULL) delete [] dynamicRleBuf;
|
||||
*pDestSize = 0;
|
||||
LeaveCriticalSection(&rleDecompressLock);
|
||||
return decompressResult;
|
||||
}
|
||||
|
||||
//unsigned char *pucIn = (unsigned char *)rleDecompressBuf;
|
||||
unsigned char *pucEnd = pucIn + rleSize;
|
||||
unsigned char *pucOut = (unsigned char *)pDestination;
|
||||
unsigned char *pucOutEnd = pucOut + *pDestSize;
|
||||
|
||||
while( pucIn != pucEnd )
|
||||
{
|
||||
unsigned char thisOne = *pucIn++;
|
||||
if( thisOne == 255 )
|
||||
{
|
||||
if( pucIn >= pucEnd ) break;
|
||||
unsigned int count = *pucIn++;
|
||||
if( count < 3 )
|
||||
{
|
||||
count++;
|
||||
if( pucOut + count > pucOutEnd ) break;
|
||||
for( unsigned int i = 0; i < count; i++ )
|
||||
{
|
||||
*pucOut++ = 255;
|
||||
|
|
@ -230,7 +284,9 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz
|
|||
else
|
||||
{
|
||||
count++;
|
||||
if( pucIn >= pucEnd ) break;
|
||||
unsigned char data = *pucIn++;
|
||||
if( pucOut + count > pucOutEnd ) break;
|
||||
for( unsigned int i = 0; i < count; i++ )
|
||||
{
|
||||
*pucOut++ = data;
|
||||
|
|
@ -239,6 +295,7 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz
|
|||
}
|
||||
else
|
||||
{
|
||||
if( pucOut >= pucOutEnd ) break;
|
||||
*pucOut++ = thisOne;
|
||||
}
|
||||
}
|
||||
|
|
@ -260,16 +317,19 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize,
|
|||
unsigned char *pucIn = (unsigned char *)pSource;
|
||||
unsigned char *pucEnd = pucIn + SrcSize;
|
||||
unsigned char *pucOut = (unsigned char *)pDestination;
|
||||
unsigned char *pucOutEnd = pucOut + *pDestSize;
|
||||
|
||||
while( pucIn != pucEnd )
|
||||
{
|
||||
unsigned char thisOne = *pucIn++;
|
||||
if( thisOne == 255 )
|
||||
{
|
||||
if( pucIn >= pucEnd ) break;
|
||||
unsigned int count = *pucIn++;
|
||||
if( count < 3 )
|
||||
{
|
||||
count++;
|
||||
if( pucOut + count > pucOutEnd ) break;
|
||||
for( unsigned int i = 0; i < count; i++ )
|
||||
{
|
||||
*pucOut++ = 255;
|
||||
|
|
@ -278,7 +338,9 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize,
|
|||
else
|
||||
{
|
||||
count++;
|
||||
if( pucIn >= pucEnd ) break;
|
||||
unsigned char data = *pucIn++;
|
||||
if( pucOut + count > pucOutEnd ) break;
|
||||
for( unsigned int i = 0; i < count; i++ )
|
||||
{
|
||||
*pucOut++ = data;
|
||||
|
|
@ -287,6 +349,7 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize,
|
|||
}
|
||||
else
|
||||
{
|
||||
if( pucOut >= pucOutEnd ) break;
|
||||
*pucOut++ = thisOne;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue