2026-03-01 12:16:08 +08:00
# include "stdafx.h"
//#include "..\Minecraft.World\JavaMath.h"
# include "MultiplayerLocalPlayer.h"
# include "ClientConnection.h"
# include "..\Minecraft.World\net.minecraft.world.level.h"
# include "..\Minecraft.World\net.minecraft.network.h"
# include "..\Minecraft.World\Mth.h"
# include "..\Minecraft.World\AABB.h"
# include "..\Minecraft.World\net.minecraft.stats.h"
# include "..\Minecraft.World\net.minecraft.world.inventory.h"
# include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
# include "..\Minecraft.World\net.minecraft.world.effect.h"
# include "..\Minecraft.World\LevelData.h"
# include "..\Minecraft.World\net.minecraft.world.entity.item.h"
2026-03-03 03:04:10 +08:00
# include "Input.h"
# include "LevelRenderer.h"
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
// 4J added for testing
# ifdef STRESS_TEST_MOVE
volatile bool stressTestEnabled = true ;
# endif
2026-03-01 12:16:08 +08:00
MultiplayerLocalPlayer : : MultiplayerLocalPlayer ( Minecraft * minecraft , Level * level , User * user , ClientConnection * connection ) : LocalPlayer ( minecraft , level , user , level - > dimension - > id )
{
// 4J - added initialisers
flashOnSetHealth = false ;
xLast = yLast1 = yLast2 = zLast = 0 ;
yRotLast = xRotLast = 0 ;
lastOnGround = false ;
lastSneaked = false ;
lastIdle = false ;
lastSprinting = false ;
positionReminder = 0 ;
this - > connection = connection ;
}
2026-03-03 03:04:10 +08:00
bool MultiplayerLocalPlayer : : hurt ( DamageSource * source , float dmg )
2026-03-01 12:16:08 +08:00
{
return false ;
}
2026-03-03 03:04:10 +08:00
void MultiplayerLocalPlayer : : heal ( float heal )
2026-03-01 12:16:08 +08:00
{
}
void MultiplayerLocalPlayer : : tick ( )
{
// 4J Added
// 4J-PB - changing this to a game host option ot hide gamertags
//bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad);
/*if((app.GetGameSettings(m_iPad,eGameSetting_PlayerVisibleInMap)!=0) != m_bShownOnMaps)
{
m_bShownOnMaps = ( app . GetGameSettings ( m_iPad , eGameSetting_PlayerVisibleInMap ) ! = 0 ) ;
2026-03-02 17:37:16 +07:00
if ( m_bShownOnMaps ) connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : SHOW_ON_MAPS ) ) ) ;
else connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : HIDE_ON_MAPS ) ) ) ;
2026-03-01 12:16:08 +08:00
} */
if ( ! level - > hasChunkAt ( Mth : : floor ( x ) , 0 , Mth : : floor ( z ) ) ) return ;
double tempX = x , tempY = y , tempZ = z ;
2026-03-03 03:04:10 +08:00
2026-03-01 12:16:08 +08:00
LocalPlayer : : tick ( ) ;
2026-03-03 03:04:10 +08:00
// 4J added for testing
# ifdef STRESS_TEST_MOVE
if ( stressTestEnabled )
{
StressTestMove ( & tempX , & tempY , & tempZ ) ;
}
# endif
2026-03-01 12:16:08 +08:00
//if( !minecraft->localgameModes[m_iPad]->isTutorial() || minecraft->localgameModes[m_iPad]->getTutorial()->canMoveToPosition(tempX, tempY, tempZ, x, y, z) )
if ( minecraft - > localgameModes [ m_iPad ] - > getTutorial ( ) - > canMoveToPosition ( tempX , tempY , tempZ , x , y , z ) )
2026-03-02 17:37:16 +07:00
{
2026-03-03 03:04:10 +08:00
if ( isRiding ( ) )
{
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket : : Rot ( yRot , xRot , onGround , abilities . flying ) ) ) ;
connection - > send ( shared_ptr < PlayerInputPacket > ( new PlayerInputPacket ( xxa , yya , input - > jumping , input - > sneaking ) ) ) ;
}
else
{
sendPosition ( ) ;
}
2026-03-01 12:16:08 +08:00
}
else
{
//app.Debugprintf("Cannot move to position (%f, %f, %f), falling back to (%f, %f, %f)\n", x, y, z, tempX, y, tempZ);
this - > setPos ( tempX , y , tempZ ) ;
}
}
void MultiplayerLocalPlayer : : sendPosition ( )
{
bool sprinting = isSprinting ( ) ;
if ( sprinting ! = lastSprinting )
{
2026-03-02 17:37:16 +07:00
if ( sprinting ) connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : START_SPRINTING ) ) ) ;
else connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : STOP_SPRINTING ) ) ) ;
2026-03-01 12:16:08 +08:00
lastSprinting = sprinting ;
}
bool sneaking = isSneaking ( ) ;
if ( sneaking ! = lastSneaked )
{
2026-03-02 17:37:16 +07:00
if ( sneaking ) connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : START_SNEAKING ) ) ) ;
else connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : STOP_SNEAKING ) ) ) ;
2026-03-01 12:16:08 +08:00
lastSneaked = sneaking ;
}
bool idle = isIdle ( ) ;
if ( idle ! = lastIdle )
{
2026-03-02 17:37:16 +07:00
if ( idle ) connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : START_IDLEANIM ) ) ) ;
else connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : STOP_IDLEANIM ) ) ) ;
2026-03-01 12:16:08 +08:00
lastIdle = idle ;
}
double xdd = x - xLast ;
double ydd1 = bb - > y0 - yLast1 ;
double zdd = z - zLast ;
double rydd = yRot - yRotLast ;
double rxdd = xRot - xRotLast ;
bool move = ( xdd * xdd + ydd1 * ydd1 + zdd * zdd ) > 0.03 * 0.03 | | positionReminder > = POSITION_REMINDER_INTERVAL ;
bool rot = rydd ! = 0 | | rxdd ! = 0 ;
if ( riding ! = NULL )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket : : PosRot ( xd , - 999 , - 999 , zd , yRot , xRot , onGround , abilities . flying ) ) ) ;
2026-03-01 12:16:08 +08:00
move = false ;
}
else
{
if ( move & & rot )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket : : PosRot ( x , bb - > y0 , y , z , yRot , xRot , onGround , abilities . flying ) ) ) ;
2026-03-01 12:16:08 +08:00
}
else if ( move )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket : : Pos ( x , bb - > y0 , y , z , onGround , abilities . flying ) ) ) ;
2026-03-01 12:16:08 +08:00
}
else if ( rot )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket : : Rot ( yRot , xRot , onGround , abilities . flying ) ) ) ;
2026-03-01 12:16:08 +08:00
}
else
{
2026-03-03 03:04:10 +08:00
connection - > send ( shared_ptr < MovePlayerPacket > ( new MovePlayerPacket ( onGround , abilities . flying ) ) ) ;
2026-03-01 12:16:08 +08:00
}
}
positionReminder + + ;
lastOnGround = onGround ;
if ( move )
{
xLast = x ;
yLast1 = bb - > y0 ;
yLast2 = y ;
zLast = z ;
positionReminder = 0 ;
}
if ( rot )
{
yRotLast = yRot ;
xRotLast = xRot ;
}
}
2026-03-02 17:37:16 +07:00
shared_ptr < ItemEntity > MultiplayerLocalPlayer : : drop ( )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < PlayerActionPacket > ( new PlayerActionPacket ( PlayerActionPacket : : DROP_ITEM , 0 , 0 , 0 , 0 ) ) ) ;
2026-03-01 12:16:08 +08:00
return nullptr ;
}
2026-03-02 17:37:16 +07:00
void MultiplayerLocalPlayer : : reallyDrop ( shared_ptr < ItemEntity > itemEntity )
2026-03-01 12:16:08 +08:00
{
}
void MultiplayerLocalPlayer : : chat ( const wstring & message )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < ChatPacket > ( new ChatPacket ( message ) ) ) ;
2026-03-01 12:16:08 +08:00
}
void MultiplayerLocalPlayer : : swing ( )
{
LocalPlayer : : swing ( ) ;
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < AnimatePacket > ( new AnimatePacket ( shared_from_this ( ) , AnimatePacket : : SWING ) ) ) ;
2026-03-01 12:16:08 +08:00
}
void MultiplayerLocalPlayer : : respawn ( )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < ClientCommandPacket > ( new ClientCommandPacket ( ClientCommandPacket : : PERFORM_RESPAWN ) ) ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void MultiplayerLocalPlayer : : actuallyHurt ( DamageSource * source , float dmg )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( isInvulnerable ( ) ) return ;
2026-03-01 12:16:08 +08:00
setHealth ( getHealth ( ) - dmg ) ;
}
// 4J Added override to capture event for tutorial messages
void MultiplayerLocalPlayer : : completeUsingItem ( )
{
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
if ( useItem ! = NULL & & pMinecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) pMinecraft - > localgameModes [ m_iPad ] ;
Tutorial * tutorial = gameMode - > getTutorial ( ) ;
tutorial - > completeUsingItem ( useItem ) ;
}
Player : : completeUsingItem ( ) ;
}
void MultiplayerLocalPlayer : : onEffectAdded ( MobEffectInstance * effect )
{
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
if ( pMinecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) pMinecraft - > localgameModes [ m_iPad ] ;
Tutorial * tutorial = gameMode - > getTutorial ( ) ;
tutorial - > onEffectChanged ( MobEffect : : effects [ effect - > getId ( ) ] ) ;
}
Player : : onEffectAdded ( effect ) ;
}
2026-03-03 03:04:10 +08:00
void MultiplayerLocalPlayer : : onEffectUpdated ( MobEffectInstance * effect , bool doRefreshAttributes )
2026-03-01 12:16:08 +08:00
{
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
if ( pMinecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) pMinecraft - > localgameModes [ m_iPad ] ;
Tutorial * tutorial = gameMode - > getTutorial ( ) ;
tutorial - > onEffectChanged ( MobEffect : : effects [ effect - > getId ( ) ] ) ;
}
2026-03-03 03:04:10 +08:00
Player : : onEffectUpdated ( effect , doRefreshAttributes ) ;
2026-03-01 12:16:08 +08:00
}
void MultiplayerLocalPlayer : : onEffectRemoved ( MobEffectInstance * effect )
{
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
if ( pMinecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) pMinecraft - > localgameModes [ m_iPad ] ;
Tutorial * tutorial = gameMode - > getTutorial ( ) ;
tutorial - > onEffectChanged ( MobEffect : : effects [ effect - > getId ( ) ] , true ) ;
}
Player : : onEffectRemoved ( effect ) ;
}
void MultiplayerLocalPlayer : : closeContainer ( )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < ContainerClosePacket > ( new ContainerClosePacket ( containerMenu - > containerId ) ) ) ;
2026-03-03 03:04:10 +08:00
clientSideCloseContainer ( ) ;
}
// close the container without sending a packet to the server
void MultiplayerLocalPlayer : : clientSideCloseContainer ( )
{
2026-03-01 12:16:08 +08:00
inventory - > setCarried ( nullptr ) ;
LocalPlayer : : closeContainer ( ) ;
}
2026-03-03 03:04:10 +08:00
void MultiplayerLocalPlayer : : hurtTo ( float newHealth , ETelemetryChallenges damageSource )
2026-03-01 12:16:08 +08:00
{
if ( flashOnSetHealth )
{
LocalPlayer : : hurtTo ( newHealth , damageSource ) ;
}
else
{
setHealth ( newHealth ) ;
flashOnSetHealth = true ;
}
}
void MultiplayerLocalPlayer : : awardStat ( Stat * stat , byteArray param )
{
if ( stat = = NULL )
{
delete [ ] param . data ;
return ;
}
if ( stat - > awardLocallyOnly )
{
LocalPlayer : : awardStat ( stat , param ) ;
}
else
{
delete [ ] param . data ;
return ;
}
}
void MultiplayerLocalPlayer : : awardStatFromServer ( Stat * stat , byteArray param )
{
if ( stat ! = NULL & & ! stat - > awardLocallyOnly )
{
LocalPlayer : : awardStat ( stat , param ) ;
}
else delete [ ] param . data ;
}
void MultiplayerLocalPlayer : : onUpdateAbilities ( )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < PlayerAbilitiesPacket > ( new PlayerAbilitiesPacket ( & abilities ) ) ) ;
2026-03-01 12:16:08 +08:00
}
bool MultiplayerLocalPlayer : : isLocalPlayer ( )
{
return true ;
}
2026-03-03 03:04:10 +08:00
void MultiplayerLocalPlayer : : sendRidingJump ( )
{
connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : RIDING_JUMP , ( int ) ( getJumpRidingScale ( ) * 100.0f ) ) ) ) ;
}
void MultiplayerLocalPlayer : : sendOpenInventory ( )
{
connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : OPEN_INVENTORY ) ) ) ;
}
2026-03-02 17:37:16 +07:00
void MultiplayerLocalPlayer : : ride ( shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
bool wasRiding = riding ! = NULL ;
LocalPlayer : : ride ( e ) ;
bool isRiding = riding ! = NULL ;
2026-03-03 03:04:10 +08:00
// 4J Added
if ( wasRiding & & ! isRiding )
{
setSneaking ( false ) ;
input - > sneaking = false ;
}
2026-03-01 12:16:08 +08:00
if ( isRiding )
{
ETelemetryChallenges eventType = eTelemetryChallenges_Unknown ;
if ( this - > riding ! = NULL )
{
switch ( riding - > GetType ( ) )
{
case eTYPE_BOAT :
eventType = eTelemetryInGame_Ride_Boat ;
break ;
case eTYPE_MINECART :
eventType = eTelemetryInGame_Ride_Minecart ;
break ;
case eTYPE_PIG :
eventType = eTelemetryInGame_Ride_Pig ;
break ;
} ;
}
TelemetryManager - > RecordEnemyKilledOrOvercome ( GetXboxPad ( ) , 0 , y , 0 , 0 , 0 , 0 , eventType ) ;
}
updateRichPresence ( ) ;
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
if ( pMinecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) pMinecraft - > localgameModes [ m_iPad ] ;
if ( wasRiding & & ! isRiding )
{
gameMode - > getTutorial ( ) - > changeTutorialState ( e_Tutorial_State_Gameplay ) ;
}
else if ( ! wasRiding & & isRiding )
{
2026-03-03 03:04:10 +08:00
gameMode - > getTutorial ( ) - > onRideEntity ( e ) ;
2026-03-01 12:16:08 +08:00
}
}
}
void MultiplayerLocalPlayer : : StopSleeping ( )
{
2026-03-02 17:37:16 +07:00
connection - > send ( shared_ptr < PlayerCommandPacket > ( new PlayerCommandPacket ( shared_from_this ( ) , PlayerCommandPacket : : STOP_SLEEPING ) ) ) ;
2026-03-01 12:16:08 +08:00
}
// 4J Added
void MultiplayerLocalPlayer : : setAndBroadcastCustomSkin ( DWORD skinId )
{
DWORD oldSkinIndex = getCustomSkin ( ) ;
LocalPlayer : : setCustomSkin ( skinId ) ;
# ifndef _CONTENT_PACKAGE
wprintf ( L " Skin for local player %ls has changed to %ls (%d) \n " , name . c_str ( ) , customTextureUrl . c_str ( ) , getPlayerDefaultSkin ( ) ) ;
# endif
2026-03-02 17:37:16 +07:00
if ( getCustomSkin ( ) ! = oldSkinIndex ) connection - > send ( shared_ptr < TextureAndGeometryChangePacket > ( new TextureAndGeometryChangePacket ( shared_from_this ( ) , app . GetPlayerSkinName ( GetXboxPad ( ) ) ) ) ) ;
2026-03-01 12:16:08 +08:00
}
void MultiplayerLocalPlayer : : setAndBroadcastCustomCape ( DWORD capeId )
{
DWORD oldCapeIndex = getCustomCape ( ) ;
LocalPlayer : : setCustomCape ( capeId ) ;
# ifndef _CONTENT_PACKAGE
wprintf ( L " Cape for local player %ls has changed to %ls \n " , name . c_str ( ) , customTextureUrl2 . c_str ( ) ) ;
# endif
2026-03-02 17:37:16 +07:00
if ( getCustomCape ( ) ! = oldCapeIndex ) connection - > send ( shared_ptr < TextureChangePacket > ( new TextureChangePacket ( shared_from_this ( ) , TextureChangePacket : : e_TextureChange_Cape , app . GetPlayerCapeName ( GetXboxPad ( ) ) ) ) ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
// 4J added for testing. This moves the player in a repeated sequence of 2 modes:
// Mode 0 - teleports to random location in the world, and waits for the number of chunks that are fully loaded/created to have setting for 2 seconds before changing to mode 1
// Mode 1 - picks a random direction to move in for 200 ticks (~10 seconds), repeating for a total of 2000 ticks, before cycling back to mode 0
// Whilst carrying out this movement pattern, this calls checkAllPresentChunks which checks the integrity of all currently loaded/created chunks round the player.
# ifdef STRESS_TEST_MOVE
void MultiplayerLocalPlayer : : StressTestMove ( double * tempX , double * tempY , double * tempZ )
{
static volatile int64_t lastChangeTime = 0 ;
static volatile int64_t lastTeleportTime = 0 ;
static int lastCount = 0 ;
static int stressTestCount = 0 ;
const int dirChangeTickCount = 200 ;
int64_t currentTime = System : : currentTimeMillis ( ) ;
bool faultFound = false ;
int count = Minecraft : : GetInstance ( ) - > levelRenderer - > checkAllPresentChunks ( & faultFound ) ;
/*
if ( faultFound )
{
app . DebugPrintf ( " Fault found \n " ) ;
stressTestEnabled = false ;
}
*/
if ( count ! = lastCount )
{
lastChangeTime = currentTime ;
lastCount = count ;
}
static float angle = 30.0 ;
static float dx = cos ( 30.0 ) ;
static float dz = sin ( 30.0 ) ;
#if 0
if ( ( stressTestCount % dirChangeTickCount ) = = 0 )
{
int angledeg = rand ( ) % 360 ;
angle = ( ( ( double ) angledeg ) / 360.0 ) * ( 2.0 * 3.141592654 ) ;
dx = cos ( angle ) ;
dz = sin ( angle ) ;
}
# endif
float nx = x + ( dx * 1.2 ) ;
float nz = z + ( dz * 1.2 ) ;
float ny = y ;
if ( ny < 140.0f ) ny + = 0.5f ;
if ( nx > 2539.0 )
{
nx = 2539.0 ;
dx = - dx ;
}
if ( nz > 2539.0 )
{
nz = 2539.0 ;
dz = - dz ;
}
if ( nx < - 2550.0 )
{
nx = - 2550.0 ;
dx = - dx ;
}
if ( nz < - 2550.0 )
{
nz = - 2550.0 ;
dz = - dz ;
}
absMoveTo ( nx , ny , nz , yRot , xRot ) ;
stressTestCount + + ;
}
# endif