aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/TrackedEntity.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/TrackedEntity.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/TrackedEntity.cpp')
-rw-r--r--Minecraft.Client/TrackedEntity.cpp929
1 files changed, 929 insertions, 0 deletions
diff --git a/Minecraft.Client/TrackedEntity.cpp b/Minecraft.Client/TrackedEntity.cpp
new file mode 100644
index 00000000..70f25c93
--- /dev/null
+++ b/Minecraft.Client/TrackedEntity.cpp
@@ -0,0 +1,929 @@
+#include "stdafx.h"
+#include "TrackedEntity.h"
+#include "ServerPlayer.h"
+#include "PlayerConnection.h"
+#include "..\Minecraft.World\Mth.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.item.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.monster.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.animal.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.global.h"
+#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h"
+#include "..\Minecraft.World\net.minecraft.network.packet.h"
+#include "..\Minecraft.World\net.minecraft.world.item.h"
+#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h"
+#include "MinecraftServer.h"
+#include "ServerLevel.h"
+#include "PlayerList.h"
+#include "EntityTracker.h"
+#include "PlayerChunkMap.h"
+#include <qnet.h>
+
+TrackedEntity::TrackedEntity(shared_ptr<Entity> e, int range, int updateInterval, bool trackDelta)
+{
+ // 4J added initialisers
+ xap = yap = zap = 0;
+ tickCount = 0;
+ xpu = ypu = zpu = 0;
+ updatedPlayerVisibility = false;
+ teleportDelay = 0;
+ moved = false;
+
+ this->e = e;
+ this->range = range;
+ this->updateInterval = updateInterval;
+ this->trackDelta = trackDelta;
+
+ xp = Mth::floor(e->x * 32);
+ yp = Mth::floor(e->y * 32);
+ zp = Mth::floor(e->z * 32);
+
+ yRotp = Mth::floor(e->yRot * 256 / 360);
+ xRotp = Mth::floor(e->xRot * 256 / 360);
+ yHeadRotp = Mth::floor(e->getYHeadRot() * 256 / 360);
+}
+
+int c0a = 0, c0b = 0, c1a = 0, c1b = 0, c1c = 0, c2a = 0, c2b = 0;
+
+void TrackedEntity::tick(EntityTracker *tracker, vector<shared_ptr<Player> > *players)
+{
+ moved = false;
+ if (!updatedPlayerVisibility || e->distanceToSqr(xpu, ypu, zpu) > 4 * 4)
+ {
+ xpu = e->x;
+ ypu = e->y;
+ zpu = e->z;
+ updatedPlayerVisibility = true;
+ moved = true;
+ updatePlayers(tracker, players);
+ }
+
+ if (wasRiding != e->riding)
+ {
+ wasRiding = e->riding;
+ broadcast(shared_ptr<SetRidingPacket>(new SetRidingPacket(e, e->riding)));
+ }
+
+ // Moving forward special case for item frames
+ if (e->GetType()== eTYPE_ITEM_FRAME && tickCount % 10 == 0)
+ {
+ shared_ptr<ItemFrame> frame = dynamic_pointer_cast<ItemFrame> (e);
+ shared_ptr<ItemInstance> item = frame->getItem();
+
+ if (item != NULL && item->getItem()->id == Item::map_Id && !e->removed)
+ {
+ shared_ptr<MapItemSavedData> data = Item::map->getSavedData(item, e->level);
+ for (AUTO_VAR(it,players->begin() ); it != players->end(); ++it)
+ {
+ shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(*it);
+ data->tickCarriedBy(player, item);
+
+ if (!player->removed && player->connection && player->connection->countDelayedPackets() <= 5)
+ {
+ shared_ptr<Packet> packet = Item::map->getUpdatePacket(item, e->level, player);
+ if (packet != NULL) player->connection->send(packet);
+ }
+ }
+ }
+
+ shared_ptr<SynchedEntityData> entityData = e->getEntityData();
+ if (entityData->isDirty())
+ {
+ broadcastAndSend( shared_ptr<SetEntityDataPacket>( new SetEntityDataPacket(e->entityId, entityData, false) ) );
+ }
+ }
+ else
+ {
+ if(e->riding == NULL)
+ {
+ teleportDelay++;
+ if (tickCount++ % updateInterval == 0 || e->hasImpulse)
+ {
+ int xn = Mth::floor(this->e->x * 32.0);
+ int yn = Mth::floor(this->e->y * 32.0);
+ int zn = Mth::floor(this->e->z * 32.0);
+ int yRotn = Mth::floor(e->yRot * 256 / 360);
+ int xRotn = Mth::floor(e->xRot * 256 / 360);
+
+
+ int xa = xn - xp;
+ int ya = yn - yp;
+ int za = zn - zp;
+
+ shared_ptr<Packet> packet = nullptr;
+
+ // 4J - this pos flag used to be set based on abs(xn) etc. but that just seems wrong
+ bool pos = abs(xa) >= TOLERANCE_LEVEL || abs(ya) >= TOLERANCE_LEVEL || abs(za) >= TOLERANCE_LEVEL;
+ // 4J - changed rotation to be generally sent as a delta as well as position
+ int yRota = yRotn - yRotp;
+ int xRota = xRotn - xRotp;
+ // Keep rotation deltas in +/- 180 degree range
+ while( yRota > 127 ) yRota -= 256;
+ while( yRota < -128 ) yRota += 256;
+ while( xRota > 127 ) xRota -= 256;
+ while( xRota < -128 ) xRota += 256;
+
+ bool rot = abs(yRota) >= TOLERANCE_LEVEL || abs(xRota) >= TOLERANCE_LEVEL;
+
+ if (xa < -128 || xa >= 128 || ya < -128 || ya >= 128 || za < -128 || za >= 128
+ // 4J Stu - I fixed the initialisation of teleportDelay in the ctor, but we managed this far without out
+ // and would prefer not to have all the extra traffix so ignore it
+ // 4J Stu - Fix for #9579 - GAMEPLAY: Boats with a player in them slowly sink under the water over time, and with no player in them they float into the sky.
+ || (e->GetType() == eTYPE_BOAT && teleportDelay > 20 * 20)
+ )
+ {
+ teleportDelay = 0;
+ packet = shared_ptr<TeleportEntityPacket>( new TeleportEntityPacket(e->entityId, xn, yn, zn, (byte) yRotn, (byte) xRotn) );
+ // printf("%d: New teleport rot %d\n",e->entityId,yRotn);
+ yRotp = yRotn;
+ xRotp = xRotn;
+ }
+ else
+ {
+ if (pos && rot)
+ {
+ // 4J If the movement is small enough, and there's no xrot, then use the new smaller packets
+ if( ( xa >= -16 ) && ( xa <= 15 ) &&
+ ( za >= -16 ) && ( za <= 15 ) &&
+ ( ya >= -32 ) && ( ya <= 31 ) &&
+ ( xRota == 0 ))
+ {
+ // Clamp rotations that are too big
+ if( yRota < -16 )
+ {
+ yRota = -16;
+ yRotn = yRotp + yRota;
+ }
+ else if( yRota > 15 )
+ {
+ yRota = 15;
+ yRotn = yRotp + yRota;
+ }
+ // 5 bits each for x & z, and 6 for y
+ packet = shared_ptr<MoveEntityPacketSmall>( new MoveEntityPacketSmall::PosRot(e->entityId, (char) xa, (char) ya, (char) za, (char) yRota, 0 ) );
+ c0a++;
+ }
+ else
+ {
+ packet = shared_ptr<MoveEntityPacket>( new MoveEntityPacket::PosRot(e->entityId, (char) xa, (char) ya, (char) za, (char) yRota, (char) xRota) );
+ // printf("%d: New posrot %d + %d = %d\n",e->entityId,yRotp,yRota,yRotn);
+ c0b++;
+ }
+ }
+ else if (pos)
+ {
+ // 4J If the movement is small enough, then use the new smaller packets
+ if( ( xa >= -8 ) && ( xa <= 7 ) &&
+ ( za >= -8 ) && ( za <= 7 ) &&
+ ( ya >= -16 ) && ( ya <= 15 ) )
+ {
+ // 4 bits each for x & z, and 5 for y
+ packet = shared_ptr<MoveEntityPacketSmall>( new MoveEntityPacketSmall::Pos(e->entityId, (char) xa, (char) ya, (char) za) );
+ c1a++;
+ }
+
+ else if( ( xa >= -16 ) && ( xa <= 15 ) &&
+ ( za >= -16 ) && ( za <= 15 ) &&
+ ( ya >= -32 ) && ( ya <= 31 ) )
+ {
+ // use the packet with small packet with rotation if we can - 5 bits each for x & z, and 6 for y - still a byte less than the alternative
+ packet = shared_ptr<MoveEntityPacketSmall>( new MoveEntityPacketSmall::PosRot(e->entityId, (char) xa, (char) ya, (char) za, 0, 0 ));
+ c1b++;
+ }
+ else
+ {
+ packet = shared_ptr<MoveEntityPacket>( new MoveEntityPacket::Pos(e->entityId, (char) xa, (char) ya, (char) za) );
+ c1c++;
+ }
+ }
+ else if (rot)
+ {
+ // 4J If there's no x rotation, then use the new smaller packet type
+ if( xRota == 0 )
+ {
+ // Clamp rotations that are too big
+ if( yRota < -16 )
+ {
+ yRota = -16;
+ yRotn = yRotp + yRota;
+ }
+ else if( yRota > 15 )
+ {
+ yRota = 15;
+ yRotn = yRotp + yRota;
+ }
+ packet = shared_ptr<MoveEntityPacketSmall>( new MoveEntityPacketSmall::Rot(e->entityId, (char) yRota, 0) );
+ c2a++;
+ }
+ else
+ {
+ // printf("%d: New rot %d + %d = %d\n",e->entityId,yRotp,yRota,yRotn);
+ packet = shared_ptr<MoveEntityPacket>( new MoveEntityPacket::Rot(e->entityId, (char) yRota, (char) xRota) );
+ c2b++;
+ }
+ }
+ }
+
+ if (trackDelta)
+ {
+ double xad = e->xd - xap;
+ double yad = e->yd - yap;
+ double zad = e->zd - zap;
+
+ double max = 0.02;
+
+ double diff = xad * xad + yad * yad + zad * zad;
+
+ if (diff > max * max || (diff > 0 && e->xd == 0 && e->yd == 0 && e->zd == 0))
+ {
+ xap = e->xd;
+ yap = e->yd;
+ zap = e->zd;
+ broadcast( shared_ptr<SetEntityMotionPacket>( new SetEntityMotionPacket(e->entityId, xap, yap, zap) ) );
+ }
+
+ }
+
+ if (packet != NULL)
+ {
+ broadcast(packet);
+ }
+
+ shared_ptr<SynchedEntityData> entityData = e->getEntityData();
+
+ if (entityData->isDirty())
+ {
+ broadcastAndSend( shared_ptr<SetEntityDataPacket>( new SetEntityDataPacket(e->entityId, entityData, false) ) );
+ }
+
+ int yHeadRot = Mth::floor(e->getYHeadRot() * 256 / 360);
+ if (abs(yHeadRot - yHeadRotp) >= TOLERANCE_LEVEL)
+ {
+ broadcast(shared_ptr<RotateHeadPacket>(new RotateHeadPacket(e->entityId, (byte) yHeadRot)));
+ yHeadRotp = yHeadRot;
+ }
+
+ if (pos)
+ {
+ xp = xn;
+ yp = yn;
+ zp = zn;
+ }
+ if (rot)
+ {
+ yRotp = yRotn;
+ xRotp = xRotn;
+ }
+
+ // if( dynamic_pointer_cast<ServerPlayer>(e) != NULL )
+ // {
+ // printf("%d: %d + %d = %d (%f)\n",e->entityId,xRotp,xRota,xRotn,e->xRot);
+ // }
+ }
+
+ }
+ else // 4J-JEV: Added: Mobs in minecarts weren't synching their invisibility.
+ {
+ shared_ptr<SynchedEntityData> entityData = e->getEntityData();
+ if (entityData->isDirty())
+ broadcastAndSend( shared_ptr<SetEntityDataPacket>( new SetEntityDataPacket(e->entityId, entityData, false) ) );
+ }
+ e->hasImpulse = false;
+ }
+
+ if (e->hurtMarked)
+ {
+ // broadcast(new AnimatePacket(e, AnimatePacket.HURT));
+ broadcastAndSend( shared_ptr<SetEntityMotionPacket>( new SetEntityMotionPacket(e) ) );
+ e->hurtMarked = false;
+ }
+
+}
+
+void TrackedEntity::broadcast(shared_ptr<Packet> packet)
+{
+ if( Packet::canSendToAnyClient( packet ) )
+ {
+ // 4J-PB - due to the knockback on a player being hit, we need to send to all players, but limit the network traffic here to players that have not already had it sent to their system
+ vector< shared_ptr<ServerPlayer> > sentTo;
+
+ // 4J - don't send to a player we've already sent this data to that shares the same machine.
+ // EntityMotionPacket used to limit themselves to sending once to each machine
+ // by only sending to the primary player on each machine. This was causing trouble for split screen
+ // as only the primary player would get a knockback velocity. Now these packets can be sent to any
+ // player, but we try to restrict the network impact this has by not resending to the one machine
+
+ for( AUTO_VAR(it, seenBy.begin()); it != seenBy.end(); it++ )
+ {
+ shared_ptr<ServerPlayer> player = *it;
+ bool dontSend = false;
+ if( sentTo.size() )
+ {
+ INetworkPlayer *thisPlayer =player->connection->getNetworkPlayer();
+ if( thisPlayer == NULL )
+ {
+ dontSend = true;
+ }
+ else
+ {
+ for(unsigned int j = 0; j < sentTo.size(); j++ )
+ {
+ shared_ptr<ServerPlayer> player2 = sentTo[j];
+ INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
+ if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
+ {
+ dontSend = true;
+ // #ifdef _DEBUG
+ // shared_ptr<SetEntityMotionPacket> emp= dynamic_pointer_cast<SetEntityMotionPacket> (packet);
+ // if(emp!=NULL)
+ // {
+ // app.DebugPrintf("Not sending this SetEntityMotionPacket to player - it's already been sent to a player on their console\n");
+ // }
+ // #endif
+ }
+ }
+ }
+ }
+ if( dontSend )
+ {
+ continue;
+ }
+
+
+ (*it)->connection->send(packet);
+ sentTo.push_back(player);
+ }
+ }
+ else
+ {
+ // This packet hasn't got canSendToAnyClient set, so just send to everyone here, and it
+
+ for( AUTO_VAR(it, seenBy.begin()); it != seenBy.end(); it++ )
+ {
+ (*it)->connection->send(packet);
+ }
+ }
+}
+
+void TrackedEntity::broadcastAndSend(shared_ptr<Packet> packet)
+{
+ vector< shared_ptr<ServerPlayer> > sentTo;
+ broadcast(packet);
+ shared_ptr<ServerPlayer> sp = dynamic_pointer_cast<ServerPlayer>(e);
+ if (sp != NULL && sp->connection)
+ {
+ sp->connection->send(packet);
+ }
+}
+
+void TrackedEntity::broadcastRemoved()
+{
+ for( AUTO_VAR(it, seenBy.begin()); it != seenBy.end(); it++ )
+ {
+ (*it)->entitiesToRemove.push_back(e->entityId);
+ }
+}
+
+void TrackedEntity::removePlayer(shared_ptr<ServerPlayer> sp)
+{
+ AUTO_VAR(it, seenBy.find( sp ));
+ if( it != seenBy.end() )
+ {
+ seenBy.erase( it );
+ }
+}
+
+// 4J-JEV: Added for code reuse.
+TrackedEntity::eVisibility TrackedEntity::isVisible(EntityTracker *tracker, shared_ptr<ServerPlayer> sp, bool forRider)
+{
+ // 4J Stu - We call update players when the entity has moved more than a certain amount at the start of it's tick
+ // Before this call we set xpu, ypu and zpu to the entities new position, but xp,yp and zp are the old position until later in the tick.
+ // Therefore we should use the new position for visibility checks
+ double xd = sp->x - xpu; //xp / 32;
+ double zd = sp->z - zpu; //zp / 32;
+
+ int playersRange = range;
+ if( playersRange > TRACKED_ENTITY_MINIMUM_VIEW_DISTANCE )
+ {
+ playersRange -= sp->getPlayerViewDistanceModifier();
+ }
+
+ bool bVisible = xd >= -playersRange && xd <= playersRange && zd >= -playersRange && zd <= playersRange;
+ bool canBeSeenBy = canBySeenBy(sp);
+
+ // 4J - added. Try and find other players who are in the same dimension as this one and on the same machine, and extend our visibility
+ // so things are consider visible to this player if they are near the other one. This is because we only send entity tracking info to
+ // players who canReceiveAllPackets().
+ if(!bVisible)
+ {
+ MinecraftServer *server = MinecraftServer::getInstance();
+ INetworkPlayer *thisPlayer = sp->connection->getNetworkPlayer();
+ if( thisPlayer )
+ {
+ for( unsigned int i = 0; i < server->getPlayers()->players.size(); i++ )
+ {
+ // Consider extra players, but not if they are the entity we are tracking, or the player we've been passed as input, or in another dimension
+ shared_ptr<ServerPlayer> ep = server->getPlayers()->players[i];
+ if( ep == sp ) continue;
+ if( ep == e ) continue;
+ if( ep->dimension != sp->dimension ) continue;
+
+ INetworkPlayer * otherPlayer = ep->connection->getNetworkPlayer();
+ if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
+ {
+ // 4J Stu - We call update players when the entity has moved more than a certain amount at the start of it's tick
+ // Before this call we set xpu, ypu and zpu to the entities new position, but xp,yp and zp are the old position until later in the tick.
+ // Therefore we should use the new position for visibility checks
+ double xd = ep->x - xpu; //xp / 32;
+ double zd = ep->z - zpu; //zp / 32;
+ bVisible |= ( xd >= -playersRange && xd <= playersRange && zd >= -playersRange && zd <= playersRange );
+ canBeSeenBy |= canBySeenBy(ep);
+ }
+ }
+ }
+ }
+
+ // 4J Stu - We need to ensure that we send the mount before the rider, so check that the player has been added to the seenBy list
+ if(forRider)
+ {
+ canBeSeenBy = canBeSeenBy && (seenBy.find(sp) != seenBy.end());
+ }
+
+ // 4J-JEV: ADDED! An entities mount has to be visible before the entity visible,
+ // this is to ensure that the mount is already in the client's game when the rider is added.
+ if (canBeSeenBy && bVisible && e->riding != NULL)
+ {
+ return tracker->getTracker(e->riding)->isVisible(tracker, sp, true);
+ }
+ else if (canBeSeenBy && bVisible) return eVisibility_SeenAndVisible;
+ else if (bVisible) return eVisibility_IsVisible;
+ else return eVisibility_NotVisible;
+}
+
+void TrackedEntity::updatePlayer(EntityTracker *tracker, shared_ptr<ServerPlayer> sp)
+{
+ if (sp == e) return;
+
+ eVisibility visibility = this->isVisible(tracker, sp);
+
+ if ( visibility == eVisibility_SeenAndVisible
+ && seenBy.find(sp) == seenBy.end() )
+ {
+ seenBy.insert(sp);
+ shared_ptr<Packet> packet = getAddEntityPacket();
+ sp->connection->send(packet);
+
+ xap = e->xd;
+ yap = e->yd;
+ zap = e->zd;
+
+ shared_ptr<Player> plr = dynamic_pointer_cast<Player>(e);
+ if (plr != NULL)
+ {
+ app.DebugPrintf( "TrackedEntity:: Player '%ls' is now visible to player '%ls', %s.\n",
+ plr->name.c_str(), sp->name.c_str(),
+ (e->riding==NULL?"not riding minecart":"in minecart")
+ );
+ }
+
+ // 4J Stu brought forward to fix when Item Frames
+ if (!e->getEntityData()->isEmpty() && !(dynamic_pointer_cast<AddMobPacket>(packet)))
+ {
+ sp->connection->send(shared_ptr<SetEntityDataPacket>( new SetEntityDataPacket(e->entityId, e->getEntityData(), true)));
+ }
+
+ if (this->trackDelta)
+ {
+ sp->connection->send( shared_ptr<SetEntityMotionPacket>( new SetEntityMotionPacket(e->entityId, e->xd, e->yd, e->zd) ) );
+ }
+
+ if (e->riding != NULL)
+ {
+ sp->connection->send(shared_ptr<SetRidingPacket>(new SetRidingPacket(e, e->riding)));
+ }
+
+ ItemInstanceArray equipped = e->getEquipmentSlots();
+ if (equipped.data != NULL)
+ {
+ for (unsigned int i = 0; i < equipped.length; i++)
+ {
+ sp->connection->send( shared_ptr<SetEquippedItemPacket>( new SetEquippedItemPacket(e->entityId, i, equipped[i]) ) );
+ }
+ }
+
+ if (dynamic_pointer_cast<Player>(e) != NULL)
+ {
+ shared_ptr<Player> spe = dynamic_pointer_cast<Player>(e);
+ if (spe->isSleeping())
+ {
+ sp->connection->send( shared_ptr<EntityActionAtPositionPacket>( new EntityActionAtPositionPacket(e, EntityActionAtPositionPacket::START_SLEEP, Mth::floor(e->x), Mth::floor(e->y), Mth::floor(e->z)) ) );
+ }
+ }
+
+ if (dynamic_pointer_cast<Mob>(e) != NULL)
+ {
+ shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(e);
+ vector<MobEffectInstance *> *activeEffects = mob->getActiveEffects();
+ for(AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it)
+ {
+ MobEffectInstance *effect = *it;
+
+ sp->connection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(e->entityId, effect) ) );
+ }
+ delete activeEffects;
+ }
+ }
+ else if (visibility == eVisibility_NotVisible)
+ {
+ AUTO_VAR(it, seenBy.find(sp));
+ if (it != seenBy.end())
+ {
+ seenBy.erase(it);
+ sp->entitiesToRemove.push_back(e->entityId);
+ }
+ }
+
+}
+
+bool TrackedEntity::canBySeenBy(shared_ptr<ServerPlayer> player)
+{
+ // 4J - for some reason this isn't currently working, and is causing players to not appear until we are really close to them. Not sure
+ // what the conflict is between the java & our version, but removing for now as it is causing issues and we shouldn't *really* need it
+ // TODO - investigate further
+
+ return true;
+// return player->getLevel()->getChunkMap()->isPlayerIn(player, e->xChunk, e->zChunk);
+}
+
+void TrackedEntity::updatePlayers(EntityTracker *tracker, vector<shared_ptr<Player> > *players)
+{
+ for (unsigned int i = 0; i < players->size(); i++)
+ {
+ updatePlayer(tracker, dynamic_pointer_cast<ServerPlayer>( players->at(i) ) );
+ }
+}
+
+shared_ptr<Packet> TrackedEntity::getAddEntityPacket()
+{
+ if (e->removed)
+ {
+ app.DebugPrintf("Fetching addPacket for removed entity - %ls\n", e->getAName().c_str());
+ }
+
+ // 4J-PB - replacing with a switch, rather than tons of ifs
+ if (dynamic_pointer_cast<Creature>(e) != NULL)
+ {
+ yHeadRotp = Mth::floor(e->getYHeadRot() * 256 / 360);
+ return shared_ptr<AddMobPacket>( new AddMobPacket(dynamic_pointer_cast<Mob>(e), yRotp, xRotp, xp, yp, zp, yHeadRotp) );
+ }
+
+ switch(e->GetType())
+ {
+ case eTYPE_ITEMENTITY:
+ {
+ shared_ptr<AddEntityPacket> packet = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::ITEM, 1, yRotp, xRotp, xp, yp, zp) );
+ return packet;
+ }
+ break;
+ case eTYPE_SERVERPLAYER:
+ {
+ shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(e);
+ PlayerUID xuid = INVALID_XUID;
+ PlayerUID OnlineXuid = INVALID_XUID;
+ if( player != NULL )
+ {
+ xuid = player->getXuid();
+ OnlineXuid = player->getOnlineXuid();
+ }
+ // 4J Added yHeadRotp param to fix #102563 - TU12: Content: Gameplay: When one of the Players is idle for a few minutes his head turns 180 degrees.
+ return shared_ptr<AddPlayerPacket>( new AddPlayerPacket(dynamic_pointer_cast<Player>(e), xuid, OnlineXuid, xp, yp, zp, yRotp, xRotp, yHeadRotp ) );
+ }
+ break;
+ case eTYPE_MINECART:
+ {
+ shared_ptr<Minecart> minecart = dynamic_pointer_cast<Minecart>(e);
+ if (minecart->type == Minecart::RIDEABLE) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_RIDEABLE, yRotp, xRotp, xp, yp, zp) );
+ if (minecart->type == Minecart::CHEST) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_CHEST, yRotp, xRotp, xp, yp, zp) );
+ if (minecart->type == Minecart::FURNACE) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_FURNACE, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_BOAT:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::BOAT, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_ENDERDRAGON:
+ {
+ yHeadRotp = Mth::floor(e->getYHeadRot() * 256 / 360);
+ return shared_ptr<AddMobPacket>( new AddMobPacket(dynamic_pointer_cast<Mob>(e), yRotp, xRotp, xp, yp, zp, yHeadRotp ) );
+ }
+ break;
+ case eTYPE_FISHINGHOOK:
+ {
+ shared_ptr<Entity> owner = dynamic_pointer_cast<FishingHook>(e)->owner;
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FISH_HOOK, owner != NULL ? owner->entityId : e->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_ARROW:
+ {
+ shared_ptr<Entity> owner = (dynamic_pointer_cast<Arrow>(e))->owner;
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::ARROW, owner != NULL ? owner->entityId : e->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_SNOWBALL:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SNOWBALL, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_THROWNPOTION:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_POTION, ((dynamic_pointer_cast<ThrownPotion>(e))->getPotionValue()), yRotp, xRotp, xp, yp, zp));
+ }
+ break;
+ case eTYPE_THROWNEXPBOTTLE:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_EXPBOTTLE, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_THROWNENDERPEARL:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_ENDERPEARL, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_EYEOFENDERSIGNAL:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::EYEOFENDERSIGNAL, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_SMALL_FIREBALL:
+ {
+ shared_ptr<SmallFireball> fb = dynamic_pointer_cast<SmallFireball>(e);
+ shared_ptr<AddEntityPacket> aep = nullptr;
+ if (fb->owner != NULL)
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SMALL_FIREBALL, fb->owner->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ else
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SMALL_FIREBALL, 0, yRotp, xRotp, xp, yp, zp) );
+ }
+ aep->xa = (int) (fb->xPower * 8000);
+ aep->ya = (int) (fb->yPower * 8000);
+ aep->za = (int) (fb->zPower * 8000);
+ return aep;
+ }
+ break;
+ case eTYPE_DRAGON_FIREBALL:
+ {
+ shared_ptr<DragonFireball> fb = dynamic_pointer_cast<DragonFireball>(e);
+ shared_ptr<AddEntityPacket> aep = nullptr;
+ if (fb->owner != NULL)
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::DRAGON_FIRE_BALL, fb->owner->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ else
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::DRAGON_FIRE_BALL, 0, yRotp, xRotp, xp, yp, zp) );
+ }
+ aep->xa = (int) (fb->xPower * 8000);
+ aep->ya = (int) (fb->yPower * 8000);
+ aep->za = (int) (fb->zPower * 8000);
+ return aep;
+ }
+ break;
+ case eTYPE_FIREBALL:
+ {
+ shared_ptr<Fireball> fb = dynamic_pointer_cast<Fireball>(e);
+ shared_ptr<AddEntityPacket> aep = nullptr;
+ if (fb->owner != NULL)
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FIREBALL, fb->owner->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ else
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FIREBALL, 0, yRotp, xRotp, xp, yp, zp) );
+ }
+ aep->xa = (int) (fb->xPower * 8000);
+ aep->ya = (int) (fb->yPower * 8000);
+ aep->za = (int) (fb->zPower * 8000);
+ return aep;
+ }
+ break;
+ case eTYPE_THROWNEGG:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::EGG, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_PRIMEDTNT:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::PRIMED_TNT, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_ENDER_CRYSTAL:
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::ENDER_CRYSTAL, yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_FALLINGTILE:
+ {
+ shared_ptr<FallingTile> ft = dynamic_pointer_cast<FallingTile>(e);
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FALLING, ft->tile | (ft->data << 16), yRotp, xRotp, xp, yp, zp) );
+ }
+ break;
+ case eTYPE_PAINTING:
+ {
+ return shared_ptr<AddPaintingPacket>( new AddPaintingPacket(dynamic_pointer_cast<Painting>(e)) );
+ }
+ break;
+ case eTYPE_ITEM_FRAME:
+ {
+ shared_ptr<ItemFrame> frame = dynamic_pointer_cast<ItemFrame>(e);
+ {
+
+ int ix= (int)frame->xTile;
+ int iy= (int)frame->yTile;
+ int iz= (int)frame->zTile;
+ app.DebugPrintf("eTYPE_ITEM_FRAME xyz %d,%d,%d\n",ix,iy,iz);
+ }
+
+ shared_ptr<AddEntityPacket> packet = shared_ptr<AddEntityPacket>(new AddEntityPacket(e, AddEntityPacket::ITEM_FRAME, frame->dir, yRotp, xRotp, xp, yp, zp));
+ packet->x = Mth::floor(frame->xTile * 32.0f);
+ packet->y = Mth::floor(frame->yTile * 32.0f);
+ packet->z = Mth::floor(frame->zTile * 32.0f);
+ return packet;
+ }
+ break;
+ case eTYPE_EXPERIENCEORB:
+ {
+ return shared_ptr<AddExperienceOrbPacket>( new AddExperienceOrbPacket(dynamic_pointer_cast<ExperienceOrb>(e)) );
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+/*
+ if (e->GetType() == eTYPE_ITEMENTITY)
+ {
+ shared_ptr<ItemEntity> itemEntity = dynamic_pointer_cast<ItemEntity>(e);
+ shared_ptr<AddItemEntityPacket> packet = shared_ptr<AddItemEntityPacket>( new AddItemEntityPacket(itemEntity, xp, yp, zp) );
+ itemEntity->x = packet->x / 32.0;
+ itemEntity->y = packet->y / 32.0;
+ itemEntity->z = packet->z / 32.0;
+ return packet;
+ }
+
+ if (e->GetType() == eTYPE_SERVERPLAYER )
+ {
+ shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(e);
+ XUID xuid = INVALID_XUID;
+ XUID OnlineXuid = INVALID_XUID;
+ if( player != NULL )
+ {
+ xuid = player->getXuid();
+ OnlineXuid = player->getOnlineXuid();
+ }
+ return shared_ptr<AddPlayerPacket>( new AddPlayerPacket(dynamic_pointer_cast<Player>(e), xuid, OnlineXuid, xp, yp, zp, yRotp, xRotp ) );
+ }
+ if (e->GetType() == eTYPE_MINECART)
+ {
+ shared_ptr<Minecart> minecart = dynamic_pointer_cast<Minecart>(e);
+ if (minecart->type == Minecart::RIDEABLE) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_RIDEABLE, yRotp, xRotp, xp, yp, zp) );
+ if (minecart->type == Minecart::CHEST) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_CHEST, yRotp, xRotp, xp, yp, zp) );
+ if (minecart->type == Minecart::FURNACE) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::MINECART_FURNACE, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_BOAT)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::BOAT, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (dynamic_pointer_cast<Creature>(e) != NULL)
+ {
+ return shared_ptr<AddMobPacket>( new AddMobPacket(dynamic_pointer_cast<Mob>(e), yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_ENDERDRAGON)
+ {
+ return shared_ptr<AddMobPacket>( new AddMobPacket(dynamic_pointer_cast<Mob>(e), yRotp, xRotp, xp, yp, zp ) );
+ }
+ if (e->GetType() == eTYPE_FISHINGHOOK)
+ {
+ shared_ptr<Entity> owner = dynamic_pointer_cast<FishingHook>(e)->owner;
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FISH_HOOK, owner != NULL ? owner->entityId : e->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_ARROW)
+ {
+ shared_ptr<Entity> owner = (dynamic_pointer_cast<Arrow>(e))->owner;
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::ARROW, owner != NULL ? owner->entityId : e->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_SNOWBALL)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SNOWBALL, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_THROWNPOTION)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_POTION, ((dynamic_pointer_cast<ThrownPotion>(e))->getPotionValue()), yRotp, xRotp, xp, yp, zp));
+ }
+ if (e->GetType() == eTYPE_THROWNEXPBOTTLE)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_EXPBOTTLE, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_THROWNENDERPEARL)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::THROWN_ENDERPEARL, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_EYEOFENDERSIGNAL)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::EYEOFENDERSIGNAL, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_SMALL_FIREBALL)
+ {
+ shared_ptr<SmallFireball> fb = dynamic_pointer_cast<SmallFireball>(e);
+ shared_ptr<AddEntityPacket> aep = NULL;
+ if (fb->owner != NULL)
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SMALL_FIREBALL, fb->owner->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ else
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::SMALL_FIREBALL, 0, yRotp, xRotp, xp, yp, zp) );
+ }
+ aep->xa = (int) (fb->xPower * 8000);
+ aep->ya = (int) (fb->yPower * 8000);
+ aep->za = (int) (fb->zPower * 8000);
+ return aep;
+ }
+ if (e->GetType() == eTYPE_FIREBALL)
+ {
+ shared_ptr<Fireball> fb = dynamic_pointer_cast<Fireball>(e);
+ shared_ptr<AddEntityPacket> aep = NULL;
+ if (fb->owner != NULL)
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FIREBALL, fb->owner->entityId, yRotp, xRotp, xp, yp, zp) );
+ }
+ else
+ {
+ aep = shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FIREBALL, 0, yRotp, xRotp, xp, yp, zp) );
+ }
+ aep->xa = (int) (fb->xPower * 8000);
+ aep->ya = (int) (fb->yPower * 8000);
+ aep->za = (int) (fb->zPower * 8000);
+ return aep;
+ }
+ if (e->GetType() == eTYPE_THROWNEGG)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::EGG, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_PRIMEDTNT)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::PRIMED_TNT, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_ENDER_CRYSTAL)
+ {
+ return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::ENDER_CRYSTAL, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_FALLINGTILE)
+ {
+ shared_ptr<FallingTile> ft = dynamic_pointer_cast<FallingTile>(e);
+ if (ft->tile == Tile::sand_Id) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FALLING_SAND, yRotp, xRotp, xp, yp, zp) );
+ if (ft->tile == Tile::gravel_Id) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FALLING_GRAVEL, yRotp, xRotp, xp, yp, zp) );
+ if (ft->tile == Tile::dragonEgg_Id) return shared_ptr<AddEntityPacket>( new AddEntityPacket(e, AddEntityPacket::FALLING_EGG, yRotp, xRotp, xp, yp, zp) );
+ }
+ if (e->GetType() == eTYPE_PAINTING)
+ {
+ return shared_ptr<AddPaintingPacket>( new AddPaintingPacket(dynamic_pointer_cast<Painting>(e)) );
+ }
+ if (e->GetType() == eTYPE_ITEM_FRAME)
+ {
+ shared_ptr<ItemFrame> frame = dynamic_pointer_cast<ItemFrame>(e);
+ {
+
+ int ix= (int)frame->xTile;
+ int iy= (int)frame->yTile;
+ int iz= (int)frame->zTile;
+ app.DebugPrintf("eTYPE_ITEM_FRAME xyz %d,%d,%d\n",ix,iy,iz);
+ }
+
+ shared_ptr<AddEntityPacket> packet = shared_ptr<AddEntityPacket>(new AddEntityPacket(e, AddEntityPacket::ITEM_FRAME, frame->dir, yRotp, xRotp, xp, yp, zp));
+ packet->x = Mth::floor(frame->xTile * 32.0f);
+ packet->y = Mth::floor(frame->yTile * 32.0f);
+ packet->z = Mth::floor(frame->zTile * 32.0f);
+ return packet;
+ }
+ if (e->GetType() == eTYPE_EXPERIENCEORB)
+ {
+ return shared_ptr<AddExperienceOrbPacket>( new AddExperienceOrbPacket(dynamic_pointer_cast<ExperienceOrb>(e)) );
+ }
+ assert(false);
+ */
+ return nullptr;
+}
+
+void TrackedEntity::clear(shared_ptr<ServerPlayer> sp)
+{
+ AUTO_VAR(it, seenBy.find(sp));
+ if (it != seenBy.end())
+ {
+ seenBy.erase(it);
+ sp->entitiesToRemove.push_back(e->entityId);
+ }
+}