#include "minecraft/IGameServices.h" #include "minecraft/util/Log.h" #include "MultiPlayerLocalPlayer.h" #include #include #include "ClientConnection.h" #include "app/common/Tutorial/Tutorial.h" #include "app/common/Tutorial/TutorialEnum.h" #include "app/common/Tutorial/TutorialMode.h" #include "app/linux/LinuxGame.h" #include "minecraft/client/Minecraft.h" #include "minecraft/client/multiplayer/MultiPlayerGameMode.h" #include "minecraft/client/player/Input.h" #include "minecraft/client/player/LocalPlayer.h" #include "minecraft/network/packet/AnimatePacket.h" #include "minecraft/network/packet/ChatPacket.h" #include "minecraft/network/packet/ClientCommandPacket.h" #include "minecraft/network/packet/ContainerClosePacket.h" #include "minecraft/network/packet/MovePlayerPacket.h" #include "minecraft/network/packet/PlayerAbilitiesPacket.h" #include "minecraft/network/packet/PlayerActionPacket.h" #include "minecraft/network/packet/PlayerCommandPacket.h" #include "minecraft/network/packet/PlayerInputPacket.h" #include "minecraft/network/packet/TextureAndGeometryChangePacket.h" #include "minecraft/network/packet/TextureChangePacket.h" #include "minecraft/stats/Stat.h" #include "minecraft/world/effect/MobEffect.h" #include "minecraft/world/effect/MobEffectInstance.h" #include "minecraft/world/entity/player/Abilities.h" #include "minecraft/world/entity/player/Inventory.h" #include "minecraft/world/entity/player/Player.h" #include "minecraft/world/inventory/AbstractContainerMenu.h" #include "minecraft/world/level/Level.h" #include "minecraft/world/level/dimension/Dimension.h" #include "minecraft/world/phys/AABB.h" class User; class ItemEntity; // 4J added for testing #if defined(STRESS_TEST_MOVE) volatile bool stressTestEnabled = true; #endif 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; } bool MultiplayerLocalPlayer::hurt(DamageSource* source, float dmg) { return false; } void MultiplayerLocalPlayer::heal(float heal) {} void MultiplayerLocalPlayer::tick() { // 4J Added // 4J-PB - changing this to a game host option ot hide gamertags // bool bIsisPrimaryHost=g_NetworkManager.IsHost() && // (PlatformInput.GetPrimaryPad()==m_iPad); /*if((gameServices().getGameSettings(m_iPad,eGameSetting_PlayerVisibleInMap)!=0) != m_bShownOnMaps) { m_bShownOnMaps = (gameServices().getGameSettings(m_iPad,eGameSetting_PlayerVisibleInMap)!=0); if (m_bShownOnMaps) connection->send( std::shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::SHOW_ON_MAPS) ) ); else connection->send( std::shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::HIDE_ON_MAPS) ) ); }*/ if (!level->hasChunkAt(std::floor(x), 0, std::floor(z))) return; double tempX = x, tempY = y, tempZ = z; LocalPlayer::tick(); // 4J added for testing #if defined(STRESS_TEST_MOVE) if (stressTestEnabled) { StressTestMove(&tempX, &tempY, &tempZ); } #endif // 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)) { if (isRiding()) { connection->send(std::make_shared( yRot, xRot, onGround, abilities.flying)); connection->send(std::make_shared( xxa, yya, input->jumping, input->sneaking)); } else { sendPosition(); } } 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) { if (sprinting) connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::START_SPRINTING)); else connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::STOP_SPRINTING)); lastSprinting = sprinting; } bool sneaking = isSneaking(); if (sneaking != lastSneaked) { if (sneaking) connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::START_SNEAKING)); else connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::STOP_SNEAKING)); lastSneaked = sneaking; } bool idle = isIdle(); if (idle != lastIdle) { if (idle) connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::START_IDLEANIM)); else connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::STOP_IDLEANIM)); 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 != nullptr) { connection->send(std::make_shared( xd, -999, -999, zd, yRot, xRot, onGround, abilities.flying)); move = false; } else { if (move && rot) { connection->send(std::make_shared( x, bb.y0, y, z, yRot, xRot, onGround, abilities.flying)); } else if (move) { connection->send(std::make_shared( x, bb.y0, y, z, onGround, abilities.flying)); } else if (rot) { connection->send(std::make_shared( yRot, xRot, onGround, abilities.flying)); } else { connection->send(std::shared_ptr( new MovePlayerPacket(onGround, abilities.flying))); } } positionReminder++; lastOnGround = onGround; if (move) { xLast = x; yLast1 = bb.y0; yLast2 = y; zLast = z; positionReminder = 0; } if (rot) { yRotLast = yRot; xRotLast = xRot; } } std::shared_ptr MultiplayerLocalPlayer::drop() { connection->send(std::shared_ptr( new PlayerActionPacket(PlayerActionPacket::DROP_ITEM, 0, 0, 0, 0))); return nullptr; } void MultiplayerLocalPlayer::reallyDrop( std::shared_ptr itemEntity) {} void MultiplayerLocalPlayer::chat(const std::string& message) { connection->send(std::make_shared(message)); } void MultiplayerLocalPlayer::swing() { LocalPlayer::swing(); connection->send(std::shared_ptr( new AnimatePacket(shared_from_this(), AnimatePacket::SWING))); } void MultiplayerLocalPlayer::respawn() { connection->send(std::shared_ptr( new ClientCommandPacket(ClientCommandPacket::PERFORM_RESPAWN))); } void MultiplayerLocalPlayer::actuallyHurt(DamageSource* source, float dmg) { if (isInvulnerable()) return; setHealth(getHealth() - dmg); } // 4J Added override to capture event for tutorial messages void MultiplayerLocalPlayer::completeUsingItem() { Minecraft* pMinecraft = Minecraft::GetInstance(); if (useItem != nullptr && pMinecraft->localgameModes[m_iPad] != nullptr) { 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] != nullptr) { TutorialMode* gameMode = (TutorialMode*)pMinecraft->localgameModes[m_iPad]; Tutorial* tutorial = gameMode->getTutorial(); tutorial->onEffectChanged(MobEffect::effects[effect->getId()]); } Player::onEffectAdded(effect); } void MultiplayerLocalPlayer::onEffectUpdated(MobEffectInstance* effect, bool doRefreshAttributes) { Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft->localgameModes[m_iPad] != nullptr) { TutorialMode* gameMode = (TutorialMode*)pMinecraft->localgameModes[m_iPad]; Tutorial* tutorial = gameMode->getTutorial(); tutorial->onEffectChanged(MobEffect::effects[effect->getId()]); } Player::onEffectUpdated(effect, doRefreshAttributes); } void MultiplayerLocalPlayer::onEffectRemoved(MobEffectInstance* effect) { Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft->localgameModes[m_iPad] != nullptr) { TutorialMode* gameMode = (TutorialMode*)pMinecraft->localgameModes[m_iPad]; Tutorial* tutorial = gameMode->getTutorial(); tutorial->onEffectChanged(MobEffect::effects[effect->getId()], true); } Player::onEffectRemoved(effect); } void MultiplayerLocalPlayer::closeContainer() { connection->send(std::shared_ptr( new ContainerClosePacket(containerMenu->containerId))); clientSideCloseContainer(); } // close the container without sending a packet to the server void MultiplayerLocalPlayer::clientSideCloseContainer() { inventory->setCarried(nullptr); LocalPlayer::closeContainer(); } void MultiplayerLocalPlayer::hurtTo(float newHealth, uint8_t damageSource) { if (flashOnSetHealth) { LocalPlayer::hurtTo(newHealth, damageSource); } else { setHealth(newHealth); flashOnSetHealth = true; } } void MultiplayerLocalPlayer::awardStat(Stat* stat, const std::vector& param) { if (stat == nullptr) { return; } if (stat->awardLocallyOnly) { LocalPlayer::awardStat(stat, param); } else { return; } } void MultiplayerLocalPlayer::awardStatFromServer(Stat* stat, std::vector& param) { if (stat != nullptr && !stat->awardLocallyOnly) { LocalPlayer::awardStat(stat, param); } } void MultiplayerLocalPlayer::onUpdateAbilities() { connection->send(std::shared_ptr( new PlayerAbilitiesPacket(&abilities))); } bool MultiplayerLocalPlayer::isLocalPlayer() { return true; } void MultiplayerLocalPlayer::sendRidingJump() { connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::RIDING_JUMP, (int)(getJumpRidingScale() * 100.0f))); } void MultiplayerLocalPlayer::sendOpenInventory() { connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::OPEN_INVENTORY)); } void MultiplayerLocalPlayer::ride(std::shared_ptr e) { bool wasRiding = riding != nullptr; LocalPlayer::ride(e); bool isRiding = riding != nullptr; // 4J Added if (wasRiding && !isRiding) { setSneaking(false); input->sneaking = false; } updateRichPresence(); Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft->localgameModes[m_iPad] != nullptr) { TutorialMode* gameMode = (TutorialMode*)pMinecraft->localgameModes[m_iPad]; if (wasRiding && !isRiding) { gameMode->getTutorial()->changeTutorialState( e_Tutorial_State_Gameplay); } else if (!wasRiding && isRiding) { gameMode->getTutorial()->onRideEntity(e); } } } void MultiplayerLocalPlayer::StopSleeping() { connection->send(std::make_shared( shared_from_this(), PlayerCommandPacket::STOP_SLEEPING)); } // 4J Added void MultiplayerLocalPlayer::setAndBroadcastCustomSkin(std::uint32_t skinId) { std::uint32_t oldSkinIndex = getCustomSkin(); LocalPlayer::setCustomSkin(skinId); #if !defined(_CONTENT_PACKAGE) printf("Skin for local player %s has changed to %s (%d)\n", name.c_str(), customTextureUrl.c_str(), getPlayerDefaultSkin()); #endif if (getCustomSkin() != oldSkinIndex) connection->send(std::shared_ptr( new TextureAndGeometryChangePacket( shared_from_this(), gameServices().getPlayerSkinName(GetXboxPad())))); } void MultiplayerLocalPlayer::setAndBroadcastCustomCape(std::uint32_t capeId) { std::uint32_t oldCapeIndex = getCustomCape(); LocalPlayer::setCustomCape(capeId); #if !defined(_CONTENT_PACKAGE) printf("Cape for local player %s has changed to %s\n", name.c_str(), customTextureUrl2.c_str()); #endif if (getCustomCape() != oldCapeIndex) connection->send(std::make_shared( shared_from_this(), TextureChangePacket::e_TextureChange_Cape, gameServices().getPlayerCapeName(GetXboxPad()))); } // 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. #if defined(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 ) { Log::info("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); 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