the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1682 lines 56 kB view raw
1#include "stdafx.h" 2#include "ServerPlayer.h" 3#include "ServerPlayerGameMode.h" 4#include "ServerLevel.h" 5#include "MinecraftServer.h" 6#include "EntityTracker.h" 7#include "PlayerConnection.h" 8#include "Settings.h" 9#include "PlayerList.h" 10#include "MultiPlayerLevel.h" 11 12#include "..\Minecraft.World\net.minecraft.network.packet.h" 13#include "..\Minecraft.World\net.minecraft.world.damagesource.h" 14#include "..\Minecraft.World\net.minecraft.world.inventory.h" 15#include "..\Minecraft.World\net.minecraft.world.level.h" 16#include "..\Minecraft.World\net.minecraft.world.level.storage.h" 17#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" 18#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" 19#include "..\Minecraft.World\net.minecraft.world.entity.h" 20#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" 21#include "..\Minecraft.World\net.minecraft.world.item.h" 22#include "..\Minecraft.World\net.minecraft.world.item.trading.h" 23#include "..\Minecraft.World\net.minecraft.world.entity.item.h" 24#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" 25#include "..\Minecraft.World\net.minecraft.world.scores.h" 26#include "..\Minecraft.World\net.minecraft.world.scores.criteria.h" 27#include "..\Minecraft.World\net.minecraft.stats.h" 28#include "..\Minecraft.World\net.minecraft.locale.h" 29 30#include "..\Minecraft.World\Pos.h" 31#include "..\Minecraft.World\Random.h" 32 33#include "..\Minecraft.World\LevelChunk.h" 34#include "LevelRenderer.h" 35 36 37ServerPlayer::ServerPlayer(MinecraftServer *server, Level *level, const wstring& name, ServerPlayerGameMode *gameMode) : Player(level, name) 38{ 39 // 4J - added initialisers 40 connection = nullptr; 41 lastMoveX = lastMoveZ = 0; 42 spewTimer = 0; 43 lastRecordedHealthAndAbsorption = FLT_MIN; 44 lastSentHealth = -99999999; 45 lastSentFood = -99999999; 46 lastFoodSaturationZero = true; 47 lastSentExp = -99999999; 48 invulnerableTime = 20 * 3; 49 containerCounter = 0; 50 ignoreSlotUpdateHack = false; 51 latency = 0; 52 wonGame = false; 53 m_enteredEndExitPortal = false; 54 // lastCarried = ItemInstanceArray(5); 55 lastActionTime = 0; 56 57 viewDistance = server->getPlayers()->getViewDistance(); 58 59 // gameMode->player = this; // 4J - removed to avoid use of shared_from_this in ctor, now set up externally 60 this->gameMode = gameMode; 61 62 Pos *spawnPos = level->getSharedSpawnPos(); 63 int xx = spawnPos->x; 64 int zz = spawnPos->z; 65 int yy = spawnPos->y; 66 delete spawnPos; 67 68 if (!level->dimension->hasCeiling && level->getLevelData()->getGameType() != GameType::ADVENTURE) 69 { 70 level->isFindingSpawn = true; 71 72 int radius = max(5, server->getSpawnProtectionRadius() - 6); 73 74 // 4J added - do additional checking that we aren't putting the player in deep water. Give up after 20 or goes just 75 // in case the spawnPos is somehow in a really bad spot and we would just lock here. 76 int waterDepth = 0; 77 int attemptCount = 0; 78 int xx2, yy2, zz2; 79 80 int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; 81 int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; 82 83 bool playerNear = false; 84 do 85 { 86 // Also check that we aren't straying outside of the map 87 do 88 { 89 xx2 = xx + random->nextInt(radius * 2) - radius; 90 zz2 = zz + random->nextInt(radius * 2) - radius; 91 } while ( ( xx2 > maxXZ ) || ( xx2 < minXZ ) || ( zz2 > maxXZ ) || ( zz2 < minXZ ) ); 92 yy2 = level->getTopSolidBlock(xx2, zz2); 93 94 waterDepth = 0; 95 int yw = yy2; 96 while( ( yw < 128 ) && 97 (( level->getTile(xx2,yw,zz2) == Tile::water_Id ) || 98 ( level->getTile(xx2,yw,zz2) == Tile::calmWater_Id )) ) 99 { 100 yw++; 101 waterDepth++; 102 } 103 attemptCount++; 104 playerNear = ( level->getNearestPlayer(xx + 0.5, yy, zz + 0.5,3) != NULL ); 105 } while ( ( waterDepth > 1 ) && (!playerNear) && ( attemptCount < 20 ) ); 106 xx = xx2; 107 yy = yy2; 108 zz = zz2; 109 110 level->isFindingSpawn = false; 111 } 112 113 this->server = server; 114 footSize = 0; 115 116 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 117 this->moveTo(xx + 0.5, yy, zz + 0.5, 0, 0); 118 119 // 4J Handled later 120 //while (!level->getCubes(this, bb).empty()) 121 //{ 122 // setPos(x, y + 1, z); 123 //} 124 125 // m_UUID = name; 126 127 // 4J Added 128 lastBrupSendTickCount = 0; 129} 130 131ServerPlayer::~ServerPlayer() 132{ 133 // delete [] lastCarried.data; 134} 135 136// 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. 137// 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 138// 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 139// flag array and adds to it for this ServerPlayer. 140void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFound) 141{ 142 if( entitiesToRemove.empty() ) 143 { 144 return; 145 } 146 if( ( *removedFound ) == false ) 147 { 148 *removedFound = true; 149 memset(flags, 0, 2048/32); 150 } 151 152 AUTO_VAR(it, entitiesToRemove.begin() ); 153 for( AUTO_VAR(it, entitiesToRemove.begin()); it != entitiesToRemove.end(); it++ ) 154 { 155 int index = *it; 156 if( index < 2048 ) 157 { 158 unsigned int i = index / 32; 159 unsigned int j = index % 32; 160 unsigned int uiMask = 0x80000000 >> j; 161 162 flags[i] |= uiMask; 163 } 164 } 165} 166 167void ServerPlayer::readAdditionalSaveData(CompoundTag *entityTag) 168{ 169 Player::readAdditionalSaveData(entityTag); 170 171 if (entityTag->contains(L"playerGameType")) 172 { 173 // 4J Stu - We do not want to change the game mode for the player, instead we let the server override it globally 174 //if (MinecraftServer::getInstance()->getForceGameType()) 175 //{ 176 // gameMode->setGameModeForPlayer(MinecraftServer::getInstance()->getDefaultGameType()); 177 //} 178 //else 179 //{ 180 // gameMode->setGameModeForPlayer(GameType::byId(entityTag->getInt(L"playerGameType"))); 181 //} 182 } 183 184 GameRulesInstance *grs = gameMode->getGameRules(); 185 if (entityTag->contains(L"GameRules") && grs != NULL) 186 { 187 byteArray ba = entityTag->getByteArray(L"GameRules"); 188 ByteArrayInputStream bais(ba); 189 DataInputStream dis(&bais); 190 grs->read(&dis); 191 dis.close(); 192 bais.close(); 193 //delete [] ba.data; 194 } 195} 196 197void ServerPlayer::addAdditonalSaveData(CompoundTag *entityTag) 198{ 199 Player::addAdditonalSaveData(entityTag); 200 201 GameRulesInstance *grs = gameMode->getGameRules(); 202 if (grs != NULL) 203 { 204 ByteArrayOutputStream baos; 205 DataOutputStream dos(&baos); 206 grs->write(&dos); 207 entityTag->putByteArray(L"GameRules", baos.buf); 208 baos.buf.data = NULL; 209 dos.close(); 210 baos.close(); 211 } 212 213 // 4J Stu - We do not want to change the game mode for the player, instead we let the server override it globally 214 //entityTag->putInt(L"playerGameType", gameMode->getGameModeForPlayer()->getId()); 215} 216 217void ServerPlayer::giveExperienceLevels(int amount) 218{ 219 Player::giveExperienceLevels(amount); 220 lastSentExp = -1; 221} 222 223void ServerPlayer::initMenu() 224{ 225 containerMenu->addSlotListener(this); 226} 227 228void ServerPlayer::setDefaultHeadHeight() 229{ 230 heightOffset = 0; 231} 232 233float ServerPlayer::getHeadHeight() 234{ 235 return 1.62f; 236} 237 238void ServerPlayer::tick() 239{ 240 gameMode->tick(); 241 242 if (invulnerableTime > 0) invulnerableTime--; 243 containerMenu->broadcastChanges(); 244 245 // 4J-JEV, hook for Durango event 'EnteredNewBiome'. 246 Biome *newBiome = level->getBiome(x,z); 247 if (newBiome != currentBiome) 248 { 249 awardStat( 250 GenericStats::enteredBiome(newBiome->id), 251 GenericStats::param_enteredBiome(newBiome->id) 252 ); 253 currentBiome = newBiome; 254 } 255 256 if (!level->isClientSide) 257 { 258 if (!containerMenu->stillValid(dynamic_pointer_cast<Player>(shared_from_this()))) 259 { 260 closeContainer(); 261 containerMenu = inventoryMenu; 262 } 263 } 264 265 flushEntitiesToRemove(); 266} 267 268// 4J Stu - Split out here so that we can call this from other places 269void ServerPlayer::flushEntitiesToRemove() 270{ 271 while (!entitiesToRemove.empty()) 272 { 273 int sz = entitiesToRemove.size(); 274 int amount = min(sz, RemoveEntitiesPacket::MAX_PER_PACKET); 275 intArray ids(amount); 276 int pos = 0; 277 278 AUTO_VAR(it, entitiesToRemove.begin() ); 279 while (it != entitiesToRemove.end() && pos < amount) 280 { 281 ids[pos++] = *it; 282 it = entitiesToRemove.erase(it); 283 } 284 285 connection->send(shared_ptr<RemoveEntitiesPacket>(new RemoveEntitiesPacket(ids))); 286 } 287} 288 289// 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 290void ServerPlayer::doTick(bool sendChunks, bool dontDelayChunks/*=false*/, bool ignorePortal/*=false*/) 291{ 292 m_ignorePortal = ignorePortal; 293 if( sendChunks ) 294 { 295 updateFrameTick(); 296 } 297 doTickA(); 298 if( sendChunks ) 299 { 300 doChunkSendingTick(dontDelayChunks); 301 } 302 doTickB(); 303 m_ignorePortal = false; 304} 305 306void ServerPlayer::doTickA() 307{ 308 Player::tick(); 309 310 for (unsigned int i = 0; i < inventory->getContainerSize(); i++) 311 { 312 shared_ptr<ItemInstance> ie = inventory->getItem(i); 313 if (ie != NULL) 314 { 315 // 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, 316 // 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. 317 if (Item::items[ie->id]->isComplex() ) // && connection->countDelayedPackets() <= 2) 318 { 319 shared_ptr<Packet> packet = (dynamic_cast<ComplexItem *>(Item::items[ie->id])->getUpdatePacket(ie, level, dynamic_pointer_cast<Player>( shared_from_this() ) ) ); 320 if (packet != NULL) 321 { 322 connection->send(packet); 323 } 324 } 325 } 326 } 327} 328 329// 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 330void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) 331{ 332 // printf("[%d] %s: sendChunks: %d, empty: %d\n",tickCount, connection->getNetworkPlayer()->GetUID().getOnlineID(),sendChunks,chunksToSend.empty()); 333 if (!chunksToSend.empty()) 334 { 335 ChunkPos nearest = chunksToSend.front(); 336 bool nearestValid = false; 337 338 // 4J - reinstated and optimised some code that was commented out in the original, to make sure that we always 339 // send the nearest chunk to the player. The original uses the bukkit sorting thing to try and avoid doing this, but 340 // the player can quickly wander away from the centre of the spiral of chunks that that method creates, long before transmission 341 // of them is complete. 342 double dist = DBL_MAX; 343 for( AUTO_VAR(it, chunksToSend.begin()); it != chunksToSend.end(); it++ ) 344 { 345 ChunkPos chunk = *it; 346 if( level->isChunkFinalised(chunk.x, chunk.z) ) 347 { 348 double newDist = chunk.distanceToSqr(x, z); 349 if ( (!nearestValid) || (newDist < dist) ) 350 { 351 nearest = chunk; 352 dist = chunk.distanceToSqr(x, z); 353 nearestValid = true; 354 } 355 } 356 } 357 358 // if (nearest != NULL) // 4J - removed as we don't have references here 359 if( nearestValid ) 360 { 361 bool okToSend = false; 362 363 // if (dist < 32 * 32) okToSend = true; 364 if( connection->isLocal() ) 365 { 366 if( !connection->done ) okToSend = true; 367 } 368 else 369 { 370 bool canSendToPlayer = MinecraftServer::chunkPacketManagement_CanSendTo(connection->getNetworkPlayer()); 371 372// app.DebugPrintf(">>> %d\n", canSendToPlayer); 373// if( connection->getNetworkPlayer() ) 374// { 375// app.DebugPrintf("%d: canSendToPlayer %d, countDelayedPackets %d GetSendQueueSizeBytes %d done: %d\n", 376// connection->getNetworkPlayer()->GetSmallId(), 377// canSendToPlayer, connection->countDelayedPackets(), 378// g_NetworkManager.GetHostPlayer()->GetSendQueueSizeMessages( NULL, true ), 379// connection->done); 380// } 381 382 if( dontDelayChunks || 383 (canSendToPlayer && 384#ifdef _XBOX_ONE 385 // The network manager on xbox one doesn't currently split data into slow & fast queues - since we can only measure 386 // both together then bytes provides a better metric than count of data items to determine if we should avoid queueing too much up 387 (g_NetworkManager.GetHostPlayer()->GetSendQueueSizeBytes( NULL, true ) < 8192 )&& 388#elif defined _XBOX 389 (g_NetworkManager.GetHostPlayer()->GetSendQueueSizeMessages( NULL, true ) < 4 )&& 390#else 391 (connection->countDelayedPackets() < 4 )&& 392 (g_NetworkManager.GetHostPlayer()->GetSendQueueSizeMessages( NULL, true ) < 4 )&& 393#endif 394 //(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) && 395 !connection->done) ) 396 { 397 lastBrupSendTickCount = tickCount; 398 okToSend = true; 399 MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer()); 400 401// static unordered_map<wstring,__int64> mapLastTime; 402// __int64 thisTime = System::currentTimeMillis(); 403// __int64 lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()]; 404// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime); 405// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime; 406 } 407 else 408 { 409 // app.DebugPrintf(" - <NOT OK>\n"); 410 } 411 } 412 413 if (okToSend) 414 { 415 ServerLevel *level = server->getLevel(dimension); 416 int flagIndex = getFlagIndexForChunk(nearest,this->level->dimension->id); 417 chunksToSend.remove(nearest); 418 419 bool chunkDataSent = false; 420 421 // 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 422 // to the chunksToSend list, but that would stop the tile entities being broadcast too 423 if( !connection->isLocal() ) // force here to disable sharing of data 424 { 425 // 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 426 // (1) Sending a chunk to multiple players doing split screen on one machine 427 // (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 428 // 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 429 // unloaded on the client and so just gradually build up more and more of the finite set of chunks as the player moves 430 if( !g_NetworkManager.SystemFlagGet(connection->getNetworkPlayer(),flagIndex) ) 431 { 432 // app.DebugPrintf("Creating BRUP for %d %d\n",nearest.x, nearest.z); 433 PIXBeginNamedEvent(0,"Creation BRUP for sending\n"); 434 __int64 before = System::currentTimeMillis(); 435 shared_ptr<BlockRegionUpdatePacket> packet = shared_ptr<BlockRegionUpdatePacket>( new BlockRegionUpdatePacket(nearest.x * 16, 0, nearest.z * 16, 16, Level::maxBuildHeight, 16, level) ); 436 __int64 after = System::currentTimeMillis(); 437// app.DebugPrintf(">>><<< %d ms\n",after-before); 438 PIXEndNamedEvent(); 439 if( dontDelayChunks ) packet->shouldDelay = false; 440 441 if( packet->shouldDelay == true ) 442 { 443 // Other than the first packet we always want these initial chunks to be sent over 444 // QNet at a lower priority 445 connection->queueSend( packet ); 446 } 447 else 448 { 449 connection->send( packet ); 450 } 451 // Set flag to say we have send this block already to this system 452 g_NetworkManager.SystemFlagSet(connection->getNetworkPlayer(),flagIndex); 453 454 chunkDataSent = true; 455 } 456 } 457 else 458 { 459 // 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, 460 // 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 461 // network connection at this point. 462 463 MultiPlayerLevel *clientLevel = Minecraft::GetInstance()->getLevel(level->dimension->id); 464 if( clientLevel ) 465 { 466 LevelChunk *lc = clientLevel->getChunk( nearest.x, nearest.z ); 467 lc->reSyncLighting(); 468 lc->recalcHeightmapOnly(); 469 clientLevel->setTilesDirty(nearest.x * 16 + 1, 1, nearest.z * 16 + 1, 470 nearest.x * 16 + 14, Level::maxBuildHeight - 2, nearest.z * 16 + 14 ); 471 } 472 } 473 // Don't send TileEntity data until we have sent the block data 474 if( connection->isLocal() || chunkDataSent) 475 { 476 vector<shared_ptr<TileEntity> > *tes = level->getTileEntitiesInRegion(nearest.x * 16, 0, nearest.z * 16, nearest.x * 16 + 16, Level::maxBuildHeight, nearest.z * 16 + 16); 477 for (unsigned int i = 0; i < tes->size(); i++) 478 { 479 // 4J Stu - Added delay param to ensure that these arrive after the BRUPs from above 480 // Fix for #9169 - ART : Sign text is replaced with the words �Awaiting approval�. 481 broadcast(tes->at(i), !connection->isLocal() && !dontDelayChunks); 482 } 483 delete tes; 484 } 485 } 486 } 487 } 488} 489 490void ServerPlayer::doTickB() 491{ 492#ifndef _CONTENT_PACKAGE 493 // check if there's a debug dimension change requested 494 //if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_GoToNether)) 495 //{ 496 // if(level->dimension->id == 0 ) 497 // { 498 // isInsidePortal=true; 499 // portalTime=1; 500 // } 501 // unsigned int uiVal=app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); 502 // app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),uiVal&~(1L<<eDebugSetting_GoToNether)); 503 //} 504 // else if (app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_GoToEnd)) 505 // { 506 // if(level->dimension->id == 0 ) 507 // { 508 // server->players->toggleDimension( dynamic_pointer_cast<ServerPlayer>( shared_from_this() ), 1 ); 509 // } 510 // unsigned int uiVal=app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); 511 // app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),uiVal&~(1L<<eDebugSetting_GoToEnd)); 512 // } 513 //else 514 if (app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_GoToOverworld)) 515 { 516 if(level->dimension->id != 0 ) 517 { 518 isInsidePortal=true; 519 portalTime=1; 520 } 521 unsigned int uiVal=app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()); 522 app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),uiVal&~(1L<<eDebugSetting_GoToOverworld)); 523 } 524#endif 525 526 if (getHealth() != lastSentHealth || lastSentFood != foodData.getFoodLevel() || ((foodData.getSaturationLevel() == 0) != lastFoodSaturationZero)) 527 { 528 // 4J Stu - Added m_lastDamageSource for telemetry 529 connection->send( shared_ptr<SetHealthPacket>( new SetHealthPacket(getHealth(), foodData.getFoodLevel(), foodData.getSaturationLevel(), m_lastDamageSource) ) ); 530 lastSentHealth = getHealth(); 531 lastSentFood = foodData.getFoodLevel(); 532 lastFoodSaturationZero = foodData.getSaturationLevel() == 0; 533 } 534 535 if (getHealth() + getAbsorptionAmount() != lastRecordedHealthAndAbsorption) 536 { 537 lastRecordedHealthAndAbsorption = getHealth() + getAbsorptionAmount(); 538 539 vector<Objective *> *objectives = getScoreboard()->findObjectiveFor(ObjectiveCriteria::HEALTH); 540 if(objectives) 541 { 542 vector< shared_ptr<Player> > players = vector< shared_ptr<Player> >(); 543 players.push_back(dynamic_pointer_cast<Player>(shared_from_this())); 544 545 for (AUTO_VAR(it,objectives->begin()); it != objectives->end(); ++it) 546 { 547 Objective *objective = *it; 548 getScoreboard()->getPlayerScore(getAName(), objective)->updateFor(&players); 549 } 550 delete objectives; 551 } 552 } 553 554 if (totalExperience != lastSentExp) 555 { 556 lastSentExp = totalExperience; 557 connection->send( shared_ptr<SetExperiencePacket>( new SetExperiencePacket(experienceProgress, totalExperience, experienceLevel) ) ); 558 } 559 560} 561 562shared_ptr<ItemInstance> ServerPlayer::getCarried(int slot) 563{ 564 if (slot == 0) return inventory->getSelected(); 565 return inventory->armor[slot - 1]; 566} 567 568void ServerPlayer::die(DamageSource *source) 569{ 570 server->getPlayers()->broadcastAll(getCombatTracker()->getDeathMessagePacket()); 571 572 if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) 573 { 574 inventory->dropAll(); 575 } 576 577 vector<Objective *> *objectives = level->getScoreboard()->findObjectiveFor(ObjectiveCriteria::DEATH_COUNT); 578 if(objectives) 579 { 580 for (int i = 0; i < objectives->size(); i++) 581 { 582 Objective *objective = objectives->at(i); 583 584 Score *score = getScoreboard()->getPlayerScore(getAName(), objective); 585 score->increment(); 586 } 587 delete objectives; 588 } 589 590 shared_ptr<LivingEntity> killer = getKillCredit(); 591 if (killer != NULL) killer->awardKillScore(shared_from_this(), deathScore); 592 //awardStat(Stats::deaths, 1); 593} 594 595bool ServerPlayer::hurt(DamageSource *dmgSource, float dmg) 596{ 597 if (isInvulnerable()) return false; 598 599 // 4J: Not relevant to console servers 600 // Allow falldamage on dedicated pvpservers -- so people cannot cheat their way out of 'fall traps' 601 //bool allowFallDamage = server->isPvpAllowed() && server->isDedicatedServer() && server->isPvpAllowed() && (dmgSource->msgId.compare(L"fall") == 0); 602 if (!server->isPvpAllowed() && invulnerableTime > 0 && dmgSource != DamageSource::outOfWorld) return false; 603 604 if (dynamic_cast<EntityDamageSource *>(dmgSource) != NULL) 605 { 606 // 4J Stu - Fix for #46422 - TU5: Crash: Gameplay: Crash when being hit by a trap using a dispenser 607 // getEntity returns the owner of projectiles, and this would never be the arrow. The owner is sometimes NULL. 608 shared_ptr<Entity> source = dmgSource->getDirectEntity(); 609 610 if (source->instanceof(eTYPE_PLAYER) && !dynamic_pointer_cast<Player>(source)->canHarmPlayer(dynamic_pointer_cast<Player>(shared_from_this()))) 611 { 612 return false; 613 } 614 615 if ( (source != NULL) && source->instanceof(eTYPE_ARROW) ) 616 { 617 shared_ptr<Arrow> arrow = dynamic_pointer_cast<Arrow>(source); 618 if ( (arrow->owner != NULL) && arrow->owner->instanceof(eTYPE_PLAYER) && !canHarmPlayer(dynamic_pointer_cast<Player>(arrow->owner)) ) 619 { 620 return false; 621 } 622 } 623 } 624 bool returnVal = Player::hurt(dmgSource, dmg); 625 626 if( returnVal ) 627 { 628 // 4J Stu - Work out the source of this damage for telemetry 629 m_lastDamageSource = eTelemetryChallenges_Unknown; 630 631 if(dmgSource == DamageSource::fall) m_lastDamageSource = eTelemetryPlayerDeathSource_Fall; 632 else if(dmgSource == DamageSource::onFire || dmgSource == DamageSource::inFire) m_lastDamageSource = eTelemetryPlayerDeathSource_Fire; 633 else if(dmgSource == DamageSource::lava) m_lastDamageSource = eTelemetryPlayerDeathSource_Lava; 634 else if(dmgSource == DamageSource::drown) m_lastDamageSource = eTelemetryPlayerDeathSource_Water; 635 else if(dmgSource == DamageSource::inWall) m_lastDamageSource = eTelemetryPlayerDeathSource_Suffocate; 636 else if(dmgSource == DamageSource::outOfWorld) m_lastDamageSource = eTelemetryPlayerDeathSource_OutOfWorld; 637 else if(dmgSource == DamageSource::cactus) m_lastDamageSource = eTelemetryPlayerDeathSource_Cactus; 638 else 639 { 640 shared_ptr<Entity> source = dmgSource->getEntity(); 641 if( source != NULL ) 642 { 643 switch(source->GetType()) 644 { 645 case eTYPE_PLAYER: 646 case eTYPE_SERVERPLAYER: 647 m_lastDamageSource = eTelemetryPlayerDeathSource_Player_Weapon; 648 break; 649 case eTYPE_WOLF: 650 m_lastDamageSource = eTelemetryPlayerDeathSource_Wolf; 651 break; 652 case eTYPE_CREEPER: 653 m_lastDamageSource = eTelemetryPlayerDeathSource_Explosion_Creeper; 654 break; 655 case eTYPE_SKELETON: 656 m_lastDamageSource = eTelemetryPlayerDeathSource_Skeleton; 657 break; 658 case eTYPE_SPIDER: 659 m_lastDamageSource = eTelemetryPlayerDeathSource_Spider; 660 break; 661 case eTYPE_ZOMBIE: 662 m_lastDamageSource = eTelemetryPlayerDeathSource_Zombie; 663 break; 664 case eTYPE_PIGZOMBIE: 665 m_lastDamageSource = eTelemetryPlayerDeathSource_ZombiePigman; 666 break; 667 case eTYPE_GHAST: 668 m_lastDamageSource = eTelemetryPlayerDeathSource_Ghast; 669 break; 670 case eTYPE_SLIME: 671 m_lastDamageSource = eTelemetryPlayerDeathSource_Slime; 672 break; 673 case eTYPE_PRIMEDTNT: 674 m_lastDamageSource = eTelemetryPlayerDeathSource_Explosion_Tnt; 675 break; 676 case eTYPE_ARROW: 677 if ((dynamic_pointer_cast<Arrow>(source))->owner != NULL) 678 { 679 shared_ptr<Entity> attacker = (dynamic_pointer_cast<Arrow>(source))->owner; 680 if (attacker != NULL) 681 { 682 switch(attacker->GetType()) 683 { 684 case eTYPE_SKELETON: 685 m_lastDamageSource = eTelemetryPlayerDeathSource_Skeleton; 686 break; 687 case eTYPE_PLAYER: 688 case eTYPE_SERVERPLAYER: 689 m_lastDamageSource = eTelemetryPlayerDeathSource_Player_Arrow; 690 break; 691 } 692 } 693 } 694 break; 695 case eTYPE_FIREBALL: 696 m_lastDamageSource = eTelemetryPlayerDeathSource_Ghast; 697 break; 698 }; 699 } 700 }; 701 } 702 703 return returnVal; 704} 705 706bool ServerPlayer::canHarmPlayer(shared_ptr<Player> target) 707{ 708 if (!server->isPvpAllowed()) return false; 709 if(!isAllowedToAttackPlayers()) return false; 710 return Player::canHarmPlayer(target); 711} 712 713// 4J: Added for checking when only player name is provided (possible player isn't on server), e.g. can harm owned animals 714bool ServerPlayer::canHarmPlayer(wstring targetName) 715{ 716 bool canHarm = true; 717 718 shared_ptr<ServerPlayer> owner = server->getPlayers()->getPlayer(targetName); 719 if (owner != NULL) 720 { 721 if ((shared_from_this() != owner) && canHarmPlayer(owner)) canHarm = false; 722 } 723 else 724 { 725 if (this->name != targetName && (!isAllowedToAttackPlayers() || !server->isPvpAllowed())) canHarm = false; 726 } 727 728 return canHarm; 729} 730 731void ServerPlayer::changeDimension(int i) 732{ 733 if(!connection->hasClientTickedOnce()) return; 734 735 if (dimension == 1 && i == 1) 736 { 737 app.DebugPrintf("Start win game\n"); 738 awardStat(GenericStats::winGame(), GenericStats::param_winGame()); 739 740 // All players on the same system as this player should also be removed from the game while the Win screen is shown 741 INetworkPlayer *thisPlayer = connection->getNetworkPlayer(); 742 743 if(!wonGame) 744 { 745 level->removeEntity(shared_from_this()); 746 wonGame = true; 747 m_enteredEndExitPortal = true; // We only flag this for the player in the portal 748 connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::WIN_GAME, thisPlayer->GetUserIndex()) ) ); 749 app.DebugPrintf("Sending packet to %d\n", thisPlayer->GetUserIndex()); 750 } 751 if(thisPlayer != NULL) 752 { 753 for(AUTO_VAR(it, MinecraftServer::getInstance()->getPlayers()->players.begin()); it != MinecraftServer::getInstance()->getPlayers()->players.end(); ++it) 754 { 755 shared_ptr<ServerPlayer> servPlayer = *it; 756 INetworkPlayer *checkPlayer = servPlayer->connection->getNetworkPlayer(); 757 if(thisPlayer != checkPlayer && checkPlayer != NULL && thisPlayer->IsSameSystem( checkPlayer ) && !servPlayer->wonGame ) 758 { 759 servPlayer->wonGame = true; 760 servPlayer->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::WIN_GAME, thisPlayer->GetUserIndex() ) ) ); 761 app.DebugPrintf("Sending packet to %d\n", thisPlayer->GetUserIndex()); 762 } 763 } 764 } 765 app.DebugPrintf("End win game\n"); 766 } 767 else 768 { 769 if (dimension == 0 && i == 1) 770 { 771 awardStat(GenericStats::theEnd(), GenericStats::param_theEnd()); 772 773 Pos *pos = server->getLevel(i)->getDimensionSpecificSpawn(); 774 if (pos != NULL) 775 { 776 connection->teleport(pos->x, pos->y, pos->z, 0, 0); 777 delete pos; 778 } 779 780 i = 1; 781 } 782 else 783 { 784 // 4J: Removed on the advice of the mighty King of Achievments (JV) 785 // awardStat(GenericStats::portal(), GenericStats::param_portal()); 786 } 787 server->getPlayers()->toggleDimension( dynamic_pointer_cast<ServerPlayer>(shared_from_this()), i); 788 lastSentExp = -1; 789 lastSentHealth = -1; 790 lastSentFood = -1; 791 } 792} 793 794// 4J Added delay param 795void ServerPlayer::broadcast(shared_ptr<TileEntity> te, bool delay /*= false*/) 796{ 797 if (te != NULL) 798 { 799 shared_ptr<Packet> p = te->getUpdatePacket(); 800 if (p != NULL) 801 { 802 p->shouldDelay = delay; 803 if(delay) connection->queueSend(p); 804 else connection->send(p); 805 } 806 } 807} 808 809void ServerPlayer::take(shared_ptr<Entity> e, int orgCount) 810{ 811 Player::take(e, orgCount); 812 containerMenu->broadcastChanges(); 813} 814 815Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, bool bTestUse) 816{ 817 BedSleepingResult result = Player::startSleepInBed(x, y, z, bTestUse); 818 if (result == OK) 819 { 820 shared_ptr<Packet> p = shared_ptr<EntityActionAtPositionPacket>( new EntityActionAtPositionPacket(shared_from_this(), EntityActionAtPositionPacket::START_SLEEP, x, y, z) ); 821 getLevel()->getTracker()->broadcast(shared_from_this(), p); 822 connection->teleport(this->x, this->y, this->z, yRot, xRot); 823 connection->send(p); 824 } 825 return result; 826} 827 828void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint) 829{ 830 if (isSleeping()) 831 { 832 getLevel()->getTracker()->broadcastAndSend(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(shared_from_this(), AnimatePacket::WAKE_UP) ) ); 833 } 834 Player::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint); 835 if (connection != NULL) connection->teleport(x, y, z, yRot, xRot); 836} 837 838void ServerPlayer::ride(shared_ptr<Entity> e) 839{ 840 Player::ride(e); 841 connection->send( shared_ptr<SetEntityLinkPacket>( new SetEntityLinkPacket(SetEntityLinkPacket::RIDING, shared_from_this(), riding) ) ); 842 843 // 4J Removed this - The act of riding will be handled on the client and will change the position 844 // of the player. If we also teleport it then we can end up with a repeating movements, e.g. bouncing 845 // up and down after exiting a boat due to slight differences in position on the client and server 846 //connection->teleport(x, y, z, yRot, xRot); 847} 848 849void ServerPlayer::checkFallDamage(double ya, bool onGround) 850{ 851} 852 853void ServerPlayer::doCheckFallDamage(double ya, bool onGround) 854{ 855 Player::checkFallDamage(ya, onGround); 856} 857 858void ServerPlayer::openTextEdit(shared_ptr<TileEntity> sign) 859{ 860 shared_ptr<SignTileEntity> signTE = dynamic_pointer_cast<SignTileEntity>(sign); 861 if (signTE != NULL) 862 { 863 signTE->setAllowedPlayerEditor(dynamic_pointer_cast<Player>(shared_from_this())); 864 connection->send( shared_ptr<TileEditorOpenPacket>( new TileEditorOpenPacket(TileEditorOpenPacket::SIGN, sign->x, sign->y, sign->z)) ); 865 } 866} 867 868void ServerPlayer::nextContainerCounter() 869{ 870 containerCounter = (containerCounter % 100) + 1; 871} 872 873bool ServerPlayer::startCrafting(int x, int y, int z) 874{ 875 if(containerMenu == inventoryMenu) 876 { 877 nextContainerCounter(); 878 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::WORKBENCH, L"", 9, false) ) ); 879 containerMenu = new CraftingMenu(inventory, level, x, y, z); 880 containerMenu->containerId = containerCounter; 881 containerMenu->addSlotListener(this); 882 } 883 else 884 { 885 app.DebugPrintf("ServerPlayer tried to open crafting container when one was already open\n"); 886 } 887 888 return true; 889} 890 891bool ServerPlayer::openFireworks(int x, int y, int z) 892{ 893 if(containerMenu == inventoryMenu) 894 { 895 nextContainerCounter(); 896 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false) ) ); 897 containerMenu = new FireworksMenu(inventory, level, x, y, z); 898 containerMenu->containerId = containerCounter; 899 containerMenu->addSlotListener(this); 900 } 901 else if(dynamic_cast<CraftingMenu *>(containerMenu) != NULL) 902 { 903 closeContainer(); 904 905 nextContainerCounter(); 906 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::FIREWORKS, L"", 9, false) ) ); 907 containerMenu = new FireworksMenu(inventory, level, x, y, z); 908 containerMenu->containerId = containerCounter; 909 containerMenu->addSlotListener(this); 910 } 911 else 912 { 913 app.DebugPrintf("ServerPlayer tried to open crafting container when one was already open\n"); 914 } 915 916 return true; 917} 918 919bool ServerPlayer::startEnchanting(int x, int y, int z, const wstring &name) 920{ 921 if(containerMenu == inventoryMenu) 922 { 923 nextContainerCounter(); 924 connection->send(shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::ENCHANTMENT, name.empty()? L"" : name, 9, !name.empty() ) )); 925 containerMenu = new EnchantmentMenu(inventory, level, x, y, z); 926 containerMenu->containerId = containerCounter; 927 containerMenu->addSlotListener(this); 928 } 929 else 930 { 931 app.DebugPrintf("ServerPlayer tried to open enchanting container when one was already open\n"); 932 } 933 934 return true; 935} 936 937bool ServerPlayer::startRepairing(int x, int y, int z) 938{ 939 if(containerMenu == inventoryMenu) 940 { 941 nextContainerCounter(); 942 connection->send(shared_ptr<ContainerOpenPacket> ( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::REPAIR_TABLE, L"", 9, false)) ); 943 containerMenu = new AnvilMenu(inventory, level, x, y, z, dynamic_pointer_cast<Player>(shared_from_this())); 944 containerMenu->containerId = containerCounter; 945 containerMenu->addSlotListener(this); 946 } 947 else 948 { 949 app.DebugPrintf("ServerPlayer tried to open enchanting container when one was already open\n"); 950 } 951 952 return true; 953} 954 955bool ServerPlayer::openContainer(shared_ptr<Container> container) 956{ 957 if(containerMenu == inventoryMenu) 958 { 959 nextContainerCounter(); 960 961 // 4J-JEV: Added to distinguish between ender, bonus, large and small chests (for displaying the name of the chest). 962 int containerType = container->getContainerType(); 963 assert(containerType >= 0); 964 965 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, containerType, container->getCustomName(), container->getContainerSize(), container->hasCustomName()) ) ); 966 967 containerMenu = new ContainerMenu(inventory, container); 968 containerMenu->containerId = containerCounter; 969 containerMenu->addSlotListener(this); 970 } 971 else 972 { 973 app.DebugPrintf("ServerPlayer tried to open container when one was already open\n"); 974 } 975 976 return true; 977} 978 979bool ServerPlayer::openHopper(shared_ptr<HopperTileEntity> container) 980{ 981 if(containerMenu == inventoryMenu) 982 { 983 nextContainerCounter(); 984 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName())) ); 985 containerMenu = new HopperMenu(inventory, container); 986 containerMenu->containerId = containerCounter; 987 containerMenu->addSlotListener(this); 988 } 989 else 990 { 991 app.DebugPrintf("ServerPlayer tried to open hopper container when one was already open\n"); 992 } 993 994 return true; 995} 996 997bool ServerPlayer::openHopper(shared_ptr<MinecartHopper> container) 998{ 999 if(containerMenu == inventoryMenu) 1000 { 1001 nextContainerCounter(); 1002 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::HOPPER, container->getCustomName(), container->getContainerSize(), container->hasCustomName())) ); 1003 containerMenu = new HopperMenu(inventory, container); 1004 containerMenu->containerId = containerCounter; 1005 containerMenu->addSlotListener(this); 1006 } 1007 else 1008 { 1009 app.DebugPrintf("ServerPlayer tried to open minecart hopper container when one was already open\n"); 1010 } 1011 1012 return true; 1013} 1014 1015bool ServerPlayer::openFurnace(shared_ptr<FurnaceTileEntity> furnace) 1016{ 1017 if(containerMenu == inventoryMenu) 1018 { 1019 nextContainerCounter(); 1020 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::FURNACE, furnace->getCustomName(), furnace->getContainerSize(), furnace->hasCustomName()) ) ); 1021 containerMenu = new FurnaceMenu(inventory, furnace); 1022 containerMenu->containerId = containerCounter; 1023 containerMenu->addSlotListener(this); 1024 } 1025 else 1026 { 1027 app.DebugPrintf("ServerPlayer tried to open furnace when one was already open\n"); 1028 } 1029 1030 return true; 1031} 1032 1033bool ServerPlayer::openTrap(shared_ptr<DispenserTileEntity> trap) 1034{ 1035 if(containerMenu == inventoryMenu) 1036 { 1037 nextContainerCounter(); 1038 connection->send( shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, trap->GetType() == eTYPE_DROPPERTILEENTITY ? ContainerOpenPacket::DROPPER : ContainerOpenPacket::TRAP, trap->getCustomName(), trap->getContainerSize(), trap->hasCustomName() ) ) ); 1039 containerMenu = new TrapMenu(inventory, trap); 1040 containerMenu->containerId = containerCounter; 1041 containerMenu->addSlotListener(this); 1042 } 1043 else 1044 { 1045 app.DebugPrintf("ServerPlayer tried to open dispenser when one was already open\n"); 1046 } 1047 1048 return true; 1049} 1050 1051bool ServerPlayer::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingStand) 1052{ 1053 if(containerMenu == inventoryMenu) 1054 { 1055 nextContainerCounter(); 1056 connection->send(shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::BREWING_STAND, brewingStand->getCustomName(), brewingStand->getContainerSize(), brewingStand->hasCustomName() ))); 1057 containerMenu = new BrewingStandMenu(inventory, brewingStand); 1058 containerMenu->containerId = containerCounter; 1059 containerMenu->addSlotListener(this); 1060 } 1061 else 1062 { 1063 app.DebugPrintf("ServerPlayer tried to open brewing stand when one was already open\n"); 1064 } 1065 1066 return true; 1067} 1068 1069bool ServerPlayer::openBeacon(shared_ptr<BeaconTileEntity> beacon) 1070{ 1071 if(containerMenu == inventoryMenu) 1072 { 1073 nextContainerCounter(); 1074 connection->send(shared_ptr<ContainerOpenPacket>( new ContainerOpenPacket(containerCounter, ContainerOpenPacket::BEACON, beacon->getCustomName(), beacon->getContainerSize(), beacon->hasCustomName() ))); 1075 containerMenu = new BeaconMenu(inventory, beacon); 1076 containerMenu->containerId = containerCounter; 1077 containerMenu->addSlotListener(this); 1078 } 1079 else 1080 { 1081 app.DebugPrintf("ServerPlayer tried to open beacon when one was already open\n"); 1082 } 1083 1084 return true; 1085} 1086 1087bool ServerPlayer::openTrading(shared_ptr<Merchant> traderTarget, const wstring &name) 1088{ 1089 if(containerMenu == inventoryMenu) 1090 { 1091 nextContainerCounter(); 1092 containerMenu = new MerchantMenu(inventory, traderTarget, level); 1093 containerMenu->containerId = containerCounter; 1094 containerMenu->addSlotListener(this); 1095 shared_ptr<Container> container = ((MerchantMenu *) containerMenu)->getTradeContainer(); 1096 1097 connection->send(shared_ptr<ContainerOpenPacket>(new ContainerOpenPacket(containerCounter, ContainerOpenPacket::TRADER_NPC, name.empty()?L"":name, container->getContainerSize(), !name.empty()))); 1098 1099 MerchantRecipeList *offers = traderTarget->getOffers(dynamic_pointer_cast<Player>(shared_from_this())); 1100 if (offers != NULL) 1101 { 1102 ByteArrayOutputStream rawOutput; 1103 DataOutputStream output(&rawOutput); 1104 1105 // just to make sure the offers are matched to the container 1106 output.writeInt(containerCounter); 1107 offers->writeToStream(&output); 1108 1109 connection->send(shared_ptr<CustomPayloadPacket>( new CustomPayloadPacket(CustomPayloadPacket::TRADER_LIST_PACKET, rawOutput.toByteArray()))); 1110 } 1111 } 1112 else 1113 { 1114 app.DebugPrintf("ServerPlayer tried to open trading menu when one was already open\n"); 1115 } 1116 1117 return true; 1118} 1119 1120bool ServerPlayer::openHorseInventory(shared_ptr<EntityHorse> horse, shared_ptr<Container> container) 1121{ 1122 if (containerMenu != inventoryMenu) 1123 { 1124 closeContainer(); 1125 } 1126 nextContainerCounter(); 1127 connection->send(shared_ptr<ContainerOpenPacket>(new ContainerOpenPacket(containerCounter, ContainerOpenPacket::HORSE, horse->getCustomName(), container->getContainerSize(), container->hasCustomName(), horse->entityId ))); 1128 containerMenu = new HorseInventoryMenu(inventory, container, horse); 1129 containerMenu->containerId = containerCounter; 1130 containerMenu->addSlotListener(this); 1131 1132 return true; 1133} 1134 1135void ServerPlayer::slotChanged(AbstractContainerMenu *container, int slotIndex, shared_ptr<ItemInstance> item) 1136{ 1137 if (dynamic_cast<ResultSlot *>(container->getSlot(slotIndex))) 1138 { 1139 return; 1140 } 1141 1142 if (ignoreSlotUpdateHack) 1143 { 1144 // Do not send this packet! 1145 // 1146 // This is a horrible hack that makes sure that inventory clicks 1147 // that the client correctly predicted don't get sent out to the 1148 // client again. 1149 return; 1150 } 1151 1152 connection->send( shared_ptr<ContainerSetSlotPacket>( new ContainerSetSlotPacket(container->containerId, slotIndex, item) ) ); 1153 1154} 1155 1156void ServerPlayer::refreshContainer(AbstractContainerMenu *menu) 1157{ 1158 vector<shared_ptr<ItemInstance> > *items = menu->getItems(); 1159 refreshContainer(menu, items); 1160 delete items; 1161} 1162 1163void ServerPlayer::refreshContainer(AbstractContainerMenu *container, vector<shared_ptr<ItemInstance> > *items) 1164{ 1165 connection->send( shared_ptr<ContainerSetContentPacket>( new ContainerSetContentPacket(container->containerId, items) ) ); 1166 connection->send( shared_ptr<ContainerSetSlotPacket>( new ContainerSetSlotPacket(-1, -1, inventory->getCarried()) ) ); 1167} 1168 1169void ServerPlayer::setContainerData(AbstractContainerMenu *container, int id, int value) 1170{ 1171 // 4J - added, so that furnace updates also have this hack 1172 if (ignoreSlotUpdateHack) 1173 { 1174 // Do not send this packet! 1175 // 1176 // This is a horrible hack that makes sure that inventory clicks 1177 // that the client correctly predicted don't get sent out to the 1178 // client again. 1179 return; 1180 } 1181 connection->send( shared_ptr<ContainerSetDataPacket>( new ContainerSetDataPacket(container->containerId, id, value) ) ); 1182} 1183 1184void ServerPlayer::closeContainer() 1185{ 1186 connection->send( shared_ptr<ContainerClosePacket>( new ContainerClosePacket(containerMenu->containerId) ) ); 1187 doCloseContainer(); 1188} 1189 1190void ServerPlayer::broadcastCarriedItem() 1191{ 1192 if (ignoreSlotUpdateHack) 1193 { 1194 // Do not send this packet! 1195 // This is a horrible hack that makes sure that inventory clicks 1196 // that the client correctly predicted don't get sent out to the 1197 // client again. 1198 return; 1199 } 1200 connection->send( shared_ptr<ContainerSetSlotPacket>( new ContainerSetSlotPacket(-1, -1, inventory->getCarried()) ) ); 1201} 1202 1203void ServerPlayer::doCloseContainer() 1204{ 1205 containerMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) ); 1206 containerMenu = inventoryMenu; 1207} 1208 1209void ServerPlayer::setPlayerInput(float xxa, float yya, bool jumping, bool sneaking) 1210{ 1211 if(riding != NULL) 1212 { 1213 if (xxa >= -1 && xxa <= 1) this->xxa = xxa; 1214 if (yya >= -1 && yya <= 1) this->yya = yya; 1215 this->jumping = jumping; 1216 this->setSneaking(sneaking); 1217 } 1218} 1219 1220void ServerPlayer::awardStat(Stat *stat, byteArray param) 1221{ 1222 if (stat == NULL) 1223 { 1224 delete [] param.data; 1225 return; 1226 } 1227 1228 if (!stat->awardLocallyOnly) 1229 { 1230#ifndef _DURANGO 1231 int count = *((int*)param.data); 1232 delete [] param.data; 1233 1234 connection->send( shared_ptr<AwardStatPacket>( new AwardStatPacket(stat->id, count) ) ); 1235#else 1236 connection->send( shared_ptr<AwardStatPacket>( new AwardStatPacket(stat->id, param) ) ); 1237 // byteArray deleted in AwardStatPacket destructor. 1238#endif 1239 } 1240 else delete [] param.data; 1241} 1242 1243void ServerPlayer::disconnect() 1244{ 1245 if (rider.lock() != NULL) rider.lock()->ride(shared_from_this() ); 1246 if (m_isSleeping) 1247 { 1248 stopSleepInBed(true, false, false); 1249 } 1250} 1251 1252void ServerPlayer::resetSentInfo() 1253{ 1254 lastSentHealth = -99999999; 1255} 1256 1257void ServerPlayer::displayClientMessage(int messageId) 1258{ 1259 ChatPacket::EChatPacketMessage messageType = ChatPacket::e_ChatCustom; 1260 // Convert the message id to an enum that will not change between game versions 1261 switch(messageId) 1262 { 1263 case IDS_TILE_BED_OCCUPIED: 1264 messageType = ChatPacket::e_ChatBedOccupied; 1265 connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1266 break; 1267 case IDS_TILE_BED_NO_SLEEP: 1268 messageType = ChatPacket::e_ChatBedNoSleep; 1269 connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1270 break; 1271 case IDS_TILE_BED_NOT_VALID: 1272 messageType = ChatPacket::e_ChatBedNotValid; 1273 connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1274 break; 1275 case IDS_TILE_BED_NOTSAFE: 1276 messageType = ChatPacket::e_ChatBedNotSafe; 1277 connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1278 break; 1279 case IDS_TILE_BED_PLAYERSLEEP: 1280 messageType = ChatPacket::e_ChatBedPlayerSleep; 1281 // broadcast to all the other players in the game 1282 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1283 { 1284 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1285 if(shared_from_this()!=player) 1286 { 1287 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatBedPlayerSleep))); 1288 } 1289 else 1290 { 1291 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatBedMeSleep))); 1292 } 1293 } 1294 return; 1295 break; 1296 case IDS_PLAYER_ENTERED_END: 1297 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1298 { 1299 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1300 if(shared_from_this()!=player) 1301 { 1302 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerEnteredEnd))); 1303 } 1304 } 1305 break; 1306 case IDS_PLAYER_LEFT_END: 1307 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1308 { 1309 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1310 if(shared_from_this()!=player) 1311 { 1312 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerLeftEnd))); 1313 } 1314 } 1315 break; 1316 case IDS_TILE_BED_MESLEEP: 1317 messageType = ChatPacket::e_ChatBedMeSleep; 1318 connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1319 break; 1320 1321 case IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED: 1322 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1323 { 1324 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1325 if(shared_from_this()==player) 1326 { 1327 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxPigsSheepCows))); 1328 } 1329 } 1330 break; 1331 case IDS_MAX_CHICKENS_SPAWNED: 1332 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1333 { 1334 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1335 if(shared_from_this()==player) 1336 { 1337 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxChickens))); 1338 } 1339 } 1340 break; 1341 case IDS_MAX_SQUID_SPAWNED: 1342 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1343 { 1344 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1345 if(shared_from_this()==player) 1346 { 1347 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxSquid))); 1348 } 1349 } 1350 break; 1351 case IDS_MAX_BATS_SPAWNED: 1352 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1353 { 1354 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1355 if(shared_from_this()==player) 1356 { 1357 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBats))); 1358 } 1359 } 1360 break; 1361 case IDS_MAX_WOLVES_SPAWNED: 1362 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1363 { 1364 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1365 if(shared_from_this()==player) 1366 { 1367 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxWolves))); 1368 } 1369 } 1370 break; 1371 case IDS_MAX_MOOSHROOMS_SPAWNED: 1372 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1373 { 1374 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1375 if(shared_from_this()==player) 1376 { 1377 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxMooshrooms))); 1378 } 1379 } 1380 break; 1381 case IDS_MAX_ENEMIES_SPAWNED: 1382 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1383 { 1384 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1385 if(shared_from_this()==player) 1386 { 1387 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxEnemies))); 1388 } 1389 } 1390 break; 1391 1392 case IDS_MAX_VILLAGERS_SPAWNED: 1393 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1394 { 1395 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1396 if(shared_from_this()==player) 1397 { 1398 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxVillagers))); 1399 } 1400 } 1401 break; 1402 case IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED: 1403 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1404 { 1405 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1406 if(shared_from_this()==player) 1407 { 1408 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBredPigsSheepCows))); 1409 } 1410 } 1411 break; 1412 case IDS_MAX_CHICKENS_BRED: 1413 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1414 { 1415 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1416 if(shared_from_this()==player) 1417 { 1418 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBredChickens))); 1419 } 1420 } 1421 break; 1422 case IDS_MAX_MUSHROOMCOWS_BRED: 1423 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1424 { 1425 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1426 if(shared_from_this()==player) 1427 { 1428 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBredMooshrooms))); 1429 } 1430 } 1431 break; 1432 1433 case IDS_MAX_WOLVES_BRED: 1434 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1435 { 1436 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1437 if(shared_from_this()==player) 1438 { 1439 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBredWolves))); 1440 } 1441 } 1442 break; 1443 1444 case IDS_CANT_SHEAR_MOOSHROOM: 1445 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1446 { 1447 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1448 if(shared_from_this()==player) 1449 { 1450 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerCantShearMooshroom))); 1451 } 1452 } 1453 break; 1454 1455 1456 case IDS_MAX_HANGINGENTITIES: 1457 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1458 { 1459 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1460 if(shared_from_this()==player) 1461 { 1462 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxHangingEntities))); 1463 } 1464 } 1465 break; 1466 case IDS_CANT_SPAWN_IN_PEACEFUL: 1467 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1468 { 1469 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1470 if(shared_from_this()==player) 1471 { 1472 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerCantSpawnInPeaceful))); 1473 } 1474 } 1475 break; 1476 1477 case IDS_MAX_BOATS: 1478 for (unsigned int i = 0; i < server->getPlayers()->players.size(); i++) 1479 { 1480 shared_ptr<ServerPlayer> player = server->getPlayers()->players[i]; 1481 if(shared_from_this()==player) 1482 { 1483 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(name, ChatPacket::e_ChatPlayerMaxBoats))); 1484 } 1485 } 1486 break; 1487 1488 default: 1489 app.DebugPrintf("Tried to send a chat packet to the player with an unhandled messageId\n"); 1490 assert( false ); 1491 break; 1492 } 1493 1494 //Language *language = Language::getInstance(); 1495 //wstring languageString = app.GetString(messageId);//language->getElement(messageId); 1496 //connection->send( shared_ptr<ChatPacket>( new ChatPacket(L"", messageType) ) ); 1497} 1498 1499void ServerPlayer::completeUsingItem() 1500{ 1501 connection->send(shared_ptr<EntityEventPacket>( new EntityEventPacket(entityId, EntityEvent::USE_ITEM_COMPLETE) ) ); 1502 Player::completeUsingItem(); 1503} 1504 1505void ServerPlayer::startUsingItem(shared_ptr<ItemInstance> instance, int duration) 1506{ 1507 Player::startUsingItem(instance, duration); 1508 1509 if (instance != NULL && instance->getItem() != NULL && instance->getItem()->getUseAnimation(instance) == UseAnim_eat) 1510 { 1511 getLevel()->getTracker()->broadcastAndSend(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(shared_from_this(), AnimatePacket::EAT) ) ); 1512 } 1513} 1514 1515void ServerPlayer::restoreFrom(shared_ptr<Player> oldPlayer, bool restoreAll) 1516{ 1517 Player::restoreFrom(oldPlayer, restoreAll); 1518 lastSentExp = -1; 1519 lastSentHealth = -1; 1520 lastSentFood = -1; 1521 entitiesToRemove = dynamic_pointer_cast<ServerPlayer>(oldPlayer)->entitiesToRemove; 1522} 1523 1524void ServerPlayer::onEffectAdded(MobEffectInstance *effect) 1525{ 1526 Player::onEffectAdded(effect); 1527 connection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(entityId, effect) ) ); 1528} 1529 1530 1531void ServerPlayer::onEffectUpdated(MobEffectInstance *effect, bool doRefreshAttributes) 1532{ 1533 Player::onEffectUpdated(effect, doRefreshAttributes); 1534 connection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(entityId, effect) ) ); 1535} 1536 1537 1538void ServerPlayer::onEffectRemoved(MobEffectInstance *effect) 1539{ 1540 Player::onEffectRemoved(effect); 1541 connection->send(shared_ptr<RemoveMobEffectPacket>( new RemoveMobEffectPacket(entityId, effect) ) ); 1542} 1543 1544void ServerPlayer::teleportTo(double x, double y, double z) 1545{ 1546 connection->teleport(x, y, z, yRot, xRot); 1547} 1548 1549void ServerPlayer::crit(shared_ptr<Entity> entity) 1550{ 1551 getLevel()->getTracker()->broadcastAndSend(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(entity, AnimatePacket::CRITICAL_HIT) )); 1552} 1553 1554void ServerPlayer::magicCrit(shared_ptr<Entity> entity) 1555{ 1556 getLevel()->getTracker()->broadcastAndSend(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(entity, AnimatePacket::MAGIC_CRITICAL_HIT) )); 1557} 1558 1559void ServerPlayer::onUpdateAbilities() 1560{ 1561 if (connection == NULL) return; 1562 connection->send(shared_ptr<PlayerAbilitiesPacket>(new PlayerAbilitiesPacket(&abilities))); 1563} 1564 1565ServerLevel *ServerPlayer::getLevel() 1566{ 1567 return (ServerLevel *) level; 1568} 1569 1570void ServerPlayer::setGameMode(GameType *mode) 1571{ 1572 gameMode->setGameModeForPlayer(mode); 1573 connection->send(shared_ptr<GameEventPacket>(new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, mode->getId()))); 1574} 1575 1576void ServerPlayer::sendMessage(const wstring& message, ChatPacket::EChatPacketMessage type /*= e_ChatCustom*/, int customData /*= -1*/, const wstring& additionalMessage /*= L""*/) 1577{ 1578 connection->send(shared_ptr<ChatPacket>(new ChatPacket(message,type,customData,additionalMessage))); 1579} 1580 1581bool ServerPlayer::hasPermission(EGameCommand command) 1582{ 1583 return server->getPlayers()->isOp(dynamic_pointer_cast<ServerPlayer>(shared_from_this())); 1584 1585 // 4J: Removed permission level 1586 /*if( server->getPlayers()->isOp(dynamic_pointer_cast<ServerPlayer>(shared_from_this())) ) 1587 { 1588 return server->getOperatorUserPermissionLevel() >= permissionLevel; 1589 } 1590 return false;*/ 1591} 1592 1593// 4J - Don't use 1594//void ServerPlayer::updateOptions(shared_ptr<ClientInformationPacket> packet) 1595//{ 1596// // 4J - Don't need 1597// //if (language.getLanguageList().containsKey(packet.getLanguage())) 1598// //{ 1599// // language.loadLanguage(packet->getLanguage()); 1600// //} 1601// 1602// int dist = 16 * 16 >> packet->getViewDistance(); 1603// if (dist > PlayerChunkMap::MIN_VIEW_DISTANCE && dist < PlayerChunkMap::MAX_VIEW_DISTANCE) 1604// { 1605// this->viewDistance = dist; 1606// } 1607// 1608// chatVisibility = packet->getChatVisibility(); 1609// canChatColor = packet->getChatColors(); 1610// 1611// // 4J - Don't need 1612// //if (server.isSingleplayer() && server.getSingleplayerName().equals(name)) 1613// //{ 1614// // server.setDifficulty(packet.getDifficulty()); 1615// //} 1616//} 1617 1618int ServerPlayer::getViewDistance() 1619{ 1620 return viewDistance; 1621} 1622 1623//bool ServerPlayer::canChatInColor() 1624//{ 1625// return canChatColor; 1626//} 1627// 1628//int ServerPlayer::getChatVisibility() 1629//{ 1630// return chatVisibility; 1631//} 1632 1633Pos *ServerPlayer::getCommandSenderWorldPosition() 1634{ 1635 return new Pos(Mth::floor(x), Mth::floor(y + .5), Mth::floor(z)); 1636} 1637 1638void ServerPlayer::resetLastActionTime() 1639{ 1640 this->lastActionTime = MinecraftServer::getCurrentTimeMillis(); 1641} 1642 1643// Get an index that can be used to uniquely reference this chunk from either dimension 1644int ServerPlayer::getFlagIndexForChunk(const ChunkPos& pos, int dimension) 1645{ 1646 // Scale pos x & z up by 16 as getGlobalIndexForChunk is expecting tile rather than chunk coords 1647 return LevelRenderer::getGlobalIndexForChunk(pos.x * 16 , 0, pos.z * 16, dimension ) / (Level::maxBuildHeight / 16); // dividing here by number of renderer chunks in one column; 1648} 1649 1650// 4J Added, returns a number which is subtracted from the default view distance 1651int ServerPlayer::getPlayerViewDistanceModifier() 1652{ 1653 int value = 0; 1654 1655 if( !connection->isLocal() ) 1656 { 1657 INetworkPlayer *player = connection->getNetworkPlayer(); 1658 1659 if( player != NULL ) 1660 { 1661 DWORD rtt = player->GetCurrentRtt(); 1662 1663 value = rtt >> 6; 1664 1665 if(value > 4) value = 4; 1666 } 1667 } 1668 1669 return value; 1670} 1671 1672void ServerPlayer::handleCollectItem(shared_ptr<ItemInstance> item) 1673{ 1674 if(gameMode->getGameRules() != NULL) gameMode->getGameRules()->onCollectItem(item); 1675} 1676 1677#ifndef _CONTENT_PACKAGE 1678void ServerPlayer::debug_setPosition(double x, double y, double z, double nYRot, double nXRot) 1679{ 1680 connection->teleport(x, y, z, nYRot, nXRot); 1681} 1682#endif