#include "../Platform/stdafx.h" #include "ServerPlayer.h" #include "ServerPlayerGameMode.h" #include "../Level/ServerLevel.h" #include "../MinecraftServer.h" #include "EntityTracker.h" #include "../Network/PlayerConnection.h" #include "../GameState/Settings.h" #include "../Network/PlayerList.h" #include "../Level/MultiPlayerLevel.h" #include "../../Minecraft.World/Headers/net.minecraft.network.packet.h" #include "../../Minecraft.World/Headers/net.minecraft.world.damagesource.h" #include "../../Minecraft.World/Headers/net.minecraft.world.inventory.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.storage.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.dimension.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.projectile.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.animal.h" #include "../../Minecraft.World/Headers/net.minecraft.world.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.item.trading.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.scores.h" #include "../../Minecraft.World/Headers/net.minecraft.world.scores.criteria.h" #include "../../Minecraft.World/Headers/net.minecraft.stats.h" #include "../../Minecraft.World/Headers/net.minecraft.locale.h" #include "../../Minecraft.World/Util/Pos.h" #include "../../Minecraft.World/Util/Random.h" #include "../../Minecraft.World/Level/LevelChunk.h" #include "../Rendering/LevelRenderer.h" ServerPlayer::ServerPlayer(MinecraftServer* server, Level* level, const std::wstring& name, ServerPlayerGameMode* gameMode) : Player(level, name) { // 4J - added initialisers connection = nullptr; lastMoveX = lastMoveZ = 0; spewTimer = 0; lastRecordedHealthAndAbsorption = FLT_MIN; lastSentHealth = -99999999; lastSentFood = -99999999; lastFoodSaturationZero = true; lastSentExp = -99999999; invulnerableTime = 20 * 3; containerCounter = 0; ignoreSlotUpdateHack = false; latency = 0; wonGame = false; m_enteredEndExitPortal = false; // lastCarried = ItemInstanceArray(5); lastActionTime = 0; viewDistance = server->getPlayers()->getViewDistance(); // gameMode->player = this; // 4J - removed to avoid use of // shared_from_this in ctor, now set up externally this->gameMode = gameMode; Pos* spawnPos = level->getSharedSpawnPos(); int xx = spawnPos->x; int zz = spawnPos->z; int yy = spawnPos->y; delete spawnPos; if (!level->dimension->hasCeiling && level->getLevelData()->getGameType() != GameType::ADVENTURE) { level->isFindingSpawn = true; int radius = std::max(5, server->getSpawnProtectionRadius() - 6); // 4J added - do additional checking that we aren't putting the player // in deep water. Give up after 20 or goes just in case the spawnPos is // somehow in a really bad spot and we would just lock here. int waterDepth = 0; int attemptCount = 0; int xx2, yy2, zz2; int minXZ = -(level->dimension->getXZSize() * 16) / 2; int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1; bool playerNear = false; do { // Also check that we aren't straying outside of the map do { xx2 = xx + random->nextInt(radius * 2) - radius; zz2 = zz + random->nextInt(radius * 2) - radius; } while ((xx2 > maxXZ) || (xx2 < minXZ) || (zz2 > maxXZ) || (zz2 < minXZ)); yy2 = level->getTopSolidBlock(xx2, zz2); waterDepth = 0; int yw = yy2; while ((yw < 128) && ((level->getTile(xx2, yw, zz2) == Tile::water_Id) || (level->getTile(xx2, yw, zz2) == Tile::calmWater_Id))) { yw++; waterDepth++; } attemptCount++; playerNear = (level->getNearestPlayer(xx + 0.5, yy, zz + 0.5, 3) != NULL); } while ((waterDepth > 1) && (!playerNear) && (attemptCount < 20)); xx = xx2; yy = yy2; zz = zz2; level->isFindingSpawn = false; } this->server = server; footSize = 0; heightOffset = 0; // 4J - this height used to be set up after moveTo, but that ends up // with the y value being incorrect as it depends on this offset this->moveTo(xx + 0.5, yy, zz + 0.5, 0, 0); // 4J Handled later // while (!level->getCubes(this, bb).empty()) //{ // setPos(x, y + 1, z); //} // m_UUID = name; // 4J Added lastBrupSendTickCount = 0; } ServerPlayer::~ServerPlayer() { // delete [] lastCarried.data; } // 4J added - add bits to a flag array that is passed in, to represent those // entities which have small Ids, and are in our vector of entitiesToRemove. If // there aren't any entities to be flagged, this function does nothing. If there // *are* entities to be added, uses the removedFound as an input to determine if // the flag array has already been initialised at all - if it has been, then // just adds flags to it; if it hasn't, then memsets the output flag array and // adds to it for this ServerPlayer. void ServerPlayer::flagEntitiesToBeRemoved(unsigned int* flags, bool* removedFound) { if (entitiesToRemove.empty()) { return; } if ((*removedFound) == false) { *removedFound = true; memset(flags, 0, 2048 / 32); } AUTO_VAR(it, entitiesToRemove.begin()); for (AUTO_VAR(it, entitiesToRemove.begin()); it != entitiesToRemove.end(); it++) { int index = *it; if (index < 2048) { unsigned int i = index / 32; unsigned int j = index % 32; unsigned int uiMask = 0x80000000 >> j; flags[i] |= uiMask; } } } void ServerPlayer::readAdditionalSaveData(CompoundTag* entityTag) { Player::readAdditionalSaveData(entityTag); if (entityTag->contains(L"playerGameType")) { // 4J Stu - We do not want to change the game mode for the player, // instead we let the server override it globally // if (MinecraftServer::getInstance()->getForceGameType()) //{ // gameMode->setGameModeForPlayer(MinecraftServer::getInstance()->getDefaultGameType()); //} // else //{ // gameMode->setGameModeForPlayer(GameType::byId(entityTag->getInt(L"playerGameType"))); //} } GameRulesInstance* grs = gameMode->getGameRules(); if (entityTag->contains(L"GameRules") && grs != NULL) { byteArray ba = entityTag->getByteArray(L"GameRules"); ByteArrayInputStream bais(ba); DataInputStream dis(&bais); grs->read(&dis); dis.close(); bais.close(); // delete [] ba.data; } } void ServerPlayer::addAdditonalSaveData(CompoundTag* entityTag) { Player::addAdditonalSaveData(entityTag); GameRulesInstance* grs = gameMode->getGameRules(); if (grs != NULL) { ByteArrayOutputStream baos; DataOutputStream dos(&baos); grs->write(&dos); entityTag->putByteArray(L"GameRules", baos.buf); baos.buf.data = NULL; dos.close(); baos.close(); } // 4J Stu - We do not want to change the game mode for the player, instead // we let the server override it globally // entityTag->putInt(L"playerGameType", // gameMode->getGameModeForPlayer()->getId()); } void ServerPlayer::giveExperienceLevels(int amount) { Player::giveExperienceLevels(amount); lastSentExp = -1; } void ServerPlayer::initMenu() { containerMenu->addSlotListener(this); } void ServerPlayer::setDefaultHeadHeight() { heightOffset = 0; } float ServerPlayer::getHeadHeight() { return 1.62f; } void ServerPlayer::tick() { gameMode->tick(); if (invulnerableTime > 0) invulnerableTime--; containerMenu->broadcastChanges(); // 4J-JEV, hook for Durango event 'EnteredNewBiome'. Biome* newBiome = level->getBiome(x, z); if (newBiome != currentBiome) { awardStat(GenericStats::enteredBiome(newBiome->id), GenericStats::param_enteredBiome(newBiome->id)); currentBiome = newBiome; } if (!level->isClientSide) { if (!containerMenu->stillValid( std::dynamic_pointer_cast(shared_from_this()))) { closeContainer(); containerMenu = inventoryMenu; } } flushEntitiesToRemove(); } // 4J Stu - Split out here so that we can call this from other places void ServerPlayer::flushEntitiesToRemove() { while (!entitiesToRemove.empty()) { int sz = entitiesToRemove.size(); int amount = std::min(sz, RemoveEntitiesPacket::MAX_PER_PACKET); intArray ids(amount); int pos = 0; AUTO_VAR(it, entitiesToRemove.begin()); while (it != entitiesToRemove.end() && pos < amount) { ids[pos++] = *it; it = entitiesToRemove.erase(it); } connection->send(std::shared_ptr( new RemoveEntitiesPacket(ids))); } } // 4J - have split doTick into 3 bits, so that we can call the // doChunkSendingTick separately, but still do the equivalent of what calling a // full doTick used to do, by calling this method void ServerPlayer::doTick(bool sendChunks, bool dontDelayChunks /*=false*/, bool ignorePortal /*=false*/) { m_ignorePortal = ignorePortal; if (sendChunks) { updateFrameTick(); } doTickA(); if (sendChunks) { doChunkSendingTick(dontDelayChunks); } doTickB(); m_ignorePortal = false; } void ServerPlayer::doTickA() { Player::tick(); for (unsigned int i = 0; i < inventory->getContainerSize(); i++) { std::shared_ptr ie = inventory->getItem(i); if (ie != NULL) { // 4J - removed condition. These were getting lower priority than // tile update packets etc. on the slow outbound queue, and so were // extremely slow to send sometimes, particularly at the start of a // game. They don't typically seem to be massive and shouldn't be // send when there isn't actually any updating to do. if (Item::items[ie->id] ->isComplex()) // && connection->countDelayedPackets() <= // 2) { std::shared_ptr packet = (dynamic_cast(Item::items[ie->id]) ->getUpdatePacket(ie, level, std::dynamic_pointer_cast( shared_from_this()))); if (packet != NULL) { connection->send(packet); } } } } } // 4J - split off the chunk sending bit of the tick here from ::doTick so we can // do this exactly once per player per server tick void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) { // printf("[%d] %s: sendChunks: %d, empty: %d\n",tickCount, // connection->getNetworkPlayer()->GetUID().getOnlineID(),sendChunks,chunksToSend.empty()); if (!chunksToSend.empty()) { ChunkPos nearest = chunksToSend.front(); bool nearestValid = false; // 4J - reinstated and optimised some code that was commented out in the // original, to make sure that we always send the nearest chunk to the // player. The original uses the bukkit sorting thing to try and avoid // doing this, but the player can quickly wander away from the centre of // the spiral of chunks that that method creates, long before // transmission of them is complete. double dist = DBL_MAX; for (AUTO_VAR(it, chunksToSend.begin()); it != chunksToSend.end(); it++) { ChunkPos chunk = *it; if (level->isChunkFinalised(chunk.x, chunk.z)) { double newDist = chunk.distanceToSqr(x, z); if ((!nearestValid) || (newDist < dist)) { nearest = chunk; dist = chunk.distanceToSqr(x, z); nearestValid = true; } } } // if (nearest != NULL) // 4J - removed as we don't have // references here if (nearestValid) { bool okToSend = false; // if (dist < 32 * 32) okToSend = true; if (connection->isLocal()) { if (!connection->done) okToSend = true; } else { bool canSendToPlayer = MinecraftServer::chunkPacketManagement_CanSendTo( connection->getNetworkPlayer()); // app.DebugPrintf(">>> %d\n", // canSendToPlayer); if( // connection->getNetworkPlayer() ) // { // app.DebugPrintf("%d: // canSendToPlayer %d, countDelayedPackets %d // GetSendQueueSizeBytes %d done: %d\n", // connection->getNetworkPlayer()->GetSmallId(), // canSendToPlayer, // connection->countDelayedPackets(), // g_NetworkManager.GetHostPlayer()->GetSendQueueSizeMessages( // NULL, true ), // connection->done); // } if (dontDelayChunks || (canSendToPlayer && #ifdef _XBOX_ONE // The network manager on xbox one doesn't currently split // data into slow & fast queues - since we can only measure // both together then bytes provides a better metric than // count of data items to determine if we should avoid // queueing too much up (g_NetworkManager.GetHostPlayer()->GetSendQueueSizeBytes( NULL, true) < 8192) && #elif defined _XBOX (g_NetworkManager.GetHostPlayer() ->GetSendQueueSizeMessages(NULL, true) < 4) && #else (connection->countDelayedPackets() < 4) && (g_NetworkManager.GetHostPlayer() ->GetSendQueueSizeMessages(NULL, true) < 4) && #endif //(tickCount - lastBrupSendTickCount) > //(connection->getNetworkPlayer()->GetCurrentRtt()>>4) && !connection->done)) { lastBrupSendTickCount = tickCount; okToSend = true; MinecraftServer::chunkPacketManagement_DidSendTo( connection->getNetworkPlayer()); // static // unordered_map mapLastTime; // int64_t thisTime = // System::currentTimeMillis(); // int64_t lastTime = // mapLastTime[connection->getNetworkPlayer()->GetUID().toString()]; // app.DebugPrintf(" - OK // to send (%d ms since last)\n", thisTime - lastTime); // mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] //= thisTime; } else { // app.DebugPrintf(" - \n"); } } if (okToSend) { ServerLevel* level = server->getLevel(dimension); int flagIndex = getFlagIndexForChunk(nearest, this->level->dimension->id); chunksToSend.remove(nearest); bool chunkDataSent = false; // Don't send the chunk to the local machine - the chunks there // are mapped directly to the server chunks. We could // potentially stop this process earlier on by not adding to the // chunksToSend list, but that would stop the tile entities // being broadcast too if (!connection ->isLocal()) // force here to disable sharing of data { // Don't send the chunk if we've set a flag to say that // we've already sent it to this machine. This stops two // things (1) Sending a chunk to multiple players doing // split screen on one machine (2) Sending a chunk that // we've already sent as the player moves around. The // original version of the game resends these, since it // maintains // a region of active chunks round each player in the // "infinite" world, but in our finite world, we don't // ever request that chunks be unloaded on the client // and so just gradually build up more and more of the // finite set of chunks as the player moves if (!g_NetworkManager.SystemFlagGet( connection->getNetworkPlayer(), flagIndex)) { // app.DebugPrintf("Creating // BRUP for %d %d\n",nearest.x, nearest.z); PIXBeginNamedEvent(0, "Creation BRUP for sending\n"); int64_t before = System::currentTimeMillis(); std::shared_ptr packet = std::shared_ptr( new BlockRegionUpdatePacket( nearest.x * 16, 0, nearest.z * 16, 16, Level::maxBuildHeight, 16, level)); int64_t after = System::currentTimeMillis(); // app.DebugPrintf(">>><<< //%d ms\n",after-before); PIXEndNamedEvent(); if (dontDelayChunks) packet->shouldDelay = false; if (packet->shouldDelay == true) { // Other than the first packet we always want these // initial chunks to be sent over QNet at a lower // priority connection->queueSend(packet); } else { connection->send(packet); } // Set flag to say we have send this block already to // this system g_NetworkManager.SystemFlagSet( connection->getNetworkPlayer(), flagIndex); chunkDataSent = true; } } else { // For local connections, we'll need to copy the lighting // data over from server to client at this point. This is to // try and keep lighting as similar as possible to the java // version, where client & server are individually // responsible for maintaining their lighting (since 1.2.3). // This is really an alternative to sending the lighting // data over the fake local network connection at this // point. MultiPlayerLevel* clientLevel = Minecraft::GetInstance()->getLevel( level->dimension->id); if (clientLevel) { LevelChunk* lc = clientLevel->getChunk(nearest.x, nearest.z); lc->reSyncLighting(); lc->recalcHeightmapOnly(); clientLevel->setTilesDirty( nearest.x * 16 + 1, 1, nearest.z * 16 + 1, nearest.x * 16 + 14, Level::maxBuildHeight - 2, nearest.z * 16 + 14); } } // Don't send TileEntity data until we have sent the block data if (connection->isLocal() || chunkDataSent) { std::vector >* tes = level->getTileEntitiesInRegion( nearest.x * 16, 0, nearest.z * 16, nearest.x * 16 + 16, Level::maxBuildHeight, nearest.z * 16 + 16); for (unsigned int i = 0; i < tes->size(); i++) { // 4J Stu - Added delay param to ensure that these // arrive after the BRUPs from above Fix for #9169 - ART // : Sign text is replaced with the words “Awaiting // approval”. broadcast(tes->at(i), !connection->isLocal() && !dontDelayChunks); } delete tes; } } } } } void ServerPlayer::doTickB() { #ifndef _CONTENT_PACKAGE // check if there's a debug dimension change requested // if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<dimension->id == 0 ) // { // isInsidePortal=true; // portalTime=1; // } // unsigned int // uiVal=app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); // app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),uiVal&~(1L<dimension->id == 0 ) // { // server->players->toggleDimension( // std::dynamic_pointer_cast( shared_from_this() ), 1 ); // } // unsigned int // uiVal=app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); // app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),uiVal&~(1L<dimension->id != 0) { isInsidePortal = true; portalTime = 1; } unsigned int uiVal = app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); app.SetGameSettingsDebugMask( ProfileManager.GetPrimaryPad(), uiVal & ~(1L << eDebugSetting_GoToOverworld)); } #endif if (getHealth() != lastSentHealth || lastSentFood != foodData.getFoodLevel() || ((foodData.getSaturationLevel() == 0) != lastFoodSaturationZero)) { // 4J Stu - Added m_lastDamageSource for telemetry connection->send(std::shared_ptr(new SetHealthPacket( getHealth(), foodData.getFoodLevel(), foodData.getSaturationLevel(), m_lastDamageSource))); lastSentHealth = getHealth(); lastSentFood = foodData.getFoodLevel(); lastFoodSaturationZero = foodData.getSaturationLevel() == 0; } if (getHealth() + getAbsorptionAmount() != lastRecordedHealthAndAbsorption) { lastRecordedHealthAndAbsorption = getHealth() + getAbsorptionAmount(); std::vector* objectives = getScoreboard()->findObjectiveFor(ObjectiveCriteria::HEALTH); if (objectives) { std::vector > players = std::vector >(); players.push_back( std::dynamic_pointer_cast(shared_from_this())); for (AUTO_VAR(it, objectives->begin()); it != objectives->end(); ++it) { Objective* objective = *it; getScoreboard() ->getPlayerScore(getAName(), objective) ->updateFor(&players); } delete objectives; } } if (totalExperience != lastSentExp) { lastSentExp = totalExperience; connection->send( std::shared_ptr(new SetExperiencePacket( experienceProgress, totalExperience, experienceLevel))); } std:: } std::shared_ptr ServerPlayer::getCarried(int slot) { if (slot == 0) return inventory->getSelected(); return inventory->armor[slot - 1]; } void ServerPlayer::die(DamageSource* source) { server->getPlayers()->broadcastAll( getCombatTracker()->getDeathMessagePacket()); if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) { inventory->dropAll(); } std::vector* objectives = level->getScoreboard()->findObjectiveFor( ObjectiveCriteria::DEATH_COUNT); if (objectives) { for (int i = 0; i < objectives->size(); i++) { Objective* objective = objectives->at(i); Score* score = getScoreboard()->getPlayerScore(getAName(), objective); score->increment(); } delete objectives; } std::shared_ptr killer = getKillCredit(); if (killer != NULL) killer->awardKillScore(shared_from_this(), deathScore); // awardStat(Stats::deaths, 1); } bool ServerPlayer::hurt(DamageSource* dmgSource, float dmg) { if (isInvulnerable()) return false; // 4J: Not relevant to console servers // Allow falldamage on dedicated pvpservers -- so people cannot cheat their // way out of 'fall traps' // bool allowFallDamage = server->isPvpAllowed() && // server->isDedicatedServer() && server->isPvpAllowed() && // (dmgSource->msgId.compare(L"fall") == 0); if (!server->isPvpAllowed() && invulnerableTime > 0 && dmgSource != DamageSource::outOfWorld) return false; if (dynamic_cast(dmgSource) != NULL) { // 4J Stu - Fix for #46422 - TU5: Crash: Gameplay: Crash when being hit // by a trap using a dispenser getEntity returns the owner of // projectiles, and this would never be the arrow. The owner is // sometimes NULL. std::shared_ptr source = dmgSource->getDirectEntity(); if (source->instanceof(eTYPE_PLAYER) && !std::dynamic_pointer_cast(source)->canHarmPlayer( std::dynamic_pointer_cast(shared_from_this()))) { return false; } if ((source != NULL) && source->instanceof(eTYPE_ARROW)) { std::shared_ptr arrow = std::dynamic_pointer_cast(source); if ((arrow->owner != NULL) && arrow->owner->instanceof(eTYPE_PLAYER) && !canHarmPlayer( std::dynamic_pointer_cast(arrow->owner))) { return false; } } } bool returnVal = Player::hurt(dmgSource, dmg); if (returnVal) { // 4J Stu - Work out the source of this damage for telemetry m_lastDamageSource = eTelemetryChallenges_Unknown; if (dmgSource == DamageSource::fall) m_lastDamageSource = eTelemetryPlayerDeathSource_Fall; else if (dmgSource == DamageSource::onFire || dmgSource == DamageSource::inFire) m_lastDamageSource = eTelemetryPlayerDeathSource_Fire; else if (dmgSource == DamageSource::lava) m_lastDamageSource = eTelemetryPlayerDeathSource_Lava; else if (dmgSource == DamageSource::drown) m_lastDamageSource = eTelemetryPlayerDeathSource_Water; else if (dmgSource == DamageSource::inWall) m_lastDamageSource = eTelemetryPlayerDeathSource_Suffocate; else if (dmgSource == DamageSource::outOfWorld) m_lastDamageSource = eTelemetryPlayerDeathSource_OutOfWorld; else if (dmgSource == DamageSource::cactus) m_lastDamageSource = eTelemetryPlayerDeathSource_Cactus; else { std::shared_ptr source = dmgSource->getEntity(); if (source != NULL) { switch (source->GetType()) { case eTYPE_PLAYER: case eTYPE_SERVERPLAYER: m_lastDamageSource = eTelemetryPlayerDeathSource_Player_Weapon; break; case eTYPE_WOLF: m_lastDamageSource = eTelemetryPlayerDeathSource_Wolf; break; case eTYPE_CREEPER: m_lastDamageSource = eTelemetryPlayerDeathSource_Explosion_Creeper; break; case eTYPE_SKELETON: m_lastDamageSource = eTelemetryPlayerDeathSource_Skeleton; break; case eTYPE_SPIDER: m_lastDamageSource = eTelemetryPlayerDeathSource_Spider; break; case eTYPE_ZOMBIE: m_lastDamageSource = eTelemetryPlayerDeathSource_Zombie; break; case eTYPE_PIGZOMBIE: m_lastDamageSource = eTelemetryPlayerDeathSource_ZombiePigman; break; case eTYPE_GHAST: m_lastDamageSource = eTelemetryPlayerDeathSource_Ghast; break; case eTYPE_SLIME: m_lastDamageSource = eTelemetryPlayerDeathSource_Slime; break; case eTYPE_PRIMEDTNT: m_lastDamageSource = eTelemetryPlayerDeathSource_Explosion_Tnt; break; case eTYPE_ARROW: if ((std::dynamic_pointer_cast(source))->owner != NULL) { std::shared_ptr attacker = (std::dynamic_pointer_cast(source)) ->owner; if (attacker != NULL) { switch (attacker->GetType()) { case eTYPE_SKELETON: m_lastDamageSource = eTelemetryPlayerDeathSource_Skeleton; break; case eTYPE_PLAYER: case eTYPE_SERVERPLAYER: m_lastDamageSource = eTelemetryPlayerDeathSource_Player_Arrow; break; default: break; } } } break; case eTYPE_FIREBALL: m_lastDamageSource = eTelemetryPlayerDeathSource_Ghast; break; default: break; }; } }; } return returnVal; } bool ServerPlayer::canHarmPlayer(std::shared_ptr target) { if (!server->isPvpAllowed()) return false; if (!isAllowedToAttackPlayers()) return false; return Player::canHarmPlayer(target); } // 4J: Added for checking when only player name is provided (possible player // isn't on server), e.g. can harm owned animals bool ServerPlayer::canHarmPlayer(std::wstring targetName) { bool canHarm = true; std::shared_ptr owner = server->getPlayers()->getPlayer(targetName); if (owner != NULL) { if ((shared_from_this() != owner) && canHarmPlayer(owner)) canHarm = false; } else { if (this->name != targetName && (!isAllowedToAttackPlayers() || !server->isPvpAllowed())) canHarm = false; } return canHarm; } void ServerPlayer::changeDimension(int i) { if (!connection->hasClientTickedOnce()) return; if (dimension == 1 && i == 1) { app.DebugPrintf("Start win game\n"); awardStat(GenericStats::winGame(), GenericStats::param_winGame()); // All players on the same system as this player should also be removed // from the game while the Win screen is shown INetworkPlayer* thisPlayer = connection->getNetworkPlayer(); if (!wonGame) { level->removeEntity(shared_from_this()); wonGame = true; m_enteredEndExitPortal = true; // We only flag this for the player in the portal connection->send( std::shared_ptr(new GameEventPacket( GameEventPacket::WIN_GAME, thisPlayer->GetUserIndex()))); app.DebugPrintf("Sending packet to %d\n", thisPlayer->GetUserIndex()); } if (thisPlayer != NULL) { for (AUTO_VAR(it, MinecraftServer::getInstance() ->getPlayers() ->players.begin()); it != MinecraftServer::getInstance()->getPlayers()->players.end(); ++it) { std::shared_ptr servPlayer = *it; INetworkPlayer* checkPlayer = servPlayer->connection->getNetworkPlayer(); if (thisPlayer != checkPlayer && checkPlayer != NULL && thisPlayer->IsSameSystem(checkPlayer) && !servPlayer->wonGame) { servPlayer->wonGame = true; servPlayer->connection->send( std::shared_ptr( new GameEventPacket(GameEventPacket::WIN_GAME, thisPlayer->GetUserIndex()))); app.DebugPrintf("Sending packet to %d\n", thisPlayer->GetUserIndex()); } } } app.DebugPrintf("End win game\n"); } else { if (dimension == 0 && i == 1) { awardStat(GenericStats::theEnd(), GenericStats::param_theEnd()); Pos* pos = server->getLevel(i)->getDimensionSpecificSpawn(); if (pos != NULL) { connection->teleport(pos->x, pos->y, pos->z, 0, 0); delete pos; } i = 1; } else { // 4J: Removed on the advice of the mighty King of Achievments (JV) // awardStat(GenericStats::portal(), GenericStats::param_portal()); } server->getPlayers()->toggleDimension( std::dynamic_pointer_cast(shared_from_this()), i); lastSentExp = -1; lastSentHealth = -1; lastSentFood = -1; } } // 4J Added delay param void ServerPlayer::broadcast(std::shared_ptr te, bool delay /*= false*/) { if (te != NULL) { std::shared_ptr p = te->getUpdatePacket(); if (p != NULL) { p->shouldDelay = delay; if (delay) connection->queueSend(p); else connection->send(p); } } } void ServerPlayer::take(std::shared_ptr e, int orgCount) { Player::take(e, orgCount); containerMenu->broadcastChanges(); } Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, bool bTestUse) { BedSleepingResult result = Player::startSleepInBed(x, y, z, bTestUse); if (result == OK) { std::shared_ptr p = std::shared_ptr( new EntityActionAtPositionPacket( shared_from_this(), EntityActionAtPositionPacket::START_SLEEP, x, y, z)); getLevel()->getTracker()->broadcast(shared_from_this(), p); connection->teleport(this->x, this->y, this->z, yRot, xRot); connection->send(p); } return result; } void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint) { if (isSleeping()) { getLevel()->getTracker()->broadcastAndSend( shared_from_this(), std::shared_ptr( new AnimatePacket(shared_from_this(), AnimatePacket::WAKE_UP))); } Player::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); if (connection != NULL) connection->teleport(x, y, z, yRot, xRot); } void ServerPlayer::ride(std::shared_ptr e) { Player::ride(e); connection->send( std::shared_ptr(new SetEntityLinkPacket( SetEntityLinkPacket::RIDING, shared_from_this(), riding))); // 4J Removed this - The act of riding will be handled on the client and // will change the position of the player. If we also teleport it then we // can end up with a repeating movements, e.g. bouncing up and down after // exiting a boat due to slight differences in position on the client and // server // connection->teleport(x, y, z, yRot, xRot); } void ServerPlayer::checkFallDamage(double ya, bool onGround) {} void ServerPlayer::doCheckFallDamage(double ya, bool onGround) { Player::checkFallDamage(ya, onGround); } void ServerPlayer::openTextEdit(std::shared_ptr sign) { std::shared_ptr signTE = std::dynamic_pointer_cast(sign); if (signTE != NULL) { signTE->setAllowedPlayerEditor( std::dynamic_pointer_cast(shared_from_this())); connection->send( std::shared_ptr(new TileEditorOpenPacket( TileEditorOpenPacket::SIGN, sign->x, sign->y, sign->z))); } } void ServerPlayer::nextContainerCounter() { containerCounter = (containerCounter % 100) + 1; } bool ServerPlayer::startCrafting(int x, int y, int z) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false))); containerMenu = new CraftingMenu(inventory, level, x, y, z); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open crafting container when one was " "already open\n"); } return true; } bool ServerPlayer::openFireworks(int x, int y, int z) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false))); containerMenu = new FireworksMenu(inventory, level, x, y, z); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else if (dynamic_cast(containerMenu) != NULL) { closeContainer(); nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false))); containerMenu = new FireworksMenu(inventory, level, x, y, z); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open crafting container when one was " "already open\n"); } return true; } bool ServerPlayer::startEnchanting(int x, int y, int z, const std::wstring& name) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty() ? L"" : name, 9, !name.empty()))); containerMenu = new EnchantmentMenu(inventory, level, x, y, z); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open enchanting container when one was " "already open\n"); } return true; } bool ServerPlayer::startRepairing(int x, int y, int z) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false))); containerMenu = new AnvilMenu( inventory, level, x, y, z, std::dynamic_pointer_cast(shared_from_this())); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open enchanting container when one was " "already open\n"); } return true; } bool ServerPlayer::openContainer(std::shared_ptr container) { if (containerMenu == inventoryMenu) { nextContainerCounter(); // 4J-JEV: Added to distinguish between ender, bonus, large and small // chests (for displaying the name of the chest). int containerType = container->getContainerType(); assert(containerType >= 0); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()))); containerMenu = new ContainerMenu(inventory, container); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open container when one was already open\n"); } return true; } bool ServerPlayer::openHopper( std::shared_ptr container) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()))); containerMenu = new HopperMenu(inventory, container); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open hopper container when one was already " "open\n"); } return true; } bool ServerPlayer::openHopper(std::shared_ptr container) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName()))); containerMenu = new HopperMenu(inventory, container); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open minecart hopper container when one was " "already open\n"); } return true; } bool ServerPlayer::openFurnace(std::shared_ptr furnace) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()))); containerMenu = new FurnaceMenu(inventory, furnace); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open furnace when one was already open\n"); } return true; } bool ServerPlayer::openTrap(std::shared_ptr trap) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName()))); containerMenu = new TrapMenu(inventory, trap); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open dispenser when one was already open\n"); } return true; } bool ServerPlayer::openBrewingStand( std::shared_ptr brewingStand) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName()))); containerMenu = new BrewingStandMenu(inventory, brewingStand); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open brewing stand when one was already " "open\n"); } return true; } bool ServerPlayer::openBeacon(std::shared_ptr beacon) { if (containerMenu == inventoryMenu) { nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName()))); containerMenu = new BeaconMenu(inventory, beacon); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); } else { app.DebugPrintf( "ServerPlayer tried to open beacon when one was already open\n"); } return true; } bool ServerPlayer::openTrading(std::shared_ptr traderTarget, const std::wstring& name) { if (containerMenu == inventoryMenu) { nextContainerCounter(); containerMenu = new MerchantMenu(inventory, traderTarget, level); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); std::shared_ptr container = ((MerchantMenu*)containerMenu)->getTradeContainer(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::TRADER_NPC, name.empty() ? L"" : name, container->getContainerSize(), !name.empty()))); MerchantRecipeList* offers = traderTarget->getOffers( std::dynamic_pointer_cast(shared_from_this())); if (offers != NULL) { ByteArrayOutputStream rawOutput; DataOutputStream output(&rawOutput); // just to make sure the offers are matched to the container output.writeInt(containerCounter); offers->writeToStream(&output); connection->send(std::shared_ptr( new CustomPayloadPacket(CustomPayloadPacket::TRADER_LIST_PACKET, rawOutput.toByteArray()))); } } else { app.DebugPrintf( "ServerPlayer tried to open trading menu when one was already " "open\n"); } return true; } bool ServerPlayer::openHorseInventory( std::shared_ptr horse, std::shared_ptr container) { if (containerMenu != inventoryMenu) { closeContainer(); } nextContainerCounter(); connection->send( std::shared_ptr(new ContainerOpenPacket( containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId))); containerMenu = new HorseInventoryMenu(inventory, container, horse); containerMenu->containerId = containerCounter; containerMenu->addSlotListener(this); return true; } void ServerPlayer::slotChanged(AbstractContainerMenu* container, int slotIndstd::ex, std::shared_ptr item) { if (dynamic_cast(container->getSlot(slotIndex))) { return; } if (ignoreSlotUpdateHack) { // Do not send this packet! // // This is a horrible hack that makes sure that inventory clicks // that the client correctly predicted don't get sent out to the // client again. return; } connection->send(std::shared_ptr( new ContainerSetSlotPacket(container->containerId, slotIndex, item))); } void ServerPlayer::refreshContainer(AbstractContainerMenu* menu) { std::vector >* items = menu->getItems(); refreshContainer(menu, items); delete items; } void ServerPlayer::refreshContainer( AbstractContainerMenu* container, std::vector >* items) { connection->send(std::shared_ptr( new ContainerSetContentPacket(container->containerId, items))); connection->send(std::shared_ptr( new ContainerSetSlotPacket(-1, -1, inventory->getCarried()))); } void ServerPlayer::setContainerData(AbstractContainerMenu* container, int id, int value) { // 4J - added, so that furnace updates also have this hack if (ignoreSlotUpdateHack) { // Do not send this packet! // // This is a horrible hack that makes sure that inventory clicks // that the client correctly predicted don't get sent out to the // client again. return; } connection->send(std::shared_ptr( new ContainerSetDataPacket(container->containerId, id, value))); } void ServerPlayer::closeContainer() { connection->send(std::shared_ptr( new ContainerClosePacket(containerMenu->containerId))); doCloseContainer(); } void ServerPlayer::broadcastCarriedItem() { if (ignoreSlotUpdateHack) { // Do not send this packet! // This is a horrible hack that makes sure that inventory clicks // that the client correctly predicted don't get sent out to the // client again. return; } connection->send(std::shared_ptr( new ContainerSetSlotPacket(-1, -1, inventory->getCarried()))); } void ServerPlayer::doCloseContainer() { containerMenu->removed( std::dynamic_pointer_cast(shared_from_this())); containerMenu = inventoryMenu; } void ServerPlayer::setPlayerInput(float xxa, float yya, bool jumping, bool sneaking) { if (riding != NULL) { if (xxa >= -1 && xxa <= 1) this->xxa = xxa; if (yya >= -1 && yya <= 1) this->yya = yya; this->jumping = jumping; this->setSneaking(sneaking); } } void ServerPlayer::awardStat(Stat* stat, byteArray param) { if (stat == NULL) { delete[] param.data; return; } if (!stat->awardLocallyOnly) { #ifndef _DURANGO int count = *((int*)param.data); delete[] param.data; connection->send(std::shared_ptr( new AwardStatPacket(stat->id, count))); #else connection->send(std::shared_ptr( new AwardStatPacket(stat->id, param))); // byteArray deleted in AwardStatPacket destructor. #endif } else delete[] param.data; } void ServerPlayer::disconnect() { if (rider.lock() != NULL) rider.lock()->ride(shared_from_this()); if (m_isSleeping) { stopSleepInBed(true, false, false); } } void ServerPlayer::resetSentInfo() { lastSentHealth = -99999999; } void ServerPlayer::displayClientMessage(int messageId) { ChatPacket::EChatPacketMessage messageType = ChatPacket::e_ChatCustom; // Convert the message id to an enum that will not change between game // versions switch (messageId) { case IDS_TILE_BED_OCCUPIED: messageType = ChatPacket::e_ChatBedOccupied; connection->send( std::shared_ptr(new ChatPacket(L"", messageType))); break; case IDS_TILE_BED_NO_SLEEP: messageType = ChatPacket::e_ChatBedNoSleep; connection->send( std::shared_ptr(new ChatPacket(L"", messageType))); break; case IDS_TILE_BED_NOT_VALID: messageType = ChatPacket::e_ChatBedNotValid; connection->send( std::shared_ptr(new ChatPacket(L"", messageType))); break; case IDS_TILE_BED_NOTSAFE: messageType = ChatPacket::e_ChatBedNotSafe; connection->send( std::shared_ptr(new ChatPacket(L"", messageType))); break; case IDS_TILE_BED_PLAYERSLEEP: messageType = ChatPacket::e_ChatBedPlayerSleep; // broadcast to all the other players in the game for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() != player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatBedPlayerSleep))); } else { player->connection->send(std::shared_ptr( new ChatPacket(name, ChatPacket::e_ChatBedMeSleep))); } } return; break; case IDS_PLAYER_ENTERED_END: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() != player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerEnteredEnd))); } } break; case IDS_PLAYER_LEFT_END: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() != player) { player->connection->send(std::shared_ptr( new ChatPacket(name, ChatPacket::e_ChatPlayerLeftEnd))); } } break; case IDS_TILE_BED_MESLEEP: messageType = ChatPacket::e_ChatBedMeSleep; connection->send( std::shared_ptr(new ChatPacket(L"", messageType))); break; case IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxPigsSheepCows))); } } break; case IDS_MAX_CHICKENS_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxChickens))); } } break; case IDS_MAX_SQUID_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxSquid))); } } break; case IDS_MAX_BATS_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send(std::shared_ptr( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBats))); } } break; case IDS_MAX_WOLVES_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxWolves))); } } break; case IDS_MAX_MOOSHROOMS_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxMooshrooms))); } } break; case IDS_MAX_ENEMIES_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxEnemies))); } } break; case IDS_MAX_VILLAGERS_SPAWNED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxVillagers))); } } break; case IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxBredPigsSheepCows))); } } break; case IDS_MAX_CHICKENS_BRED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxBredChickens))); } } break; case IDS_MAX_MUSHROOMCOWS_BRED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxBredMooshrooms))); } } break; case IDS_MAX_WOLVES_BRED: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxBredWolves))); } } break; case IDS_CANT_SHEAR_MOOSHROOM: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerCantShearMooshroom))); } } break; case IDS_MAX_HANGINGENTITIES: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxHangingEntities))); } } break; case IDS_CANT_SPAWN_IN_PEACEFUL: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerCantSpawnInPeaceful))); } } break; case IDS_MAX_BOATS: for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) { std::shared_ptr player = server->getPlayers()->players[i]; if (shared_from_this() == player) { player->connection->send( std::shared_ptr(new ChatPacket( name, ChatPacket::e_ChatPlayerMaxBoats))); } } break; default: app.DebugPrintf( "Tried to send a chat packet to the player with an unhandled " "messageId\n"); assert(false); break; } // Language *language = Language::getInstance(); // wstring languageString = // app.GetString(messageId);//language->getElement(messageId); // connection->send( shared_ptr( new ChatPacket(L"", // messageType) ) ); } void ServerPlayer::completeUsingItem() { connection->send(std::shared_ptr( new EntityEventPacket(entityId, EntityEvent::USE_ITEM_COMPLETE))); Player::completeUsingItem(); } void ServerPlayer::startUsingItem(std::shared_ptr instance, int duration) { Player::startUsingItem(instance, duration); if (instance != NULL && instance->getItem() != NULL && instance->getItem()->getUseAnimation(instance) == UseAnim_eat) { getLevel()->getTracker()->broadcastAndSend( shared_from_this(), std::shared_ptr( new AnimatePacket(shared_from_this(), AnimatePacket::EAT))); } } void ServerPlayer::restoreFrom(std::shared_ptr oldPlayer, bool restoreAll) { Player::restoreFrom(oldPlayer, restoreAll); lastSentExp = -1; lastSentHealth = -1; lastSentFood = -1; entitiesToRemove = std::dynamic_pointer_cast(oldPlayer)->entitiesToRemove; } void ServerPlayer::onEffectAdded(MobEffectInstance* effect) { Player::onEffectAdded(effect); connection->send(std::shared_ptr( new UpdateMobEffectPacket(entityId, effect))); } void ServerPlayer::onEffectUpdated(MobEffectInstance* effect, bool doRefreshAttributes) { Player::onEffectUpdated(effect, doRefreshAttributes); connection->send(std::shared_ptr( new UpdateMobEffectPacket(entityId, effect))); } void ServerPlayer::onEffectRemoved(MobEffectInstance* effect) { Player::onEffectRemoved(effect); connection->send(std::shared_ptr( new RemoveMobEffectPacket(entityId, effect))); } void ServerPlayer::teleportTo(double x, double y, double z) { connection->teleport(x, y, z, yRot, xRot); } void ServerPlayer::crit(std::shared_ptr entity) { getLevel()->getTracker()->broadcastAndSend( shared_from_this(), std::shared_ptr(new AnimatePacket( entity, AnimatePacket::CRITICAL_HIT))); } void ServerPlayer::magicCrit(std::shared_ptr entity) { getLevel()->getTracker()->broadcastAndSend( shared_from_this(), std::shared_ptr(new AnimatePacket( entity, AnimatePacket::MAGIC_CRITICAL_HIT))); } void ServerPlayer::onUpdateAbilities() { if (connection == NULL) return; connection->send(std::shared_ptr( new PlayerAbilitiesPacket(&abilities))); } ServerLevel* ServerPlayer::getLevel() { return (ServerLevel*)level; } void ServerPlayer::setGameMode(GameType* mode) { gameMode->setGameModeForPlayer(mode); connection->send(std::shared_ptr( new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, mode->getId()))); } void ServerPlayer::sendMessage( const std::wstring& message, ChatPacket::EChatPacketMessage type /*= e_ChatCustom*/, int customData /*= -1*/, const std::wstring& additionalMessage /*= L""*/) { connection->send(std::shared_ptr( new ChatPacket(message, type, customData, additionalMessage))); } bool ServerPlayer::hasPermission(EGameCommand command) { return server->getPlayers()->isOp( std::dynamic_pointer_cast(shared_from_this())); // 4J: Removed permission level /*if( server->getPlayers()->isOp(std::dynamic_pointer_cast(shared_from_this())) ) { return server->getOperatorUserPermissionLevel() >= permissionLevel; } return false;*/ } // 4J - Don't use // void ServerPlayer::updateOptions(shared_ptr packet) //{ // // 4J - Don't need // //if (language.getLanguageList().containsKey(packet.getLanguage())) // //{ // // language.loadLanguage(packet->getLanguage()); // //} // // int dist = 16 * 16 >> packet->getViewDistance(); // if (dist > PlayerChunkMap::MIN_VIEW_DISTANCE && dist < // PlayerChunkMap::MAX_VIEW_DISTANCE) // { // this->viewDistance = dist; // } // // chatVisibility = packet->getChatVisibility(); // canChatColor = packet->getChatColors(); // // // 4J - Don't need // //if (server.isSingleplayer() && // server.getSingleplayerName().equals(name)) // //{ // // server.setDifficulty(packet.getDifficulty()); // //} //} int ServerPlayer::getViewDistance() { return viewDistance; } // bool ServerPlayer::canChatInColor() //{ // return canChatColor; // } // // int ServerPlayer::getChatVisibility() //{ // return chatVisibility; // } Pos* ServerPlayer::getCommandSenderWorldPosition() { return new Pos(Mth::floor(x), Mth::floor(y + .5), Mth::floor(z)); } void ServerPlayer::resetLastActionTime() { this->lastActionTime = MinecraftServer::getCurrentTimeMillis(); } // Get an index that can be used to uniquely reference this chunk from either // dimension int ServerPlayer::getFlagIndexForChunk(const ChunkPos& pos, int dimension) { // Scale pos x & z up by 16 as getGlobalIndexForChunk is expecting tile // rather than chunk coords return LevelRenderer::getGlobalIndexForChunk(pos.x * 16, 0, pos.z * 16, dimension) / (Level::maxBuildHeight / 16); // dividing here by number of renderer chunks in one column; } // 4J Added, returns a number which is subtracted from the default view distance int ServerPlayer::getPlayerViewDistanceModifier() { int value = 0; if (!connection->isLocal()) { INetworkPlayer* player = connection->getNetworkPlayer(); if (player != NULL) { int rtt = player->GetCurrentRtt(); value = rtt >> 6; if (value > 4) value = 4; } } return value; } void ServerPlayer::handleCollectItem(std::shared_ptr item) { if (gameMode->getGameRules() != NULL) gameMode->getGameRules()->onCollectItem(item); } #ifndef _CONTENT_PACKAGE void ServerPlayer::debug_setPosition(double x, double y, double z, double nYRot, double nXRot) { connection->teleport(x, y, z, nYRot, nXRot); } #endif