2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "EntityTracker.h"
# include "MinecraftServer.h"
# include "PlayerList.h"
# include "TrackedEntity.h"
# include "ServerPlayer.h"
# include "ServerLevel.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.world.entity.boss.enderdragon.h"
# include "..\Minecraft.World\net.minecraft.network.packet.h"
# include "..\Minecraft.World\net.minecraft.network.h"
# include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
# include "..\Minecraft.World\BasicTypeContainers.h"
# include "PlayerConnection.h"
EntityTracker : : EntityTracker ( ServerLevel * level )
{
this - > level = level ;
maxRange = level - > getServer ( ) - > getPlayers ( ) - > getMaxRange ( ) ;
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : addEntity ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
if ( e - > GetType ( ) = = eTYPE_SERVERPLAYER )
{
addEntity ( e , 32 * 16 , 2 ) ;
2026-03-02 17:10:34 +07:00
std : : shared_ptr < ServerPlayer > player = std : : dynamic_pointer_cast < ServerPlayer > ( e ) ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; it + + )
{
if ( ( * it ) - > e ! = player )
{
( * it ) - > updatePlayer ( this , player ) ;
}
}
}
else if ( e - > GetType ( ) = = eTYPE_FISHINGHOOK ) addEntity ( e , 16 * 4 , 5 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_SMALL_FIREBALL ) addEntity ( e , 16 * 4 , 10 , false ) ;
else if ( e - > GetType ( ) = = eTYPE_DRAGON_FIREBALL ) addEntity ( e , 16 * 4 , 10 , false ) ; // 4J Added TU9
else if ( e - > GetType ( ) = = eTYPE_ARROW ) addEntity ( e , 16 * 4 , 20 , false ) ;
else if ( e - > GetType ( ) = = eTYPE_FIREBALL ) addEntity ( e , 16 * 4 , 10 , false ) ;
else if ( e - > GetType ( ) = = eTYPE_SNOWBALL ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_THROWNENDERPEARL ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_EYEOFENDERSIGNAL ) addEntity ( e , 16 * 4 , 4 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_THROWNEGG ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_THROWNPOTION ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_THROWNEXPBOTTLE ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_ITEMENTITY ) addEntity ( e , 16 * 4 , 20 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_MINECART ) addEntity ( e , 16 * 5 , 3 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_BOAT ) addEntity ( e , 16 * 5 , 3 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_SQUID ) addEntity ( e , 16 * 4 , 3 , true ) ;
2026-03-02 17:10:34 +07:00
else if ( std : : dynamic_pointer_cast < Creature > ( e ) ! = NULL ) addEntity ( e , 16 * 5 , 3 , true ) ;
2026-03-01 12:16:08 +08:00
else if ( e - > GetType ( ) = = eTYPE_ENDERDRAGON ) addEntity ( e , 16 * 10 , 3 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_PRIMEDTNT ) addEntity ( e , 16 * 10 , 10 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_FALLINGTILE ) addEntity ( e , 16 * 10 , 20 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_PAINTING ) addEntity ( e , 16 * 10 , INT_MAX , false ) ;
else if ( e - > GetType ( ) = = eTYPE_EXPERIENCEORB ) addEntity ( e , 16 * 10 , 20 , true ) ;
else if ( e - > GetType ( ) = = eTYPE_ENDER_CRYSTAL ) addEntity ( e , 16 * 16 , INT_MAX , false ) ;
else if ( e - > GetType ( ) = = eTYPE_ITEM_FRAME ) addEntity ( e , 16 * 10 , INT_MAX , false ) ;
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : addEntity ( std : : shared_ptr < Entity > e , int range , int updateInterval )
2026-03-01 12:16:08 +08:00
{
addEntity ( e , range , updateInterval , false ) ;
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : addEntity ( std : : shared_ptr < Entity > e , int range , int updateInterval , bool trackDeltas )
2026-03-01 12:16:08 +08:00
{
if ( range > maxRange ) range = maxRange ;
if ( entityMap . find ( e - > entityId ) ! = entityMap . end ( ) )
{
assert ( false ) ; // Entity already tracked
}
if ( e - > entityId > = 2048 )
{
__debugbreak ( ) ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = std : : shared_ptr < TrackedEntity > ( new TrackedEntity ( e , range , updateInterval , trackDeltas ) ) ;
2026-03-01 12:16:08 +08:00
entities . insert ( te ) ;
entityMap [ e - > entityId ] = te ;
te - > updatePlayers ( this , & level - > players ) ;
}
// 4J - have split removeEntity into two bits - it used to do the equivalent of EntityTracker::removePlayer followed by EntityTracker::removeEntity.
// This is to allow us to now choose to remove the player as a "seenBy" only when the player has actually been removed from the level's own player array
2026-03-02 15:58:20 +07:00
void EntityTracker : : removeEntity ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( it , entityMap . find ( e - > entityId ) ) ;
if ( it ! = entityMap . end ( ) )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = it - > second ;
2026-03-01 12:16:08 +08:00
entityMap . erase ( it ) ;
entities . erase ( te ) ;
te - > broadcastRemoved ( ) ;
}
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : removePlayer ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
if ( e - > GetType ( ) = = eTYPE_SERVERPLAYER )
{
2026-03-02 17:10:34 +07:00
std : : shared_ptr < ServerPlayer > player = std : : dynamic_pointer_cast < ServerPlayer > ( e ) ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; it + + )
{
( * it ) - > removePlayer ( player ) ;
}
}
}
void EntityTracker : : tick ( )
{
2026-03-02 17:10:34 +07:00
std : : vector < std : : shared_ptr < ServerPlayer > > movedPlayers ;
2026-03-01 12:16:08 +08:00
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; it + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = * it ;
2026-03-01 12:16:08 +08:00
te - > tick ( this , & level - > players ) ;
if ( te - > moved & & te - > e - > GetType ( ) = = eTYPE_SERVERPLAYER )
{
2026-03-02 17:10:34 +07:00
movedPlayers . push_back ( std : : dynamic_pointer_cast < ServerPlayer > ( te - > e ) ) ;
2026-03-01 12:16:08 +08:00
}
}
// 4J Stu - If one player on a system is updated, then make sure they all are as they all have their
// range extended to include entities visible by any other player on the system
// Fix for #11194 - Gameplay: Host player and their split-screen avatars can become invisible and invulnerable to client.
MinecraftServer * server = MinecraftServer : : getInstance ( ) ;
for ( unsigned int i = 0 ; i < server - > getPlayers ( ) - > players . size ( ) ; i + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > ep = server - > getPlayers ( ) - > players [ i ] ;
2026-03-01 12:16:08 +08:00
if ( ep - > dimension ! = level - > dimension - > id ) continue ;
if ( ep - > connection = = NULL ) continue ;
INetworkPlayer * thisPlayer = ep - > connection - > getNetworkPlayer ( ) ;
if ( thisPlayer = = NULL ) continue ;
bool addPlayer = false ;
for ( unsigned int j = 0 ; j < movedPlayers . size ( ) ; j + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > sp = movedPlayers [ j ] ;
2026-03-01 12:16:08 +08:00
if ( sp = = ep ) break ;
if ( sp - > connection = = NULL ) continue ;
INetworkPlayer * otherPlayer = sp - > connection - > getNetworkPlayer ( ) ;
if ( otherPlayer ! = NULL & & thisPlayer - > IsSameSystem ( otherPlayer ) )
{
addPlayer = true ;
break ;
}
2026-03-02 15:58:20 +07:00
}
2026-03-01 12:16:08 +08:00
if ( addPlayer ) movedPlayers . push_back ( ep ) ;
}
for ( unsigned int i = 0 ; i < movedPlayers . size ( ) ; i + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > player = movedPlayers [ i ] ;
2026-03-01 12:16:08 +08:00
if ( player - > connection = = NULL ) continue ;
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; it + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = * it ;
2026-03-01 12:16:08 +08:00
if ( te - > e ! = player )
{
te - > updatePlayer ( this , player ) ;
}
}
}
// 4J Stu - We want to do this for dead players as they don't tick normally
for ( AUTO_VAR ( it , level - > players . begin ( ) ) ; it ! = level - > players . end ( ) ; + + it )
{
2026-03-02 17:10:34 +07:00
std : : shared_ptr < ServerPlayer > player = std : : dynamic_pointer_cast < ServerPlayer > ( * it ) ;
2026-03-01 12:16:08 +08:00
if ( ! player - > isAlive ( ) )
{
player - > flushEntitiesToRemove ( ) ;
}
}
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : broadcast ( std : : shared_ptr < Entity > e , std : : shared_ptr < Packet > packet )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( it , entityMap . find ( e - > entityId ) ) ;
if ( it ! = entityMap . end ( ) )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = it - > second ;
2026-03-01 12:16:08 +08:00
te - > broadcast ( packet ) ;
}
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : broadcastAndSend ( std : : shared_ptr < Entity > e , std : : shared_ptr < Packet > packet )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( it , entityMap . find ( e - > entityId ) ) ;
if ( it ! = entityMap . end ( ) )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = it - > second ;
2026-03-01 12:16:08 +08:00
te - > broadcastAndSend ( packet ) ;
}
}
2026-03-02 15:58:20 +07:00
void EntityTracker : : clear ( std : : shared_ptr < ServerPlayer > serverPlayer )
2026-03-01 12:16:08 +08:00
{
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; it + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > te = * it ;
2026-03-01 12:16:08 +08:00
te - > clear ( serverPlayer ) ;
}
}
// AP added for Vita so the range can be increased once the level starts
void EntityTracker : : updateMaxRange ( )
{
maxRange = level - > getServer ( ) - > getPlayers ( ) - > getMaxRange ( ) ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < TrackedEntity > EntityTracker : : getTracker ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( it , entityMap . find ( e - > entityId ) ) ;
if ( it ! = entityMap . end ( ) )
{
return it - > second ;
}
return nullptr ;
}