the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1786 lines 63 kB view raw
1#include "stdafx.h" 2#include "PlayerConnection.h" 3#include "ServerPlayer.h" 4#include "ServerLevel.h" 5#include "ServerPlayerGameMode.h" 6#include "PlayerList.h" 7#include "MinecraftServer.h" 8#include "..\Minecraft.World\net.minecraft.commands.h" 9#include "..\Minecraft.World\net.minecraft.network.h" 10#include "..\Minecraft.World\net.minecraft.world.entity.item.h" 11#include "..\Minecraft.World\net.minecraft.world.level.h" 12#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" 13#include "..\Minecraft.World\net.minecraft.world.item.h" 14#include "..\Minecraft.World\net.minecraft.world.item.trading.h" 15#include "..\Minecraft.World\net.minecraft.world.inventory.h" 16#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" 17#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h" 18#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" 19#include "..\Minecraft.World\net.minecraft.network.h" 20#include "..\Minecraft.World\net.minecraft.world.food.h" 21#include "..\Minecraft.World\AABB.h" 22#include "..\Minecraft.World\Pos.h" 23#include "..\Minecraft.World\SharedConstants.h" 24#include "..\Minecraft.World\Socket.h" 25#include "..\Minecraft.World\Achievements.h" 26#include "..\Minecraft.World\net.minecraft.h" 27#include "EntityTracker.h" 28#include "ServerConnection.h" 29#include "..\Minecraft.World\GenericStats.h" 30#include "..\Minecraft.World\JavaMath.h" 31 32// 4J Added 33#include "..\Minecraft.World\net.minecraft.world.item.crafting.h" 34#include "Options.h" 35 36Random PlayerConnection::random; 37 38PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player) 39{ 40 // 4J - added initialisers 41 done = false; 42 tickCount = 0; 43 aboveGroundTickCount = 0; 44 xLastOk = yLastOk = zLastOk = 0; 45 synched = true; 46 didTick = false; 47 lastKeepAliveId = 0; 48 lastKeepAliveTime = 0; 49 lastKeepAliveTick = 0; 50 chatSpamTickCount = 0; 51 dropSpamTickCount = 0; 52 53 this->server = server; 54 this->connection = connection; 55 connection->setListener(this); 56 this->player = player; 57 // player->connection = this; // 4J - moved out as we can't assign in a ctor 58 InitializeCriticalSection(&done_cs); 59 60 m_bCloseOnTick = false; 61 m_bWasKicked = false; 62 63 m_friendsOnlyUGC = false; 64 m_offlineXUID = INVALID_XUID; 65 m_onlineXUID = INVALID_XUID; 66 m_bHasClientTickedOnce = false; 67 68 setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); 69} 70 71PlayerConnection::~PlayerConnection() 72{ 73 delete connection; 74 DeleteCriticalSection(&done_cs); 75} 76 77void PlayerConnection::tick() 78{ 79 if( done ) return; 80 81 if( m_bCloseOnTick ) 82 { 83 disconnect( DisconnectPacket::eDisconnect_Closed ); 84 return; 85 } 86 87 didTick = false; 88 tickCount++; 89 connection->tick(); 90 if(done) return; 91 92 if ((tickCount - lastKeepAliveTick) > 20 * 1) 93 { 94 lastKeepAliveTick = tickCount; 95 lastKeepAliveTime = System::nanoTime() / 1000000; 96 lastKeepAliveId = random.nextInt(); 97 send( shared_ptr<KeepAlivePacket>( new KeepAlivePacket(lastKeepAliveId) ) ); 98 } 99 100 if (chatSpamTickCount > 0) 101 { 102 chatSpamTickCount--; 103 } 104 if (dropSpamTickCount > 0) 105 { 106 dropSpamTickCount--; 107 } 108} 109 110void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) 111{ 112 EnterCriticalSection(&done_cs); 113 if( done ) 114 { 115 LeaveCriticalSection(&done_cs); 116 return; 117 } 118 119 app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); 120 player->disconnect(); 121 122 // 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system 123 server->getPlayers()->removePlayerFromReceiving( player ); 124 send( shared_ptr<DisconnectPacket>( new DisconnectPacket(reason) )); 125 connection->sendAndQuit(); 126 // 4J-PB - removed, since it needs to be localised in the language the client is in 127 //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"�e" + player->name + L" left the game.") ) ); 128 if(getWasKicked()) 129 { 130 server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); 131 } 132 else 133 { 134 server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); 135 } 136 137 server->getPlayers()->remove(player); 138 done = true; 139 LeaveCriticalSection(&done_cs); 140} 141 142void PlayerConnection::handlePlayerInput(shared_ptr<PlayerInputPacket> packet) 143{ 144 player->setPlayerInput(packet->getXxa(), packet->getYya(), packet->isJumping(), packet->isSneaking()); 145} 146 147void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet) 148{ 149 ServerLevel *level = server->getLevel(player->dimension); 150 151 didTick = true; 152 if(synched) m_bHasClientTickedOnce = true; 153 154 if (player->wonGame) return; 155 156 if (!synched) 157 { 158 double yDiff = packet->y - yLastOk; 159 if (packet->x == xLastOk && yDiff * yDiff < 0.01 && packet->z == zLastOk) 160 { 161 synched = true; 162 } 163 } 164 165 if (synched) 166 { 167 if (player->riding != NULL) 168 { 169 170 float yRotT = player->yRot; 171 float xRotT = player->xRot; 172 player->riding->positionRider(); 173 double xt = player->x; 174 double yt = player->y; 175 double zt = player->z; 176 177 if (packet->hasRot) 178 { 179 yRotT = packet->yRot; 180 xRotT = packet->xRot; 181 } 182 183 player->onGround = packet->onGround; 184 185 player->doTick(false); 186 player->ySlideOffset = 0; 187 player->absMoveTo(xt, yt, zt, yRotT, xRotT); 188 if (player->riding != NULL) player->riding->positionRider(); 189 server->getPlayers()->move(player); 190 191 // player may have been kicked off the mount during the tick, so 192 // only copy valid coordinates if the player still is "synched" 193 if (synched) { 194 xLastOk = player->x; 195 yLastOk = player->y; 196 zLastOk = player->z; 197 } 198 ((Level *)level)->tick(player); 199 200 return; 201 } 202 203 if (player->isSleeping()) 204 { 205 player->doTick(false); 206 player->absMoveTo(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); 207 ((Level *)level)->tick(player); 208 return; 209 } 210 211 double startY = player->y; 212 xLastOk = player->x; 213 yLastOk = player->y; 214 zLastOk = player->z; 215 216 217 double xt = player->x; 218 double yt = player->y; 219 double zt = player->z; 220 221 float yRotT = player->yRot; 222 float xRotT = player->xRot; 223 224 if (packet->hasPos && packet->y == -999 && packet->yView == -999) 225 { 226 packet->hasPos = false; 227 } 228 229 if (packet->hasPos) 230 { 231 xt = packet->x; 232 yt = packet->y; 233 zt = packet->z; 234 double yd = packet->yView - packet->y; 235 if (!player->isSleeping() && (yd > 1.65 || yd < 0.1)) 236 { 237 disconnect(DisconnectPacket::eDisconnect_IllegalStance); 238 // logger.warning(player->name + " had an illegal stance: " + yd); 239 return; 240 } 241 if (abs(packet->x) > 32000000 || abs(packet->z) > 32000000) 242 { 243 disconnect(DisconnectPacket::eDisconnect_IllegalPosition); 244 return; 245 } 246 } 247 if (packet->hasRot) 248 { 249 yRotT = packet->yRot; 250 xRotT = packet->xRot; 251 } 252 253 // 4J Stu Added to stop server player y pos being different than client when flying 254 if(player->abilities.mayfly || player->isAllowedToFly() ) 255 { 256 player->abilities.flying = packet->isFlying; 257 } 258 else player->abilities.flying = false; 259 260 player->doTick(false); 261 player->ySlideOffset = 0; 262 player->absMoveTo(xLastOk, yLastOk, zLastOk, yRotT, xRotT); 263 264 if (!synched) return; 265 266 double xDist = xt - player->x; 267 double yDist = yt - player->y; 268 double zDist = zt - player->z; 269 270 double dist = xDist * xDist + yDist * yDist + zDist * zDist; 271 272 // 4J-PB - removing this one for now 273 /*if (dist > 100.0f) 274 { 275 // logger.warning(player->name + " moved too quickly!"); 276 disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly); 277 // System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt); 278 // teleport(player->x, player->y, player->z, player->yRot, player->xRot); 279 return; 280 } 281 */ 282 283 float r = 1 / 16.0f; 284 bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); 285 286 if (player->onGround && !packet->onGround && yDist > 0) 287 { 288 // assume the player made a jump 289 player->causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP); 290 } 291 292 player->move(xDist, yDist, zDist); 293 294 // 4J Stu - It is possible that we are no longer synched (eg By moving into an End Portal), so we should stop any further movement based on this packet 295 // Fix for #87764 - Code: Gameplay: Host cannot move and experiences End World Chunks flickering, while in Splitscreen Mode 296 // and Fix for #87788 - Code: Gameplay: Client cannot move and experiences End World Chunks flickering, while in Splitscreen Mode 297 if (!synched) return; 298 299 player->onGround = packet->onGround; 300 // Since server players don't call travel we check food exhaustion 301 // here 302 player->checkMovementStatistiscs(xDist, yDist, zDist); 303 304 double oyDist = yDist; 305 306 xDist = xt - player->x; 307 yDist = yt - player->y; 308 309 // 4J-PB - line below will always be true! 310 if (yDist > -0.5 || yDist < 0.5) 311 { 312 yDist = 0; 313 } 314 zDist = zt - player->z; 315 dist = xDist * xDist + yDist * yDist + zDist * zDist; 316 bool fail = false; 317 if (dist > 0.25 * 0.25 && !player->isSleeping() && !player->gameMode->isCreative() && !player->isAllowedToFly()) 318 { 319 fail = true; 320 // logger.warning(player->name + " moved wrongly!"); 321 // System.out.println("Got position " + xt + ", " + yt + ", " + zt); 322 // System.out.println("Expected " + player->x + ", " + player->y + ", " + player->z); 323#ifndef _CONTENT_PACKAGE 324 wprintf(L"%ls moved wrongly!\n",player->name.c_str()); 325 app.DebugPrintf("Got position %f, %f, %f\n", xt,yt,zt); 326 app.DebugPrintf("Expected %f, %f, %f\n", player->x, player->y, player->z); 327#endif 328 } 329 player->absMoveTo(xt, yt, zt, yRotT, xRotT); 330 331 bool newOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); 332 if (oldOk && (fail || !newOk) && !player->isSleeping()) 333 { 334 teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT); 335 return; 336 } 337 AABB *testBox = player->bb->copy()->grow(r, r, r)->expand(0, -0.55, 0); 338 // && server.level.getCubes(player, testBox).size() == 0 339 if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() ) 340 { 341 if (oyDist >= (-0.5f / 16.0f)) 342 { 343 aboveGroundTickCount++; 344 if (aboveGroundTickCount > 80) 345 { 346 // logger.warning(player->name + " was kicked for floating too long!"); 347#ifndef _CONTENT_PACKAGE 348 wprintf(L"%ls was kicked for floating too long!\n", player->name.c_str()); 349#endif 350 disconnect(DisconnectPacket::eDisconnect_NoFlying); 351 return; 352 } 353 } 354 } 355 else 356 { 357 aboveGroundTickCount = 0; 358 } 359 360 player->onGround = packet->onGround; 361 server->getPlayers()->move(player); 362 player->doCheckFallDamage(player->y - startY, packet->onGround); 363 } 364 else if ((tickCount % SharedConstants::TICKS_PER_SECOND) == 0) 365 { 366 teleport(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); 367 } 368} 369 370void PlayerConnection::teleport(double x, double y, double z, float yRot, float xRot, bool sendPacket /*= true*/) 371{ 372 synched = false; 373 xLastOk = x; 374 yLastOk = y; 375 zLastOk = z; 376 player->absMoveTo(x, y, z, yRot, xRot); 377 // 4J - note that 1.62 is added to the height here as the client connection that receives this will presume it represents y + heightOffset at that end 378 // This is different to the way that height is sent back to the server, where it represents the bottom of the player bounding volume 379 if(sendPacket) player->connection->send( shared_ptr<MovePlayerPacket>( new MovePlayerPacket::PosRot(x, y + 1.62f, y, z, yRot, xRot, false, false) ) ); 380} 381 382void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet) 383{ 384 ServerLevel *level = server->getLevel(player->dimension); 385 player->resetLastActionTime(); 386 387 if (packet->action == PlayerActionPacket::DROP_ITEM) 388 { 389 player->drop(false); 390 return; 391 } 392 else if (packet->action == PlayerActionPacket::DROP_ALL_ITEMS) 393 { 394 player->drop(true); 395 return; 396 } 397 else if (packet->action == PlayerActionPacket::RELEASE_USE_ITEM) 398 { 399 player->releaseUsingItem(); 400 return; 401 } 402 403 bool shouldVerifyLocation = false; 404 if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) shouldVerifyLocation = true; 405 if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK) shouldVerifyLocation = true; 406 if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) shouldVerifyLocation = true; 407 408 int x = packet->x; 409 int y = packet->y; 410 int z = packet->z; 411 if (shouldVerifyLocation) 412 { 413 double xDist = player->x - (x + 0.5); 414 // there is a mismatch between the player's camera and the player's 415 // position, so add 1.5 blocks 416 double yDist = player->y - (y + 0.5) + 1.5; 417 double zDist = player->z - (z + 0.5); 418 double dist = xDist * xDist + yDist * yDist + zDist * zDist; 419 if (dist > 6 * 6) 420 { 421 return; 422 } 423 if (y >= server->getMaxBuildHeight()) 424 { 425 return; 426 } 427 } 428 429 if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) 430 { 431 if (true) player->gameMode->startDestroyBlock(x, y, z, packet->face); // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from Java 1.6.4) but putting back to old behaviour 432 else player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); 433 434 } 435 else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) 436 { 437 player->gameMode->stopDestroyBlock(x, y, z); 438 server->getPlayers()->prioritiseTileChanges(x, y, z, level->dimension->id); // 4J added - make sure that the update packets for this get prioritised over other general world updates 439 if (level->getTile(x, y, z) != 0) player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); 440 } 441 else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK) 442 { 443 player->gameMode->abortDestroyBlock(x, y, z); 444 if (level->getTile(x, y, z) != 0) player->connection->send(shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level))); 445 } 446} 447 448void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet) 449{ 450 ServerLevel *level = server->getLevel(player->dimension); 451 shared_ptr<ItemInstance> item = player->inventory->getSelected(); 452 bool informClient = false; 453 int x = packet->getX(); 454 int y = packet->getY(); 455 int z = packet->getZ(); 456 int face = packet->getFace(); 457 player->resetLastActionTime(); 458 459 // 4J Stu - We don't have ops, so just use the levels setting 460 bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); 461 if (packet->getFace() == 255) 462 { 463 if (item == NULL) return; 464 player->gameMode->useItem(player, level, item); 465 } 466 else if ((packet->getY() < server->getMaxBuildHeight() - 1) || (packet->getFace() != Facing::UP && packet->getY() < server->getMaxBuildHeight())) 467 { 468 if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8) 469 { 470 if (true) // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from java 1.6.4) but putting back to old behaviour 471 { 472 player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); 473 } 474 } 475 476 informClient = true; 477 } 478 else 479 { 480 //player->connection->send(shared_ptr<ChatPacket>(new ChatPacket("\u00A77Height limit for building is " + server->maxBuildHeight))); 481 informClient = true; 482 } 483 484 if (informClient) 485 { 486 487 player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); 488 489 if (face == 0) y--; 490 if (face == 1) y++; 491 if (face == 2) z--; 492 if (face == 3) z++; 493 if (face == 4) x--; 494 if (face == 5) x++; 495 496 // 4J - Fixes an issue where pistons briefly disappear when retracting. The pistons themselves shouldn't have their change from being pistonBase_Id to pistonMovingPiece_Id 497 // directly sent to the client, as this will happen on the client as a result of it actioning (via a tile event) the retraction of the piston locally. However, by putting a switch 498 // beside a piston and then performing an action on the side of it facing a piston, the following line of code will send a TileUpdatePacket containing the change to pistonMovingPiece_Id 499 // to the client, and this packet is received before the piston retract action happens - when the piston retract then occurs, it doesn't work properly because the piston tile 500 // isn't what it is expecting. 501 if( level->getTile(x,y,z) != Tile::pistonMovingPiece_Id ) 502 { 503 player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); 504 } 505 506 } 507 508 item = player->inventory->getSelected(); 509 510 bool forceClientUpdate = false; 511 if(item != NULL && packet->getItem() == NULL) 512 { 513 forceClientUpdate = true; 514 } 515 if (item != NULL && item->count == 0) 516 { 517 player->inventory->items[player->inventory->selected] = nullptr; 518 item = nullptr; 519 } 520 521 if (item == NULL || item->getUseDuration() == 0) 522 { 523 player->ignoreSlotUpdateHack = true; 524 player->inventory->items[player->inventory->selected] = ItemInstance::clone(player->inventory->items[player->inventory->selected]); 525 Slot *s = player->containerMenu->getSlotFor(player->inventory, player->inventory->selected); 526 player->containerMenu->broadcastChanges(); 527 player->ignoreSlotUpdateHack = false; 528 529 if (forceClientUpdate || !ItemInstance::matches(player->inventory->getSelected(), packet->getItem())) 530 { 531 send( shared_ptr<ContainerSetSlotPacket>( new ContainerSetSlotPacket(player->containerMenu->containerId, s->index, player->inventory->getSelected()) ) ); 532 } 533 } 534} 535 536void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) 537{ 538 EnterCriticalSection(&done_cs); 539 if( done ) return; 540 // logger.info(player.name + " lost connection: " + reason); 541 // 4J-PB - removed, since it needs to be localised in the language the client is in 542 //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"�e" + player->name + L" left the game.") ) ); 543 if(getWasKicked()) 544 { 545 server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); 546 } 547 else 548 { 549 server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); 550 } 551 server->getPlayers()->remove(player); 552 done = true; 553 LeaveCriticalSection(&done_cs); 554} 555 556void PlayerConnection::onUnhandledPacket(shared_ptr<Packet> packet) 557{ 558 // logger.warning(getClass() + " wasn't prepared to deal with a " + packet.getClass()); 559 disconnect(DisconnectPacket::eDisconnect_UnexpectedPacket); 560} 561 562void PlayerConnection::send(shared_ptr<Packet> packet) 563{ 564 if( connection->getSocket() != NULL ) 565 { 566 if( !server->getPlayers()->canReceiveAllPackets( player ) ) 567 { 568 // Check if we are allowed to send this packet type 569 if( !Packet::canSendToAnyClient(packet) ) 570 { 571 //wprintf(L"Not the systems primary player, so not sending them a packet : %ls / %d\n", player->name.c_str(), packet->getId() ); 572 return; 573 } 574 } 575 connection->send(packet); 576 } 577} 578 579// 4J Added 580void PlayerConnection::queueSend(shared_ptr<Packet> packet) 581{ 582 if( connection->getSocket() != NULL ) 583 { 584 if( !server->getPlayers()->canReceiveAllPackets( player ) ) 585 { 586 // Check if we are allowed to send this packet type 587 if( !Packet::canSendToAnyClient(packet) ) 588 { 589 //wprintf(L"Not the systems primary player, so not queueing them a packet : %ls\n", connection->getSocket()->getPlayer()->GetGamertag() ); 590 return; 591 } 592 } 593 connection->queueSend(packet); 594 } 595} 596 597void PlayerConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> packet) 598{ 599 if (packet->slot < 0 || packet->slot >= Inventory::getSelectionSize()) 600 { 601 // logger.warning(player.name + " tried to set an invalid carried item"); 602 return; 603 } 604 player->inventory->selected = packet->slot; 605 player->resetLastActionTime(); 606} 607 608void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet) 609{ 610 // 4J - TODO 611#if 0 612 wstring message = packet->message; 613 if (message.length() > SharedConstants::maxChatLength) 614 { 615 disconnect(L"Chat message too long"); 616 return; 617 } 618 message = message.trim(); 619 for (int i = 0; i < message.length(); i++) 620 { 621 if (SharedConstants.acceptableLetters.indexOf(message.charAt(i)) < 0 && (int) message.charAt(i) < 32) 622 { 623 disconnect(L"Illegal characters in chat"); 624 return; 625 } 626 } 627 628 if (message.startsWith("/")) 629 { 630 handleCommand(message); 631 } else { 632 message = "<" + player.name + "> " + message; 633 logger.info(message); 634 server.players.broadcastAll(new ChatPacket(message)); 635 } 636 chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; 637 if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) 638 { 639 disconnect("disconnect.spam"); 640 } 641#endif 642} 643 644void PlayerConnection::handleCommand(const wstring& message) 645{ 646 // 4J - TODO 647#if 0 648 server.getCommandDispatcher().performCommand(player, message); 649#endif 650} 651 652void PlayerConnection::handleAnimate(shared_ptr<AnimatePacket> packet) 653{ 654 player->resetLastActionTime(); 655 if (packet->action == AnimatePacket::SWING) 656 { 657 player->swing(); 658 } 659} 660 661void PlayerConnection::handlePlayerCommand(shared_ptr<PlayerCommandPacket> packet) 662{ 663 player->resetLastActionTime(); 664 if (packet->action == PlayerCommandPacket::START_SNEAKING) 665 { 666 player->setSneaking(true); 667 } 668 else if (packet->action == PlayerCommandPacket::STOP_SNEAKING) 669 { 670 player->setSneaking(false); 671 } 672 else if (packet->action == PlayerCommandPacket::START_SPRINTING) 673 { 674 player->setSprinting(true); 675 } 676 else if (packet->action == PlayerCommandPacket::STOP_SPRINTING) 677 { 678 player->setSprinting(false); 679 } 680 else if (packet->action == PlayerCommandPacket::STOP_SLEEPING) 681 { 682 player->stopSleepInBed(false, true, true); 683 synched = false; 684 } 685 else if (packet->action == PlayerCommandPacket::RIDING_JUMP) 686 { 687 // currently only supported by horses... 688 if ( (player->riding != NULL) && player->riding->GetType() == eTYPE_HORSE) 689 { 690 dynamic_pointer_cast<EntityHorse>(player->riding)->onPlayerJump(packet->data); 691 } 692 } 693 else if (packet->action == PlayerCommandPacket::OPEN_INVENTORY) 694 { 695 // also only supported by horses... 696 if ( (player->riding != NULL) && player->riding->instanceof(eTYPE_HORSE) ) 697 { 698 dynamic_pointer_cast<EntityHorse>(player->riding)->openInventory(player); 699 } 700 } 701 else if (packet->action == PlayerCommandPacket::START_IDLEANIM) 702 { 703 player->setIsIdle(true); 704 } 705 else if (packet->action == PlayerCommandPacket::STOP_IDLEANIM) 706 { 707 player->setIsIdle(false); 708 } 709} 710 711void PlayerConnection::setShowOnMaps(bool bVal) 712{ 713 player->setShowOnMaps(bVal); 714} 715 716void PlayerConnection::handleDisconnect(shared_ptr<DisconnectPacket> packet) 717{ 718 // 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system 719 server->getPlayers()->removePlayerFromReceiving( player ); 720 connection->close(DisconnectPacket::eDisconnect_Quitting); 721} 722 723int PlayerConnection::countDelayedPackets() 724{ 725 return connection->countDelayedPackets(); 726} 727 728void PlayerConnection::info(const wstring& string) 729{ 730 // 4J-PB - removed, since it needs to be localised in the language the client is in 731 //send( shared_ptr<ChatPacket>( new ChatPacket(L"�7" + string) ) ); 732} 733 734void PlayerConnection::warn(const wstring& string) 735{ 736 // 4J-PB - removed, since it needs to be localised in the language the client is in 737 //send( shared_ptr<ChatPacket>( new ChatPacket(L"�9" + string) ) ); 738} 739 740wstring PlayerConnection::getConsoleName() 741{ 742 return player->getName(); 743} 744 745void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet) 746{ 747 ServerLevel *level = server->getLevel(player->dimension); 748 shared_ptr<Entity> target = level->getEntity(packet->target); 749 player->resetLastActionTime(); 750 751 // Fix for #8218 - Gameplay: Attacking zombies from a different level often results in no hits being registered 752 // 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks 753 // a ray from head->head, but we may actually be looking at a different part of the entity that can be seen 754 // even though the ray is blocked. 755 if (target != NULL) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6) 756 { 757 //boole canSee = player->canSee(target); 758 //double maxDist = 6 * 6; 759 //if (!canSee) 760 //{ 761 // maxDist = 3 * 3; 762 //} 763 764 //if (player->distanceToSqr(target) < maxDist) 765 //{ 766 if (packet->action == InteractPacket::INTERACT) 767 { 768 player->interact(target); 769 } 770 else if (packet->action == InteractPacket::ATTACK) 771 { 772 if ((target->GetType() == eTYPE_ITEMENTITY) || (target->GetType() == eTYPE_EXPERIENCEORB) || (target->GetType() == eTYPE_ARROW) || target == player) 773 { 774 //disconnect("Attempting to attack an invalid entity"); 775 //server.warn("Player " + player.getName() + " tried to attack an invalid entity"); 776 return; 777 } 778 player->attack(target); 779 } 780 //} 781 } 782 783} 784 785bool PlayerConnection::canHandleAsyncPackets() 786{ 787 return true; 788} 789 790void PlayerConnection::handleTexture(shared_ptr<TexturePacket> packet) 791{ 792 // Both PlayerConnection and ClientConnection should handle this mostly the same way 793 794 if(packet->dwBytes==0) 795 { 796 // Request for texture 797#ifndef _CONTENT_PACKAGE 798 wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); 799#endif 800 PBYTE pbData=NULL; 801 DWORD dwBytes=0; 802 app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes); 803 804 if(dwBytes!=0) 805 { 806 send( shared_ptr<TexturePacket>( new TexturePacket(packet->textureName,pbData,dwBytes) ) ); 807 } 808 else 809 { 810 m_texturesRequested.push_back( packet->textureName ); 811 } 812 } 813 else 814 { 815 // Response with texture data 816#ifndef _CONTENT_PACKAGE 817 wprintf(L"Server received custom texture %ls\n",packet->textureName.c_str()); 818#endif 819 app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwBytes); 820 server->connection->handleTextureReceived(packet->textureName); 821 } 822} 823 824void PlayerConnection::handleTextureAndGeometry(shared_ptr<TextureAndGeometryPacket> packet) 825{ 826 // Both PlayerConnection and ClientConnection should handle this mostly the same way 827 828 if(packet->dwTextureBytes==0) 829 { 830 // Request for texture and geometry 831#ifndef _CONTENT_PACKAGE 832 wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); 833#endif 834 PBYTE pbData=NULL; 835 DWORD dwTextureBytes=0; 836 app.GetMemFileDetails(packet->textureName,&pbData,&dwTextureBytes); 837 DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(packet->textureName); 838 839 if(dwTextureBytes!=0) 840 { 841 842 if(pDLCSkinFile) 843 { 844 if(pDLCSkinFile->getAdditionalBoxesCount()!=0) 845 { 846 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); 847 } 848 else 849 { 850 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes) ) ); 851 } 852 } 853 else 854 { 855 // we don't have the dlc skin, so retrieve the data from the app store 856 vector<SKIN_BOX *> *pvSkinBoxes = app.GetAdditionalSkinBoxes(packet->dwSkinID); 857 unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(packet->dwSkinID); 858 859 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pvSkinBoxes,uiAnimOverrideBitmask) ) ); 860 } 861 } 862 else 863 { 864 m_texturesRequested.push_back( packet->textureName ); 865 } 866 } 867 else 868 { 869 // Response with texture and geometry data 870#ifndef _CONTENT_PACKAGE 871 wprintf(L"Server received custom texture %ls and geometry\n",packet->textureName.c_str()); 872#endif 873 app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwTextureBytes); 874 875 // add the geometry to the app list 876 if(packet->dwBoxC!=0) 877 { 878#ifndef _CONTENT_PACKAGE 879 wprintf(L"Adding skin boxes for skin id %X, box count %d\n",packet->dwSkinID,packet->dwBoxC); 880#endif 881 app.SetAdditionalSkinBoxes(packet->dwSkinID,packet->BoxDataA,packet->dwBoxC); 882 } 883 // Add the anim override 884 app.SetAnimOverrideBitmask(packet->dwSkinID,packet->uiAnimOverrideBitmask); 885 886 player->setCustomSkin(packet->dwSkinID); 887 888 server->connection->handleTextureAndGeometryReceived(packet->textureName); 889 } 890} 891 892void PlayerConnection::handleTextureReceived(const wstring &textureName) 893{ 894 // This sends the server received texture out to any other players waiting for the data 895 AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); 896 if( it != m_texturesRequested.end() ) 897 { 898 PBYTE pbData=NULL; 899 DWORD dwBytes=0; 900 app.GetMemFileDetails(textureName,&pbData,&dwBytes); 901 902 if(dwBytes!=0) 903 { 904 send( shared_ptr<TexturePacket>( new TexturePacket(textureName,pbData,dwBytes) ) ); 905 m_texturesRequested.erase(it); 906 } 907 } 908} 909 910void PlayerConnection::handleTextureAndGeometryReceived(const wstring &textureName) 911{ 912 // This sends the server received texture out to any other players waiting for the data 913 AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); 914 if( it != m_texturesRequested.end() ) 915 { 916 PBYTE pbData=NULL; 917 DWORD dwTextureBytes=0; 918 app.GetMemFileDetails(textureName,&pbData,&dwTextureBytes); 919 DLCSkinFile *pDLCSkinFile=app.m_dlcManager.getSkinFile(textureName); 920 921 if(dwTextureBytes!=0) 922 { 923 if(pDLCSkinFile && (pDLCSkinFile->getAdditionalBoxesCount()!=0)) 924 { 925 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); 926 } 927 else 928 { 929 // get the data from the app 930 DWORD dwSkinID = app.getSkinIdFromPath(textureName); 931 vector<SKIN_BOX *> *pvSkinBoxes = app.GetAdditionalSkinBoxes(dwSkinID); 932 unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(dwSkinID); 933 934 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes, pvSkinBoxes, uiAnimOverrideBitmask) ) ); 935 } 936 m_texturesRequested.erase(it); 937 } 938 } 939} 940 941void PlayerConnection::handleTextureChange(shared_ptr<TextureChangePacket> packet) 942{ 943 switch(packet->action) 944 { 945 case TextureChangePacket::e_TextureChange_Skin: 946 player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); 947#ifndef _CONTENT_PACKAGE 948 wprintf(L"Skin for server player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); 949#endif 950 break; 951 case TextureChangePacket::e_TextureChange_Cape: 952 player->setCustomCape( Player::getCapeIdFromPath( packet->path ) ); 953 //player->customTextureUrl2 = packet->path; 954#ifndef _CONTENT_PACKAGE 955 wprintf(L"Cape for server player %ls has changed to %ls\n", player->name.c_str(), player->customTextureUrl2.c_str() ); 956#endif 957 break; 958 } 959 if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) 960 { 961 if( server->connection->addPendingTextureRequest(packet->path)) 962 { 963#ifndef _CONTENT_PACKAGE 964 wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); 965#endif 966 send(shared_ptr<TexturePacket>( new TexturePacket(packet->path,NULL,0) ) ); 967 } 968 } 969 else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) 970 { 971 // Update the ref count on the memory texture data 972 app.AddMemoryTextureFile(packet->path,NULL,0); 973 } 974 server->getPlayers()->broadcastAll( shared_ptr<TextureChangePacket>( new TextureChangePacket(player,packet->action,packet->path) ), player->dimension ); 975} 976 977void PlayerConnection::handleTextureAndGeometryChange(shared_ptr<TextureAndGeometryChangePacket> packet) 978{ 979 980 player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); 981#ifndef _CONTENT_PACKAGE 982 wprintf(L"PlayerConnection::handleTextureAndGeometryChange - Skin for server player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); 983#endif 984 985 986 if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) 987 { 988 if( server->connection->addPendingTextureRequest(packet->path)) 989 { 990#ifndef _CONTENT_PACKAGE 991 wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); 992#endif 993 send(shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->path,NULL,0) ) ); 994 } 995 } 996 else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) 997 { 998 // Update the ref count on the memory texture data 999 app.AddMemoryTextureFile(packet->path,NULL,0); 1000 1001 player->setCustomSkin(packet->dwSkinID); 1002 1003 // If we already have the texture, then we already have the model parts too 1004 //app.SetAdditionalSkinBoxes(packet->dwSkinID,) 1005 //DebugBreak(); 1006 } 1007 server->getPlayers()->broadcastAll( shared_ptr<TextureAndGeometryChangePacket>( new TextureAndGeometryChangePacket(player,packet->path) ), player->dimension ); 1008} 1009 1010void PlayerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChangedPacket> packet) 1011{ 1012 if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS) 1013 { 1014 // Need to check that this player has permission to change each individual setting? 1015 1016 INetworkPlayer *networkPlayer = getNetworkPlayer(); 1017 if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) 1018 { 1019 app.SetGameHostOption(eGameHostOption_FireSpreads, app.GetGameHostOption(packet->data,eGameHostOption_FireSpreads)); 1020 app.SetGameHostOption(eGameHostOption_TNT, app.GetGameHostOption(packet->data,eGameHostOption_TNT)); 1021 app.SetGameHostOption(eGameHostOption_MobGriefing, app.GetGameHostOption(packet->data, eGameHostOption_MobGriefing)); 1022 app.SetGameHostOption(eGameHostOption_KeepInventory, app.GetGameHostOption(packet->data, eGameHostOption_KeepInventory)); 1023 app.SetGameHostOption(eGameHostOption_DoMobSpawning, app.GetGameHostOption(packet->data, eGameHostOption_DoMobSpawning)); 1024 app.SetGameHostOption(eGameHostOption_DoMobLoot, app.GetGameHostOption(packet->data, eGameHostOption_DoMobLoot)); 1025 app.SetGameHostOption(eGameHostOption_DoTileDrops, app.GetGameHostOption(packet->data, eGameHostOption_DoTileDrops)); 1026 app.SetGameHostOption(eGameHostOption_DoDaylightCycle, app.GetGameHostOption(packet->data, eGameHostOption_DoDaylightCycle)); 1027 app.SetGameHostOption(eGameHostOption_NaturalRegeneration, app.GetGameHostOption(packet->data, eGameHostOption_NaturalRegeneration)); 1028 1029 server->getPlayers()->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS,app.GetGameHostOption(eGameHostOption_All) ) ) ); 1030 1031 // Update the QoS data 1032 g_NetworkManager.UpdateAndSetGameSessionData(); 1033 } 1034 } 1035} 1036 1037void PlayerConnection::handleKickPlayer(shared_ptr<KickPlayerPacket> packet) 1038{ 1039 INetworkPlayer *networkPlayer = getNetworkPlayer(); 1040 if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) 1041 { 1042 server->getPlayers()->kickPlayerByShortId(packet->m_networkSmallId); 1043 } 1044} 1045 1046void PlayerConnection::handleGameCommand(shared_ptr<GameCommandPacket> packet) 1047{ 1048 MinecraftServer::getInstance()->getCommandDispatcher()->performCommand(player, packet->command, packet->data); 1049} 1050 1051void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packet) 1052{ 1053 player->resetLastActionTime(); 1054 if (packet->action == ClientCommandPacket::PERFORM_RESPAWN) 1055 { 1056 if (player->wonGame) 1057 { 1058 player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true); 1059 } 1060 //else if (player.getLevel().getLevelData().isHardcore()) 1061 //{ 1062 // if (server.isSingleplayer() && player.name.equals(server.getSingleplayerName())) 1063 // { 1064 // player.connection.disconnect("You have died. Game over, man, it's game over!"); 1065 // server.selfDestruct(); 1066 // } 1067 // else 1068 // { 1069 // BanEntry ban = new BanEntry(player.name); 1070 // ban.setReason("Death in Hardcore"); 1071 1072 // server.getPlayers().getBans().add(ban); 1073 // player.connection.disconnect("You have died. Game over, man, it's game over!"); 1074 // } 1075 //} 1076 else 1077 { 1078 if (player->getHealth() > 0) return; 1079 player = server->getPlayers()->respawn(player, 0, false); 1080 } 1081 } 1082} 1083 1084void PlayerConnection::handleRespawn(shared_ptr<RespawnPacket> packet) 1085{ 1086} 1087 1088void PlayerConnection::handleContainerClose(shared_ptr<ContainerClosePacket> packet) 1089{ 1090 player->doCloseContainer(); 1091} 1092 1093#ifndef _CONTENT_PACKAGE 1094void PlayerConnection::handleContainerSetSlot(shared_ptr<ContainerSetSlotPacket> packet) 1095{ 1096 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED ) 1097 { 1098 player->inventory->setCarried(packet->item); 1099 } 1100 else 1101 { 1102 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY && packet->slot >= 36 && packet->slot < 36 + 9) 1103 { 1104 shared_ptr<ItemInstance> lastItem = player->inventoryMenu->getSlot(packet->slot)->getItem(); 1105 if (packet->item != NULL) 1106 { 1107 if (lastItem == NULL || lastItem->count < packet->item->count) 1108 { 1109 packet->item->popTime = Inventory::POP_TIME_DURATION; 1110 } 1111 } 1112 player->inventoryMenu->setItem(packet->slot, packet->item); 1113 player->ignoreSlotUpdateHack = true; 1114 player->containerMenu->broadcastChanges(); 1115 player->broadcastCarriedItem(); 1116 player->ignoreSlotUpdateHack = false; 1117 } 1118 else if (packet->containerId == player->containerMenu->containerId) 1119 { 1120 player->containerMenu->setItem(packet->slot, packet->item); 1121 player->ignoreSlotUpdateHack = true; 1122 player->containerMenu->broadcastChanges(); 1123 player->broadcastCarriedItem(); 1124 player->ignoreSlotUpdateHack = false; 1125 } 1126 } 1127} 1128#endif 1129 1130void PlayerConnection::handleContainerClick(shared_ptr<ContainerClickPacket> packet) 1131{ 1132 player->resetLastActionTime(); 1133 if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) 1134 { 1135 shared_ptr<ItemInstance> clicked = player->containerMenu->clicked(packet->slotNum, packet->buttonNum, packet->clickType, player); 1136 1137 if (ItemInstance::matches(packet->item, clicked)) 1138 { 1139 // Yep, you sure did click what you claimed to click! 1140 player->connection->send( shared_ptr<ContainerAckPacket>( new ContainerAckPacket(packet->containerId, packet->uid, true) ) ); 1141 player->ignoreSlotUpdateHack = true; 1142 player->containerMenu->broadcastChanges(); 1143 player->broadcastCarriedItem(); 1144 player->ignoreSlotUpdateHack = false; 1145 } 1146 else 1147 { 1148 // No, you clicked the wrong thing! 1149 expectedAcks[player->containerMenu->containerId] = packet->uid; 1150 player->connection->send( shared_ptr<ContainerAckPacket>( new ContainerAckPacket(packet->containerId, packet->uid, false) ) ); 1151 player->containerMenu->setSynched(player, false); 1152 1153 vector<shared_ptr<ItemInstance> > items; 1154 for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++) 1155 { 1156 items.push_back(player->containerMenu->slots.at(i)->getItem()); 1157 } 1158 player->refreshContainer(player->containerMenu, &items); 1159 1160 // player.containerMenu.broadcastChanges(); 1161 } 1162 } 1163 1164} 1165 1166void PlayerConnection::handleContainerButtonClick(shared_ptr<ContainerButtonClickPacket> packet) 1167{ 1168 player->resetLastActionTime(); 1169 if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) 1170 { 1171 player->containerMenu->clickMenuButton(player, packet->buttonId); 1172 player->containerMenu->broadcastChanges(); 1173 } 1174} 1175 1176void PlayerConnection::handleSetCreativeModeSlot(shared_ptr<SetCreativeModeSlotPacket> packet) 1177{ 1178 if (player->gameMode->isCreative()) 1179 { 1180 bool drop = packet->slotNum < 0; 1181 shared_ptr<ItemInstance> item = packet->item; 1182 1183 if(item != NULL && item->id == Item::map_Id) 1184 { 1185 int mapScale = 3; 1186#ifdef _LARGE_WORLDS 1187 int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); 1188 int centreXC = (int) (Math::round(player->x / scale) * scale); 1189 int centreZC = (int) (Math::round(player->z / scale) * scale); 1190#else 1191 // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map 1192 int centreXC = 0; 1193 int centreZC = 0; 1194#endif 1195 item->setAuxValue( player->level->getAuxValueForMap(player->getXuid(), player->dimension, centreXC, centreZC, mapScale) ); 1196 1197 shared_ptr<MapItemSavedData> data = MapItem::getSavedData(item->getAuxValue(), player->level); 1198 // 4J Stu - We only have one map per player per dimension, so don't reset the one that they have 1199 // when a new one is created 1200 wchar_t buf[64]; 1201 swprintf(buf,64,L"map_%d", item->getAuxValue()); 1202 std::wstring id = wstring(buf); 1203 if( data == NULL ) 1204 { 1205 data = shared_ptr<MapItemSavedData>( new MapItemSavedData(id) ); 1206 } 1207 player->level->setSavedData(id, (shared_ptr<SavedData> ) data); 1208 1209 data->scale = mapScale; 1210 // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map 1211 data->x = centreXC; 1212 data->z = centreZC; 1213 data->dimension = (byte) player->level->dimension->id; 1214 data->setDirty(); 1215 } 1216 1217 bool validSlot = (packet->slotNum >= InventoryMenu::CRAFT_SLOT_START && packet->slotNum < (InventoryMenu::USE_ROW_SLOT_START + Inventory::getSelectionSize())); 1218 bool validItem = item == NULL || (item->id < Item::items.length && item->id >= 0 && Item::items[item->id] != NULL); 1219 bool validData = item == NULL || (item->getAuxValue() >= 0 && item->count > 0 && item->count <= 64); 1220 1221 if (validSlot && validItem && validData) 1222 { 1223 if (item == NULL) 1224 { 1225 player->inventoryMenu->setItem(packet->slotNum, nullptr); 1226 } 1227 else 1228 { 1229 player->inventoryMenu->setItem(packet->slotNum, item ); 1230 } 1231 player->inventoryMenu->setSynched(player, true); 1232 // player.slotChanged(player.inventoryMenu, packet.slotNum, player.inventoryMenu.getSlot(packet.slotNum).getItem()); 1233 } 1234 else if (drop && validItem && validData) 1235 { 1236 if (dropSpamTickCount < SharedConstants::TICKS_PER_SECOND * 10) 1237 { 1238 dropSpamTickCount += SharedConstants::TICKS_PER_SECOND; 1239 // drop item 1240 shared_ptr<ItemEntity> dropped = player->drop(item); 1241 if (dropped != NULL) 1242 { 1243 dropped->setShortLifeTime(); 1244 } 1245 } 1246 } 1247 1248 if( item != NULL && item->id == Item::map_Id ) 1249 { 1250 // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong 1251 // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem 1252 vector<shared_ptr<ItemInstance> > items; 1253 for (unsigned int i = 0; i < player->inventoryMenu->slots.size(); i++) 1254 { 1255 items.push_back(player->inventoryMenu->slots.at(i)->getItem()); 1256 } 1257 player->refreshContainer(player->inventoryMenu, &items); 1258 } 1259 } 1260} 1261 1262void PlayerConnection::handleContainerAck(shared_ptr<ContainerAckPacket> packet) 1263{ 1264 AUTO_VAR(it, expectedAcks.find(player->containerMenu->containerId)); 1265 1266 if (it != expectedAcks.end() && packet->uid == it->second && player->containerMenu->containerId == packet->containerId && !player->containerMenu->isSynched(player)) 1267 { 1268 player->containerMenu->setSynched(player, true); 1269 } 1270} 1271 1272void PlayerConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet) 1273{ 1274 player->resetLastActionTime(); 1275 app.DebugPrintf("PlayerConnection::handleSignUpdate\n"); 1276 1277 ServerLevel *level = server->getLevel(player->dimension); 1278 if (level->hasChunkAt(packet->x, packet->y, packet->z)) 1279 { 1280 shared_ptr<TileEntity> te = level->getTileEntity(packet->x, packet->y, packet->z); 1281 1282 if (dynamic_pointer_cast<SignTileEntity>(te) != NULL) 1283 { 1284 shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te); 1285 if (!ste->isEditable() || ste->getPlayerWhoMayEdit() != player) 1286 { 1287 server->warn(L"Player " + player->getName() + L" just tried to change non-editable sign"); 1288 return; 1289 } 1290 } 1291 1292 // 4J-JEV: Changed to allow characters to display as a []. 1293 if (dynamic_pointer_cast<SignTileEntity>(te) != NULL) 1294 { 1295 int x = packet->x; 1296 int y = packet->y; 1297 int z = packet->z; 1298 shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te); 1299 for (int i = 0; i < 4; i++) 1300 { 1301 wstring lineText = packet->lines[i].substr(0,15); 1302 ste->SetMessage( i, lineText ); 1303 } 1304 ste->SetVerified(false); 1305 ste->setChanged(); 1306 level->sendTileUpdated(x, y, z); 1307 } 1308 } 1309 1310} 1311 1312void PlayerConnection::handleKeepAlive(shared_ptr<KeepAlivePacket> packet) 1313{ 1314 if (packet->id == lastKeepAliveId) 1315 { 1316 int time = (int) (System::nanoTime() / 1000000 - lastKeepAliveTime); 1317 player->latency = (player->latency * 3 + time) / 4; 1318 } 1319} 1320 1321void PlayerConnection::handlePlayerInfo(shared_ptr<PlayerInfoPacket> packet) 1322{ 1323 // Need to check that this player has permission to change each individual setting? 1324 1325 INetworkPlayer *networkPlayer = getNetworkPlayer(); 1326 if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator() ) 1327 { 1328 shared_ptr<ServerPlayer> serverPlayer; 1329 // Find the player being edited 1330 for(AUTO_VAR(it, server->getPlayers()->players.begin()); it != server->getPlayers()->players.end(); ++it) 1331 { 1332 shared_ptr<ServerPlayer> checkingPlayer = *it; 1333 if(checkingPlayer->connection->getNetworkPlayer() != NULL && checkingPlayer->connection->getNetworkPlayer()->GetSmallId() == packet->m_networkSmallId) 1334 { 1335 serverPlayer = checkingPlayer; 1336 break; 1337 } 1338 } 1339 1340 if(serverPlayer != NULL) 1341 { 1342 unsigned int origPrivs = serverPlayer->getAllPlayerGamePrivileges(); 1343 1344 bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; 1345 bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; 1346 if(serverPlayer == player) 1347 { 1348 GameType *gameType = Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ? GameType::CREATIVE : GameType::SURVIVAL; 1349 gameType = LevelSettings::validateGameType(gameType->getId()); 1350 if (serverPlayer->gameMode->getGameModeForPlayer() != gameType) 1351 { 1352#ifndef _CONTENT_PACKAGE 1353 wprintf(L"Setting %ls to game mode %d\n", serverPlayer->name.c_str(), gameType); 1354#endif 1355 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CreativeMode,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ); 1356 serverPlayer->gameMode->setGameModeForPlayer(gameType); 1357 serverPlayer->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, gameType->getId()) )); 1358 } 1359 else 1360 { 1361#ifndef _CONTENT_PACKAGE 1362 wprintf(L"%ls already has game mode %d\n", serverPlayer->name.c_str(), gameType); 1363#endif 1364 } 1365 if(cheats) 1366 { 1367 // Editing self 1368 bool canBeInvisible = Player::getPlayerGamePrivilege(origPrivs, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; 1369 if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invisible) ); 1370 if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invulnerable) ); 1371 1372 bool inCreativeMode = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CreativeMode) != 0; 1373 if(!inCreativeMode) 1374 { 1375 bool canFly = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleFly); 1376 bool canChangeHunger = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleClassicHunger); 1377 1378 if(canFly)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanFly) ); 1379 if(canChangeHunger)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_ClassicHunger) ); 1380 } 1381 } 1382 } 1383 else 1384 { 1385 // Editing someone else 1386 if(!trustPlayers && !serverPlayer->connection->getNetworkPlayer()->IsHost()) 1387 { 1388 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotMine) ); 1389 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotBuild) ); 1390 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackPlayers) ); 1391 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackAnimals) ); 1392 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches) ); 1393 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseContainers) ); 1394 } 1395 1396 if(networkPlayer->IsHost()) 1397 { 1398 if(cheats) 1399 { 1400 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleInvisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleInvisible) ); 1401 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly) ); 1402 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger) ); 1403 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport) ); 1404 } 1405 serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Op) ); 1406 } 1407 } 1408 1409 server->getPlayers()->broadcastAll( shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket( serverPlayer ) ) ); 1410 } 1411 } 1412} 1413 1414bool PlayerConnection::isServerPacketListener() 1415{ 1416 return true; 1417} 1418 1419void PlayerConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> playerAbilitiesPacket) 1420{ 1421 player->abilities.flying = playerAbilitiesPacket->isFlying() && player->abilities.mayfly; 1422} 1423 1424//void handleChatAutoComplete(ChatAutoCompletePacket packet) { 1425// StringBuilder result = new StringBuilder(); 1426 1427// for (String candidate : server.getAutoCompletions(player, packet.getMessage())) { 1428// if (result.length() > 0) result.append("\0"); 1429 1430// result.append(candidate); 1431// } 1432 1433// player.connection.send(new ChatAutoCompletePacket(result.toString())); 1434//} 1435 1436//void handleClientInformation(shared_ptr<ClientInformationPacket> packet) 1437//{ 1438// player->updateOptions(packet); 1439//} 1440 1441void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> customPayloadPacket) 1442{ 1443#if 0 1444 if (CustomPayloadPacket.CUSTOM_BOOK_PACKET.equals(customPayloadPacket.identifier)) 1445 { 1446 ByteArrayInputStream bais(customPayloadPacket->data); 1447 DataInputStream input(&bais); 1448 shared_ptr<ItemInstance> sentItem = Packet::readItem(input); 1449 1450 if (!WritingBookItem.makeSureTagIsValid(sentItem.getTag())) 1451 { 1452 throw new IOException("Invalid book tag!"); 1453 } 1454 1455 // make sure the sent item is the currently carried item 1456 ItemInstance carried = player.inventory.getSelected(); 1457 if (sentItem != null && sentItem.id == Item.writingBook.id && sentItem.id == carried.id) 1458 { 1459 carried.addTagElement(WrittenBookItem.TAG_PAGES, sentItem.getTag().getList(WrittenBookItem.TAG_PAGES)); 1460 } 1461 } 1462 else if (CustomPayloadPacket.CUSTOM_BOOK_SIGN_PACKET.equals(customPayloadPacket.identifier)) 1463 { 1464 DataInputStream input = new DataInputStream(new ByteArrayInputStream(customPayloadPacket.data)); 1465 ItemInstance sentItem = Packet.readItem(input); 1466 1467 if (!WrittenBookItem.makeSureTagIsValid(sentItem.getTag())) 1468 { 1469 throw new IOException("Invalid book tag!"); 1470 } 1471 1472 // make sure the sent item is the currently carried item 1473 ItemInstance carried = player.inventory.getSelected(); 1474 if (sentItem != null && sentItem.id == Item.writtenBook.id && carried.id == Item.writingBook.id) 1475 { 1476 carried.addTagElement(WrittenBookItem.TAG_AUTHOR, new StringTag(WrittenBookItem.TAG_AUTHOR, player.getName())); 1477 carried.addTagElement(WrittenBookItem.TAG_TITLE, new StringTag(WrittenBookItem.TAG_TITLE, sentItem.getTag().getString(WrittenBookItem.TAG_TITLE))); 1478 carried.addTagElement(WrittenBookItem.TAG_PAGES, sentItem.getTag().getList(WrittenBookItem.TAG_PAGES)); 1479 carried.id = Item.writtenBook.id; 1480 } 1481 } 1482 else 1483#endif 1484 if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) 1485 { 1486 ByteArrayInputStream bais(customPayloadPacket->data); 1487 DataInputStream input(&bais); 1488 int selection = input.readInt(); 1489 1490 AbstractContainerMenu *menu = player->containerMenu; 1491 if (dynamic_cast<MerchantMenu *>(menu)) 1492 { 1493 ((MerchantMenu *) menu)->setSelectionHint(selection); 1494 } 1495 } 1496 else if (CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET.compare(customPayloadPacket->identifier) == 0) 1497 { 1498 if (!server->isCommandBlockEnabled()) 1499 { 1500 app.DebugPrintf("Command blocks not enabled"); 1501 //player->sendMessage(ChatMessageComponent.forTranslation("advMode.notEnabled")); 1502 } 1503 else if (player->hasPermission(eGameCommand_Effect) && player->abilities.instabuild) 1504 { 1505 ByteArrayInputStream bais(customPayloadPacket->data); 1506 DataInputStream input(&bais); 1507 int x = input.readInt(); 1508 int y = input.readInt(); 1509 int z = input.readInt(); 1510 wstring command = Packet::readUtf(&input, 256); 1511 1512 shared_ptr<TileEntity> tileEntity = player->level->getTileEntity(x, y, z); 1513 shared_ptr<CommandBlockEntity> cbe = dynamic_pointer_cast<CommandBlockEntity>(tileEntity); 1514 if (tileEntity != NULL && cbe != NULL) 1515 { 1516 cbe->setCommand(command); 1517 player->level->sendTileUpdated(x, y, z); 1518 //player->sendMessage(ChatMessageComponent.forTranslation("advMode.setCommand.success", command)); 1519 } 1520 } 1521 else 1522 { 1523 //player.sendMessage(ChatMessageComponent.forTranslation("advMode.notAllowed")); 1524 } 1525 } 1526 else if (CustomPayloadPacket::SET_BEACON_PACKET.compare(customPayloadPacket->identifier) == 0) 1527 { 1528 if ( dynamic_cast<BeaconMenu *>( player->containerMenu) != NULL) 1529 { 1530 ByteArrayInputStream bais(customPayloadPacket->data); 1531 DataInputStream input(&bais); 1532 int primary = input.readInt(); 1533 int secondary = input.readInt(); 1534 1535 BeaconMenu *beaconMenu = (BeaconMenu *) player->containerMenu; 1536 Slot *slot = beaconMenu->getSlot(0); 1537 if (slot->hasItem()) 1538 { 1539 slot->remove(1); 1540 shared_ptr<BeaconTileEntity> beacon = beaconMenu->getBeacon(); 1541 beacon->setPrimaryPower(primary); 1542 beacon->setSecondaryPower(secondary); 1543 beacon->setChanged(); 1544 } 1545 } 1546 } 1547 else if (CustomPayloadPacket::SET_ITEM_NAME_PACKET.compare(customPayloadPacket->identifier) == 0) 1548 { 1549 AnvilMenu *menu = dynamic_cast<AnvilMenu *>( player->containerMenu); 1550 if (menu) 1551 { 1552 if (customPayloadPacket->data.data == NULL || customPayloadPacket->data.length < 1) 1553 { 1554 menu->setItemName(L""); 1555 } 1556 else 1557 { 1558 ByteArrayInputStream bais(customPayloadPacket->data); 1559 DataInputStream dis(&bais); 1560 wstring name = dis.readUTF(); 1561 if (name.length() <= 30) 1562 { 1563 menu->setItemName(name); 1564 } 1565 } 1566 } 1567 } 1568} 1569 1570bool PlayerConnection::isDisconnected() 1571{ 1572 return done; 1573} 1574 1575// 4J Added 1576 1577void PlayerConnection::handleDebugOptions(shared_ptr<DebugOptionsPacket> packet) 1578{ 1579 //Player player = dynamic_pointer_cast<Player>( player->shared_from_this() ); 1580 player->SetDebugOptions(packet->m_uiVal); 1581} 1582 1583void PlayerConnection::handleCraftItem(shared_ptr<CraftItemPacket> packet) 1584{ 1585 int iRecipe = packet->recipe; 1586 1587 if(iRecipe == -1) 1588 return; 1589 1590 Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); 1591 shared_ptr<ItemInstance> pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); 1592 1593 if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<<eDebugSetting_CraftAnything))) 1594 { 1595 pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); 1596 if(player->inventory->add(pTempItemInst)==false ) 1597 { 1598 // no room in inventory, so throw it down 1599 player->drop(pTempItemInst); 1600 } 1601 } 1602 else if (pTempItemInst->id == Item::fireworksCharge_Id || pTempItemInst->id == Item::fireworks_Id) 1603 { 1604 CraftingMenu *menu = (CraftingMenu *)player->containerMenu; 1605 player->openFireworks(menu->getX(), menu->getY(), menu->getZ() ); 1606 } 1607 else 1608 { 1609 1610 1611 // TODO 4J Stu - Assume at the moment that the client can work this out for us... 1612 //if(pRecipeIngredientsRequired[iRecipe].bCanMake) 1613 //{ 1614 pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); 1615 1616 // and remove those resources from your inventory 1617 for(int i=0;i<pRecipeIngredientsRequired[iRecipe].iIngC;i++) 1618 { 1619 for(int j=0;j<pRecipeIngredientsRequired[iRecipe].iIngValA[i];j++) 1620 { 1621 shared_ptr<ItemInstance> ingItemInst = nullptr; 1622 // do we need to remove a specific aux value? 1623 if(pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]!=Recipes::ANY_AUX_VALUE) 1624 { 1625 ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i] ); 1626 player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]); 1627 } 1628 else 1629 { 1630 ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i] ); 1631 player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i]); 1632 } 1633 1634 // 4J Stu - Fix for #13097 - Bug: Milk Buckets are removed when crafting Cake 1635 if (ingItemInst != NULL) 1636 { 1637 if (ingItemInst->getItem()->hasCraftingRemainingItem()) 1638 { 1639 // replace item with remaining result 1640 player->inventory->add( shared_ptr<ItemInstance>( new ItemInstance(ingItemInst->getItem()->getCraftingRemainingItem()) ) ); 1641 } 1642 1643 } 1644 } 1645 } 1646 1647 // 4J Stu - Fix for #13119 - We should add the item after we remove the ingredients 1648 if(player->inventory->add(pTempItemInst)==false ) 1649 { 1650 // no room in inventory, so throw it down 1651 player->drop(pTempItemInst); 1652 } 1653 1654 if( pTempItemInst->id == Item::map_Id ) 1655 { 1656 // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong 1657 // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem 1658 vector<shared_ptr<ItemInstance> > items; 1659 for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++) 1660 { 1661 items.push_back(player->containerMenu->slots.at(i)->getItem()); 1662 } 1663 player->refreshContainer(player->containerMenu, &items); 1664 } 1665 else 1666 { 1667 // Do same hack as PlayerConnection::handleContainerClick does - do our broadcast of changes just now, but with a hack so it just thinks it has sent 1668 // things but hasn't really. This will stop the client getting a message back confirming the current inventory items, which might then arrive 1669 // after another local change has been made on the client and be stale. 1670 player->ignoreSlotUpdateHack = true; 1671 player->containerMenu->broadcastChanges(); 1672 player->broadcastCarriedItem(); 1673 player->ignoreSlotUpdateHack = false; 1674 } 1675 } 1676 1677 // handle achievements 1678 switch(pTempItemInst->id ) 1679 { 1680 case Tile::workBench_Id: player->awardStat(GenericStats::buildWorkbench(), GenericStats::param_buildWorkbench()); break; 1681 case Item::pickAxe_wood_Id: player->awardStat(GenericStats::buildPickaxe(), GenericStats::param_buildPickaxe()); break; 1682 case Tile::furnace_Id: player->awardStat(GenericStats::buildFurnace(), GenericStats::param_buildFurnace()); break; 1683 case Item::hoe_wood_Id: player->awardStat(GenericStats::buildHoe(), GenericStats::param_buildHoe()); break; 1684 case Item::bread_Id: player->awardStat(GenericStats::makeBread(), GenericStats::param_makeBread()); break; 1685 case Item::cake_Id: player->awardStat(GenericStats::bakeCake(), GenericStats::param_bakeCake()); break; 1686 case Item::pickAxe_stone_Id: player->awardStat(GenericStats::buildBetterPickaxe(), GenericStats::param_buildBetterPickaxe()); break; 1687 case Item::sword_wood_Id: player->awardStat(GenericStats::buildSword(), GenericStats::param_buildSword()); break; 1688 case Tile::dispenser_Id: player->awardStat(GenericStats::dispenseWithThis(), GenericStats::param_dispenseWithThis()); break; 1689 case Tile::enchantTable_Id: player->awardStat(GenericStats::enchantments(), GenericStats::param_enchantments()); break; 1690 case Tile::bookshelf_Id: player->awardStat(GenericStats::bookcase(), GenericStats::param_bookcase()); break; 1691 } 1692 //} 1693 // ELSE The server thinks the client was wrong... 1694} 1695 1696 1697void PlayerConnection::handleTradeItem(shared_ptr<TradeItemPacket> packet) 1698{ 1699 if (player->containerMenu->containerId == packet->containerId) 1700 { 1701 MerchantMenu *menu = (MerchantMenu *)player->containerMenu; 1702 1703 MerchantRecipeList *offers = menu->getMerchant()->getOffers(player); 1704 1705 if(offers) 1706 { 1707 int selectedShopItem = packet->offer; 1708 if( selectedShopItem < offers->size() ) 1709 { 1710 MerchantRecipe *activeRecipe = offers->at(selectedShopItem); 1711 if(!activeRecipe->isDeprecated()) 1712 { 1713 // Do we have the ingredients? 1714 shared_ptr<ItemInstance> buyAItem = activeRecipe->getBuyAItem(); 1715 shared_ptr<ItemInstance> buyBItem = activeRecipe->getBuyBItem(); 1716 1717 int buyAMatches = player->inventory->countMatches(buyAItem); 1718 int buyBMatches = player->inventory->countMatches(buyBItem); 1719 if( (buyAItem != NULL && buyAMatches >= buyAItem->count) && (buyBItem == NULL || buyBMatches >= buyBItem->count) ) 1720 { 1721 menu->getMerchant()->notifyTrade(activeRecipe); 1722 1723 // Remove the items we are purchasing with 1724 player->inventory->removeResources(buyAItem); 1725 player->inventory->removeResources(buyBItem); 1726 1727 // Add the item we have purchased 1728 shared_ptr<ItemInstance> result = activeRecipe->getSellItem()->copy(); 1729 1730 // 4J JEV - Award itemsBought stat. 1731 player->awardStat( 1732 GenericStats::itemsBought(result->getItem()->id), 1733 GenericStats::param_itemsBought( 1734 result->getItem()->id, 1735 result->getAuxValue(), 1736 result->GetCount() 1737 ) 1738 ); 1739 1740 if (!player->inventory->add(result)) 1741 { 1742 player->drop(result); 1743 } 1744 } 1745 } 1746 } 1747 } 1748 } 1749} 1750 1751INetworkPlayer *PlayerConnection::getNetworkPlayer() 1752{ 1753 if( connection != NULL && connection->getSocket() != NULL) return connection->getSocket()->getPlayer(); 1754 else return NULL; 1755} 1756 1757bool PlayerConnection::isLocal() 1758{ 1759 if( connection->getSocket() == NULL ) 1760 { 1761 return false; 1762 } 1763 else 1764 { 1765 bool isLocal = connection->getSocket()->isLocal(); 1766 return connection->getSocket()->isLocal(); 1767 } 1768} 1769 1770bool PlayerConnection::isGuest() 1771{ 1772 if( connection->getSocket() == NULL ) 1773 { 1774 return false; 1775 } 1776 else 1777 { 1778 INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer(); 1779 bool isGuest = false; 1780 if(networkPlayer != NULL) 1781 { 1782 isGuest = networkPlayer->IsGuest() == TRUE; 1783 } 1784 return isGuest; 1785 } 1786}