Fix Ender Dragon damage, End portal transition, and End Poem crash

Dragon melee damage: reassign sub-entity IDs to be sequential from
the parent entity ID in ServerLevel::entityAdded(), so the client's
offset-based ID calculation matches the server. Previously the server's
smallId pool allocated non-sequential IDs, causing melee attacks to
target entity IDs the server didn't recognize.

End portal transition: ensure the player entity is always added to the
new level when transitioning from The End, not just for non-End
dimensions. The addEntity call was previously gated behind a
lastDimension != 1 check that also excluded it from End exits.

End Poem crash: bounds-check the WIN_GAME event's player index before
accessing localplayers[], with a fallback to prevent null dereference
when the server sends an out-of-range index.
This commit is contained in:
itsRevela 2026-03-26 22:25:14 -05:00
parent 73d713878c
commit 17ed3d2b9a
5 changed files with 22 additions and 22 deletions

View file

@ -50,13 +50,18 @@ UIScene_EndPoem::UIScene_EndPoem(int iPad, void *initData, UILayer *parentLayer)
Minecraft *pMinecraft = Minecraft::GetInstance(); Minecraft *pMinecraft = Minecraft::GetInstance();
wstring playerName = L""; wstring playerName = L"";
if(pMinecraft->localplayers[ui.GetWinUserIndex()] != nullptr) unsigned int winIdx = ui.GetWinUserIndex();
if(winIdx < XUSER_MAX_COUNT && pMinecraft->localplayers[winIdx] != nullptr)
{ {
playerName = escapeXML( pMinecraft->localplayers[ui.GetWinUserIndex()]->getDisplayName() ); playerName = escapeXML( pMinecraft->localplayers[winIdx]->getDisplayName() );
}
else if(pMinecraft->localplayers[ProfileManager.GetPrimaryPad()] != nullptr)
{
playerName = escapeXML( pMinecraft->localplayers[ProfileManager.GetPrimaryPad()]->getDisplayName() );
} }
else else
{ {
playerName = escapeXML( pMinecraft->localplayers[ProfileManager.GetPrimaryPad()]->getDisplayName() ); playerName = L"Player";
} }
noNoiseString = replaceAll(noNoiseString,L"{*PLAYER*}",playerName); noNoiseString = replaceAll(noNoiseString,L"{*PLAYER*}",playerName);

View file

@ -816,9 +816,7 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
{ {
if ((target->GetType() == eTYPE_ITEMENTITY) || (target->GetType() == eTYPE_EXPERIENCEORB) || (target->GetType() == eTYPE_ARROW) || target == player) if ((target->GetType() == eTYPE_ITEMENTITY) || (target->GetType() == eTYPE_EXPERIENCEORB) || (target->GetType() == eTYPE_ARROW) || target == player)
{ {
//disconnect("Attempting to attack an invalid entity"); return;
//server.warn("Player " + player.getName() + " tried to attack an invalid entity");
return;
} }
player->attack(target); player->attack(target);
} }

View file

@ -951,15 +951,16 @@ void PlayerList::repositionAcrossDimension(shared_ptr<Entity> entity, int lastDi
addPlayerToReceiving(player); addPlayerToReceiving(player);
} }
if (lastDimension != 1) xt = static_cast<double>(Mth::clamp(static_cast<int>(xt), -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128));
zt = static_cast<double>(Mth::clamp(static_cast<int>(zt), -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128));
if (entity->isAlive())
{ {
xt = static_cast<double>(Mth::clamp(static_cast<int>(xt), -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128)); newLevel->addEntity(entity);
zt = static_cast<double>(Mth::clamp(static_cast<int>(zt), -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128)); entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot);
if (entity->isAlive()) newLevel->tick(entity, false);
// Portal forcing only for non-End exits (End exits go to spawn, not a portal)
if (lastDimension != 1)
{ {
newLevel->addEntity(entity);
entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot);
newLevel->tick(entity, false);
newLevel->cache->autoCreate = true; newLevel->cache->autoCreate = true;
newLevel->getPortalForcer()->force(entity, xOriginal, yOriginal, zOriginal, yRotOriginal); newLevel->getPortalForcer()->force(entity, xOriginal, yOriginal, zOriginal, yRotOriginal);
newLevel->cache->autoCreate = false; newLevel->cache->autoCreate = false;

View file

@ -1052,9 +1052,14 @@ void ServerLevel::entityAdded(shared_ptr<Entity> e)
vector<shared_ptr<Entity> > *es = e->getSubEntities(); vector<shared_ptr<Entity> > *es = e->getSubEntities();
if (es) if (es)
{ {
// Reassign sub-entity IDs to be sequential from the parent's ID.
// The client assumes this layout when it applies an offset in handleAddMob.
int offset = 1;
for(auto& i : *es) for(auto& i : *es)
{ {
i->entityId = e->entityId + offset;
entitiesById.emplace(i->entityId, i); entitiesById.emplace(i->entityId, i);
offset++;
} }
} }
entityAddedExtra(e); // 4J added entityAddedExtra(e); // 4J added

View file

@ -1118,15 +1118,6 @@ bool EnderDragon::hurt(shared_ptr<MultiEntityMobPart> MultiEntityMobPart, Damage
damage = damage / 4 + 1; damage = damage / 4 + 1;
} }
//float rot1 = yRot * PI / 180;
//float ss1 = sin(rot1);
//float cc1 = cos(rot1);
//xTarget = x + ss1 * 5 + (random->nextFloat() - 0.5f) * 2;
//yTarget = y + random->nextFloat() * 3 + 1;
//zTarget = z - cc1 * 5 + (random->nextFloat() - 0.5f) * 2;
//attackTarget = nullptr;
if ( source->getEntity() != nullptr && source->getEntity()->instanceof(eTYPE_PLAYER) || source->isExplosion() ) if ( source->getEntity() != nullptr && source->getEntity()->instanceof(eTYPE_PLAYER) || source->isExplosion() )
{ {
int healthBefore = getHealth(); int healthBefore = getHealth();