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"
2026-03-03 03:04:10 +08:00
# include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
2026-03-01 12:16:08 +08:00
# include "PlayerConnection.h"
EntityTracker : : EntityTracker ( ServerLevel * level )
{
this - > level = level ;
maxRange = level - > getServer ( ) - > getPlayers ( ) - > getMaxRange ( ) ;
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : addEntity ( shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( e - > instanceof ( eTYPE_SERVERPLAYER ) )
2026-03-01 12:16:08 +08:00
{
addEntity ( e , 32 * 16 , 2 ) ;
2026-03-02 17:37:16 +07:00
shared_ptr < ServerPlayer > player = 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 ) ;
}
}
}
2026-03-03 03:04:10 +08:00
else if ( e - > instanceof ( eTYPE_FISHINGHOOK ) ) addEntity ( e , 16 * 4 , 5 , true ) ;
else if ( e - > instanceof ( eTYPE_SMALL_FIREBALL ) ) addEntity ( e , 16 * 4 , 10 , false ) ;
else if ( e - > instanceof ( eTYPE_DRAGON_FIREBALL ) ) addEntity ( e , 16 * 4 , 10 , false ) ; // 4J Added TU9
else if ( e - > instanceof ( eTYPE_ARROW ) ) addEntity ( e , 16 * 4 , 20 , false ) ;
else if ( e - > instanceof ( eTYPE_FIREBALL ) ) addEntity ( e , 16 * 4 , 10 , false ) ;
else if ( e - > instanceof ( eTYPE_SNOWBALL ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_THROWNENDERPEARL ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_EYEOFENDERSIGNAL ) ) addEntity ( e , 16 * 4 , 4 , true ) ;
else if ( e - > instanceof ( eTYPE_THROWNEGG ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_THROWNPOTION ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_THROWNEXPBOTTLE ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_FIREWORKS_ROCKET ) ) addEntity ( e , 16 * 4 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_ITEMENTITY ) ) addEntity ( e , 16 * 4 , 20 , true ) ;
else if ( e - > instanceof ( eTYPE_MINECART ) ) addEntity ( e , 16 * 5 , 3 , true ) ;
else if ( e - > instanceof ( eTYPE_BOAT ) ) addEntity ( e , 16 * 5 , 3 , true ) ;
else if ( e - > instanceof ( eTYPE_SQUID ) ) addEntity ( e , 16 * 4 , 3 , true ) ;
else if ( e - > instanceof ( eTYPE_WITHERBOSS ) ) addEntity ( e , 16 * 5 , 3 , false ) ;
else if ( e - > instanceof ( eTYPE_BAT ) ) addEntity ( e , 16 * 5 , 3 , false ) ;
2026-03-02 17:36:56 +07:00
else if ( dynamic_pointer_cast < Creature > ( e ) ! = NULL ) addEntity ( e , 16 * 5 , 3 , true ) ;
2026-03-03 03:04:10 +08:00
else if ( e - > instanceof ( eTYPE_ENDERDRAGON ) ) addEntity ( e , 16 * 10 , 3 , true ) ;
else if ( e - > instanceof ( eTYPE_PRIMEDTNT ) ) addEntity ( e , 16 * 10 , 10 , true ) ;
else if ( e - > instanceof ( eTYPE_FALLINGTILE ) ) addEntity ( e , 16 * 10 , 20 , true ) ;
else if ( e - > instanceof ( eTYPE_HANGING_ENTITY ) ) addEntity ( e , 16 * 10 , INT_MAX , false ) ;
else if ( e - > instanceof ( eTYPE_EXPERIENCEORB ) ) addEntity ( e , 16 * 10 , 20 , true ) ;
else if ( e - > instanceof ( eTYPE_ENDER_CRYSTAL ) ) addEntity ( e , 16 * 16 , INT_MAX , false ) ;
else if ( e - > instanceof ( eTYPE_ITEM_FRAME ) ) addEntity ( e , 16 * 10 , INT_MAX , false ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : addEntity ( shared_ptr < Entity > e , int range , int updateInterval )
2026-03-01 12:16:08 +08:00
{
addEntity ( e , range , updateInterval , false ) ;
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : addEntity ( 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 17:37:16 +07:00
shared_ptr < TrackedEntity > te = 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 17:37:16 +07:00
void EntityTracker : : removeEntity ( 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 17:37:16 +07:00
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 17:37:16 +07:00
void EntityTracker : : removePlayer ( shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
if ( e - > GetType ( ) = = eTYPE_SERVERPLAYER )
{
2026-03-02 17:37:16 +07:00
shared_ptr < ServerPlayer > player = 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 ) ;
}
2026-03-03 03:04:10 +08:00
// 4J: Flush now to ensure remove packets are sent before player respawns and add entity packets are sent
player - > flushEntitiesToRemove ( ) ;
2026-03-01 12:16:08 +08:00
}
}
void EntityTracker : : tick ( )
{
2026-03-02 17:37:16 +07:00
vector < 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 17:37:16 +07:00
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:36:56 +07:00
movedPlayers . push_back ( 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 17:37:16 +07:00
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 17:37:16 +07:00
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 17:37:16 +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 17:37:16 +07:00
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 17:37:16 +07:00
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:37:16 +07:00
shared_ptr < ServerPlayer > player = dynamic_pointer_cast < ServerPlayer > ( * it ) ;
2026-03-01 12:16:08 +08:00
if ( ! player - > isAlive ( ) )
{
player - > flushEntitiesToRemove ( ) ;
}
}
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : broadcast ( shared_ptr < Entity > e , 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 17:37:16 +07:00
shared_ptr < TrackedEntity > te = it - > second ;
2026-03-01 12:16:08 +08:00
te - > broadcast ( packet ) ;
}
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : broadcastAndSend ( shared_ptr < Entity > e , 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 17:37:16 +07:00
shared_ptr < TrackedEntity > te = it - > second ;
2026-03-01 12:16:08 +08:00
te - > broadcastAndSend ( packet ) ;
}
}
2026-03-02 17:37:16 +07:00
void EntityTracker : : clear ( 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 17:37:16 +07:00
shared_ptr < TrackedEntity > te = * it ;
2026-03-01 12:16:08 +08:00
te - > clear ( serverPlayer ) ;
}
}
2026-03-03 03:04:10 +08:00
void EntityTracker : : playerLoadedChunk ( shared_ptr < ServerPlayer > player , LevelChunk * chunk )
{
for ( AUTO_VAR ( it , entities . begin ( ) ) ; it ! = entities . end ( ) ; + + it )
{
shared_ptr < TrackedEntity > te = * it ;
if ( te - > e ! = player & & te - > e - > xChunk = = chunk - > x & & te - > e - > zChunk = = chunk - > z )
{
te - > updatePlayer ( this , player ) ;
}
}
}
2026-03-01 12:16:08 +08:00
// 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 17:37:16 +07:00
shared_ptr < TrackedEntity > EntityTracker : : getTracker ( 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 ;
}