diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/PlayerConnection.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/PlayerConnection.cpp')
| -rw-r--r-- | Minecraft.Client/PlayerConnection.cpp | 1717 |
1 files changed, 1717 insertions, 0 deletions
diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp new file mode 100644 index 00000000..f0e538a8 --- /dev/null +++ b/Minecraft.Client/PlayerConnection.cpp @@ -0,0 +1,1717 @@ +#include "stdafx.h" +#include "PlayerConnection.h" +#include "ServerPlayer.h" +#include "ServerLevel.h" +#include "ServerPlayerGameMode.h" +#include "PlayerList.h" +#include "MinecraftServer.h" +#include "..\Minecraft.World\net.minecraft.commands.h" +#include "..\Minecraft.World\net.minecraft.network.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.trading.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h" +#include "..\Minecraft.World\net.minecraft.network.h" +#include "..\Minecraft.World\net.minecraft.world.food.h" +#include "..\Minecraft.World\AABB.h" +#include "..\Minecraft.World\Pos.h" +#include "..\Minecraft.World\SharedConstants.h" +#include "..\Minecraft.World\Socket.h" +#include "..\Minecraft.World\Achievements.h" +#include "..\Minecraft.World\net.minecraft.h" +#include "EntityTracker.h" +#include "ServerConnection.h" +#include "..\Minecraft.World\GenericStats.h" +#include "..\Minecraft.World\JavaMath.h" + +// 4J Added +#include "..\Minecraft.World\net.minecraft.world.item.crafting.h" +#include "Options.h" + +Random PlayerConnection::random; + +PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player) +{ + // 4J - added initialisers + done = false; + tickCount = 0; + aboveGroundTickCount = 0; + xLastOk = yLastOk = zLastOk = 0; + synched = true; + didTick = false; + lastKeepAliveId = 0; + lastKeepAliveTime = 0; + lastKeepAliveTick = 0; + chatSpamTickCount = 0; + dropSpamTickCount = 0; + + this->server = server; + this->connection = connection; + connection->setListener(this); + this->player = player; +// player->connection = this; // 4J - moved out as we can't assign in a ctor + InitializeCriticalSection(&done_cs); + + m_bCloseOnTick = false; + m_bWasKicked = false; + + m_friendsOnlyUGC = false; + m_offlineXUID = INVALID_XUID; + m_onlineXUID = INVALID_XUID; + m_bHasClientTickedOnce = false; + + setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); +} + +PlayerConnection::~PlayerConnection() +{ + delete connection; + DeleteCriticalSection(&done_cs); +} + +void PlayerConnection::tick() +{ + if( done ) return; + + if( m_bCloseOnTick ) + { + disconnect( DisconnectPacket::eDisconnect_Closed ); + return; + } + + didTick = false; + tickCount++; + connection->tick(); + if(done) return; + + if ((tickCount - lastKeepAliveTick) > 20 * 1) + { + lastKeepAliveTick = tickCount; + lastKeepAliveTime = System::nanoTime() / 1000000; + lastKeepAliveId = random.nextInt(); + send( shared_ptr<KeepAlivePacket>( new KeepAlivePacket(lastKeepAliveId) ) ); + } +// if (!didTick) { +// player->doTick(false); +// } + + if (chatSpamTickCount > 0) + { + chatSpamTickCount--; + } + if (dropSpamTickCount > 0) + { + dropSpamTickCount--; + } +} + +void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) +{ + EnterCriticalSection(&done_cs); + if( done ) + { + LeaveCriticalSection(&done_cs); + return; + } + + app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); + player->disconnect(); + + // 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 + server->getPlayers()->removePlayerFromReceiving( player ); + send( shared_ptr<DisconnectPacket>( new DisconnectPacket(reason) )); + connection->sendAndQuit(); + // 4J-PB - removed, since it needs to be localised in the language the client is in + //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); + if(getWasKicked()) + { + server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); + } + else + { + server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); + } + + server->getPlayers()->remove(player); + done = true; + LeaveCriticalSection(&done_cs); +} + +void PlayerConnection::handlePlayerInput(shared_ptr<PlayerInputPacket> packet) +{ + player->setPlayerInput(packet->getXa(), packet->getYa(), packet->isJumping(), packet->isSneaking(), packet->getXRot(), packet->getYRot()); +} + +void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet) +{ + ServerLevel *level = server->getLevel(player->dimension); + + didTick = true; + if(synched) m_bHasClientTickedOnce = true; + + if (player->wonGame) return; + + if (!synched) + { + double yDiff = packet->y - yLastOk; + if (packet->x == xLastOk && yDiff * yDiff < 0.01 && packet->z == zLastOk) + { + synched = true; + } + } + + if (synched) + { + if (player->riding != NULL) + { + + float yRotT = player->yRot; + float xRotT = player->xRot; + player->riding->positionRider(); + double xt = player->x; + double yt = player->y; + double zt = player->z; + double xxa = 0; + double zza = 0; + if (packet->hasRot) + { + yRotT = packet->yRot; + xRotT = packet->xRot; + } + if (packet->hasPos && packet->y == -999 && packet->yView == -999) + { + // CraftBukkit start + if (abs(packet->x) > 1 || abs(packet->z) > 1) + { + //System.err.println(player.name + " was caught trying to crash the server with an invalid position."); +#ifndef _CONTENT_PACKAGE + wprintf(L"%ls was caught trying to crash the server with an invalid position.", player->name.c_str()); +#endif + disconnect(DisconnectPacket::eDisconnect_IllegalPosition);//"Nope!"); + return; + } + // CraftBukkit end + xxa = packet->x; + zza = packet->z; + } + + + player->onGround = packet->onGround; + + player->doTick(false); + player->move(xxa, 0, zza); + player->absMoveTo(xt, yt, zt, yRotT, xRotT); + player->xd = xxa; + player->zd = zza; + if (player->riding != NULL) level->forceTick(player->riding, true); + if (player->riding != NULL) player->riding->positionRider(); + server->getPlayers()->move(player); + xLastOk = player->x; + yLastOk = player->y; + zLastOk = player->z; + ((Level *)level)->tick(player); + + return; + } + + if (player->isSleeping()) + { + player->doTick(false); + player->absMoveTo(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); + ((Level *)level)->tick(player); + return; + } + + double startY = player->y; + xLastOk = player->x; + yLastOk = player->y; + zLastOk = player->z; + + + double xt = player->x; + double yt = player->y; + double zt = player->z; + + float yRotT = player->yRot; + float xRotT = player->xRot; + + if (packet->hasPos && packet->y == -999 && packet->yView == -999) + { + packet->hasPos = false; + } + + if (packet->hasPos) + { + xt = packet->x; + yt = packet->y; + zt = packet->z; + double yd = packet->yView - packet->y; + if (!player->isSleeping() && (yd > 1.65 || yd < 0.1)) + { + disconnect(DisconnectPacket::eDisconnect_IllegalStance); +// logger.warning(player->name + " had an illegal stance: " + yd); + return; + } + if (abs(packet->x) > 32000000 || abs(packet->z) > 32000000) + { + disconnect(DisconnectPacket::eDisconnect_IllegalPosition); + return; + } + } + if (packet->hasRot) + { + yRotT = packet->yRot; + xRotT = packet->xRot; + } + + // 4J Stu Added to stop server player y pos being different than client when flying + if(player->abilities.mayfly || player->isAllowedToFly() ) + { + player->abilities.flying = packet->isFlying; + } + else player->abilities.flying = false; + + player->doTick(false); + player->ySlideOffset = 0; + player->absMoveTo(xLastOk, yLastOk, zLastOk, yRotT, xRotT); + + if (!synched) return; + + double xDist = xt - player->x; + double yDist = yt - player->y; + double zDist = zt - player->z; + + double dist = xDist * xDist + yDist * yDist + zDist * zDist; + + // 4J-PB - removing this one for now + /*if (dist > 100.0f) + { +// logger.warning(player->name + " moved too quickly!"); + disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly); +// System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt); +// teleport(player->x, player->y, player->z, player->yRot, player->xRot); + return; + } + */ + + float r = 1 / 16.0f; + bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); + + if (player->onGround && !packet->onGround && yDist > 0) + { + // assume the player made a jump + player->causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP); + } + + player->move(xDist, yDist, zDist); + + // 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 + // Fix for #87764 - Code: Gameplay: Host cannot move and experiences End World Chunks flickering, while in Splitscreen Mode + // and Fix for #87788 - Code: Gameplay: Client cannot move and experiences End World Chunks flickering, while in Splitscreen Mode + if (!synched) return; + + player->onGround = packet->onGround; + // Since server players don't call travel we check food exhaustion + // here + player->checkMovementStatistiscs(xDist, yDist, zDist); + + double oyDist = yDist; + + xDist = xt - player->x; + yDist = yt - player->y; + + // 4J-PB - line below will always be true! + if (yDist > -0.5 || yDist < 0.5) + { + yDist = 0; + } + zDist = zt - player->z; + dist = xDist * xDist + yDist * yDist + zDist * zDist; + bool fail = false; + if (dist > 0.25 * 0.25 && !player->isSleeping() && !player->gameMode->isCreative() && !player->isAllowedToFly()) + { + fail = true; +// logger.warning(player->name + " moved wrongly!"); +// System.out.println("Got position " + xt + ", " + yt + ", " + zt); +// System.out.println("Expected " + player->x + ", " + player->y + ", " + player->z); +#ifndef _CONTENT_PACKAGE + wprintf(L"%ls moved wrongly!\n",player->name.c_str()); + app.DebugPrintf("Got position %f, %f, %f\n", xt,yt,zt); + app.DebugPrintf("Expected %f, %f, %f\n", player->x, player->y, player->z); +#endif + } + player->absMoveTo(xt, yt, zt, yRotT, xRotT); + + bool newOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); + if (oldOk && (fail || !newOk) && !player->isSleeping()) + { + teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT); + return; + } + AABB *testBox = player->bb->copy()->grow(r, r, r)->expand(0, -0.55, 0); + // && server.level.getCubes(player, testBox).size() == 0 + if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() ) + { + if (oyDist >= (-0.5f / 16.0f)) + { + aboveGroundTickCount++; + if (aboveGroundTickCount > 80) + { +// logger.warning(player->name + " was kicked for floating too long!"); +#ifndef _CONTENT_PACKAGE + wprintf(L"%ls was kicked for floating too long!\n", player->name.c_str()); +#endif + disconnect(DisconnectPacket::eDisconnect_NoFlying); + return; + } + } + } + else + { + aboveGroundTickCount = 0; + } + + player->onGround = packet->onGround; + server->getPlayers()->move(player); + player->doCheckFallDamage(player->y - startY, packet->onGround); + } + +} + +void PlayerConnection::teleport(double x, double y, double z, float yRot, float xRot, bool sendPacket /*= true*/) +{ + synched = false; + xLastOk = x; + yLastOk = y; + zLastOk = z; + player->absMoveTo(x, y, z, yRot, xRot); + // 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 + // This is different to the way that height is sent back to the server, where it represents the bottom of the player bounding volume + if(sendPacket) player->connection->send( shared_ptr<MovePlayerPacket>( new MovePlayerPacket::PosRot(x, y + 1.62f, y, z, yRot, xRot, false, false) ) ); +} + +void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet) +{ + ServerLevel *level = server->getLevel(player->dimension); + + if (packet->action == PlayerActionPacket::DROP_ITEM) + { + player->drop(); + return; + } + else if (packet->action == PlayerActionPacket::RELEASE_USE_ITEM) + { + player->releaseUsingItem(); + return; + } + // 4J Stu - We don't have ops, so just use the levels setting + bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); + bool shouldVerifyLocation = false; + if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) shouldVerifyLocation = true; + if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) shouldVerifyLocation = true; + + int x = packet->x; + int y = packet->y; + int z = packet->z; + if (shouldVerifyLocation) + { + double xDist = player->x - (x + 0.5); + // there is a mismatch between the player's camera and the player's + // position, so add 1.5 blocks + double yDist = player->y - (y + 0.5) + 1.5; + double zDist = player->z - (z + 0.5); + double dist = xDist * xDist + yDist * yDist + zDist * zDist; + if (dist > 6 * 6) + { + return; + } + if (y >= server->getMaxBuildHeight()) + { + return; + } + } + Pos *spawnPos = level->getSharedSpawnPos(); + int xd = (int) Mth::abs((float)(x - spawnPos->x)); + int zd = (int) Mth::abs((float)(z - spawnPos->z)); + delete spawnPos; + if (xd > zd) zd = xd; + if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) + { + if (zd > 16 || canEditSpawn) player->gameMode->startDestroyBlock(x, y, z, packet->face); + else player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); + + } + else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) + { + player->gameMode->stopDestroyBlock(x, y, z); + 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 + if (level->getTile(x, y, z) != 0) player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); + } + else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK) + { + player->gameMode->abortDestroyBlock(x, y, z); + if (level->getTile(x, y, z) != 0) player->connection->send(shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level))); + } + else if (packet->action == PlayerActionPacket::GET_UPDATED_BLOCK) + { + double xDist = player->x - (x + 0.5); + double yDist = player->y - (y + 0.5); + double zDist = player->z - (z + 0.5); + double dist = xDist * xDist + yDist * yDist + zDist * zDist; + if (dist < 16 * 16) + { + player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); + } + } + + // 4J Stu - Don't change the levels state + //level->canEditSpawn = false; + +} + +void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet) +{ + ServerLevel *level = server->getLevel(player->dimension); + shared_ptr<ItemInstance> item = player->inventory->getSelected(); + bool informClient = false; + int x = packet->getX(); + int y = packet->getY(); + int z = packet->getZ(); + int face = packet->getFace(); + + // 4J Stu - We don't have ops, so just use the levels setting + bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); + if (packet->getFace() == 255) + { + if (item == NULL) return; + player->gameMode->useItem(player, level, item); + } + else if ((packet->getY() < server->getMaxBuildHeight() - 1) || (packet->getFace() != Facing::UP && packet->getY() < server->getMaxBuildHeight())) + { + Pos *spawnPos = level->getSharedSpawnPos(); + int xd = (int) Mth::abs((float)(x - spawnPos->x)); + int zd = (int) Mth::abs((float)(z - spawnPos->z)); + delete spawnPos; + if (xd > zd) zd = xd; + if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8) + { + if (zd > 16 || canEditSpawn) + { + player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); + } + } + + informClient = true; + } + else + { + //player->connection->send(shared_ptr<ChatPacket>(new ChatPacket("\u00A77Height limit for building is " + server->maxBuildHeight))); + informClient = true; + } + + if (informClient) + { + + player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); + + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + // 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 + // 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 + // 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 + // 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 + // isn't what it is expecting. + if( level->getTile(x,y,z) != Tile::pistonMovingPiece_Id ) + { + player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); + } + + } + + item = player->inventory->getSelected(); + if (item != NULL && item->count == 0) + { + player->inventory->items[player->inventory->selected] = nullptr; + item = nullptr; + } + + if (item == NULL || item->getUseDuration() == 0) + { + player->ignoreSlotUpdateHack = true; + player->inventory->items[player->inventory->selected] = ItemInstance::clone(player->inventory->items[player->inventory->selected]); + Slot *s = player->containerMenu->getSlotFor(player->inventory, player->inventory->selected); + player->containerMenu->broadcastChanges(); + player->ignoreSlotUpdateHack = false; + + if (!ItemInstance::matches(player->inventory->getSelected(), packet->getItem())) + { + send( shared_ptr<ContainerSetSlotPacket>( new ContainerSetSlotPacket(player->containerMenu->containerId, s->index, player->inventory->getSelected()) ) ); + } + } + + // 4J Stu - Don't change the levels state + //level->canEditSpawn = false; + +} + +void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) +{ + EnterCriticalSection(&done_cs); + if( done ) return; +// logger.info(player.name + " lost connection: " + reason); + // 4J-PB - removed, since it needs to be localised in the language the client is in + //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); + if(getWasKicked()) + { + server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); + } + else + { + server->getPlayers()->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); + } + server->getPlayers()->remove(player); + done = true; + LeaveCriticalSection(&done_cs); +} + +void PlayerConnection::onUnhandledPacket(shared_ptr<Packet> packet) +{ +// logger.warning(getClass() + " wasn't prepared to deal with a " + packet.getClass()); + disconnect(DisconnectPacket::eDisconnect_UnexpectedPacket); +} + +void PlayerConnection::send(shared_ptr<Packet> packet) +{ + if( connection->getSocket() != NULL ) + { + if( !server->getPlayers()->canReceiveAllPackets( player ) ) + { + // Check if we are allowed to send this packet type + if( !Packet::canSendToAnyClient(packet) ) + { + //wprintf(L"Not the systems primary player, so not sending them a packet : %ls / %d\n", player->name.c_str(), packet->getId() ); + return; + } + } + connection->send(packet); + } +} + +// 4J Added +void PlayerConnection::queueSend(shared_ptr<Packet> packet) +{ + if( connection->getSocket() != NULL ) + { + if( !server->getPlayers()->canReceiveAllPackets( player ) ) + { + // Check if we are allowed to send this packet type + if( !Packet::canSendToAnyClient(packet) ) + { + //wprintf(L"Not the systems primary player, so not queueing them a packet : %ls\n", connection->getSocket()->getPlayer()->GetGamertag() ); + return; + } + } + connection->queueSend(packet); + } +} + +void PlayerConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> packet) +{ + if (packet->slot < 0 || packet->slot >= Inventory::getSelectionSize()) + { +// logger.warning(player.name + " tried to set an invalid carried item"); + return; + } + player->inventory->selected = packet->slot; +} + +void PlayerConnection::handleChat(shared_ptr<ChatPacket> packet) +{ + // 4J - TODO +#if 0 + wstring message = packet->message; + if (message.length() > SharedConstants::maxChatLength) + { + disconnect(L"Chat message too long"); + return; + } + message = message.trim(); + for (int i = 0; i < message.length(); i++) + { + if (SharedConstants.acceptableLetters.indexOf(message.charAt(i)) < 0 && (int) message.charAt(i) < 32) + { + disconnect(L"Illegal characters in chat"); + return; + } + } + + if (message.startsWith("/")) + { + handleCommand(message); + } else { + message = "<" + player.name + "> " + message; + logger.info(message); + server.players.broadcastAll(new ChatPacket(message)); + } + chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; + if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) + { + disconnect("disconnect.spam"); + } +#endif +} + +void PlayerConnection::handleCommand(const wstring& message) +{ + // 4J - TODO +#if 0 + server.getCommandDispatcher().performCommand(player, message); +#endif +} + +void PlayerConnection::handleAnimate(shared_ptr<AnimatePacket> packet) +{ + if (packet->action == AnimatePacket::SWING) + { + player->swing(); + } +} + +void PlayerConnection::handlePlayerCommand(shared_ptr<PlayerCommandPacket> packet) +{ + if (packet->action == PlayerCommandPacket::START_SNEAKING) + { + player->setSneaking(true); + } + else if (packet->action == PlayerCommandPacket::STOP_SNEAKING) + { + player->setSneaking(false); + } + else if (packet->action == PlayerCommandPacket::START_SPRINTING) + { + player->setSprinting(true); + } + else if (packet->action == PlayerCommandPacket::STOP_SPRINTING) + { + player->setSprinting(false); + } + else if (packet->action == PlayerCommandPacket::STOP_SLEEPING) + { + player->stopSleepInBed(false, true, true); + synched = false; + } + else if (packet->action == PlayerCommandPacket::START_IDLEANIM) + { + player->setIsIdle(true); + } + else if (packet->action == PlayerCommandPacket::STOP_IDLEANIM) + { + player->setIsIdle(false); + } + +} + +void PlayerConnection::setShowOnMaps(bool bVal) +{ + player->setShowOnMaps(bVal); +} + +void PlayerConnection::handleDisconnect(shared_ptr<DisconnectPacket> packet) +{ + // 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 + server->getPlayers()->removePlayerFromReceiving( player ); + connection->close(DisconnectPacket::eDisconnect_Quitting); +} + +int PlayerConnection::countDelayedPackets() +{ + return connection->countDelayedPackets(); +} + +void PlayerConnection::info(const wstring& string) +{ + // 4J-PB - removed, since it needs to be localised in the language the client is in + //send( shared_ptr<ChatPacket>( new ChatPacket(L"§7" + string) ) ); +} + +void PlayerConnection::warn(const wstring& string) +{ + // 4J-PB - removed, since it needs to be localised in the language the client is in + //send( shared_ptr<ChatPacket>( new ChatPacket(L"§9" + string) ) ); +} + +wstring PlayerConnection::getConsoleName() +{ + return player->name; +} + +void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet) +{ + ServerLevel *level = server->getLevel(player->dimension); + shared_ptr<Entity> target = level->getEntity(packet->target); + + // Fix for #8218 - Gameplay: Attacking zombies from a different level often results in no hits being registered + // 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks + // a ray from head->head, but we may actually be looking at a different part of the entity that can be seen + // even though the ray is blocked. + if (target != NULL) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6) + { + //boole canSee = player->canSee(target); + //double maxDist = 6 * 6; + //if (!canSee) + //{ + // maxDist = 3 * 3; + //} + + //if (player->distanceToSqr(target) < maxDist) + //{ + if (packet->action == InteractPacket::INTERACT) + { + player->interact(target); + } + else if (packet->action == InteractPacket::ATTACK) + { + player->attack(target); + } + //} + } + +} + +bool PlayerConnection::canHandleAsyncPackets() +{ + return true; +} + +void PlayerConnection::handleTexture(shared_ptr<TexturePacket> packet) +{ + // Both PlayerConnection and ClientConnection should handle this mostly the same way + + if(packet->dwBytes==0) + { + // Request for texture +#ifndef _CONTENT_PACKAGE + wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); +#endif + PBYTE pbData=NULL; + DWORD dwBytes=0; + app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes); + + if(dwBytes!=0) + { + send( shared_ptr<TexturePacket>( new TexturePacket(packet->textureName,pbData,dwBytes) ) ); + } + else + { + m_texturesRequested.push_back( packet->textureName ); + } + } + else + { + // Response with texture data +#ifndef _CONTENT_PACKAGE + wprintf(L"Server received custom texture %ls\n",packet->textureName.c_str()); +#endif + app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwBytes); + server->connection->handleTextureReceived(packet->textureName); + } +} + +void PlayerConnection::handleTextureAndGeometry(shared_ptr<TextureAndGeometryPacket> packet) +{ + // Both PlayerConnection and ClientConnection should handle this mostly the same way + + if(packet->dwTextureBytes==0) + { + // Request for texture and geometry +#ifndef _CONTENT_PACKAGE + wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); +#endif + PBYTE pbData=NULL; + DWORD dwTextureBytes=0; + app.GetMemFileDetails(packet->textureName,&pbData,&dwTextureBytes); + DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(packet->textureName); + + if(dwTextureBytes!=0) + { + + if(pDLCSkinFile) + { + if(pDLCSkinFile->getAdditionalBoxesCount()!=0) + { + send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); + } + else + { + send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes) ) ); + } + } + else + { + // we don't have the dlc skin, so retrieve the data from the app store + vector<SKIN_BOX *> *pvSkinBoxes = app.GetAdditionalSkinBoxes(packet->dwSkinID); + unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(packet->dwSkinID); + + send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pvSkinBoxes,uiAnimOverrideBitmask) ) ); + } + } + else + { + m_texturesRequested.push_back( packet->textureName ); + } + } + else + { + // Response with texture and geometry data +#ifndef _CONTENT_PACKAGE + wprintf(L"Server received custom texture %ls and geometry\n",packet->textureName.c_str()); +#endif + app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwTextureBytes); + + // add the geometry to the app list + if(packet->dwBoxC!=0) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Adding skin boxes for skin id %X, box count %d\n",packet->dwSkinID,packet->dwBoxC); +#endif + app.SetAdditionalSkinBoxes(packet->dwSkinID,packet->BoxDataA,packet->dwBoxC); + } + // Add the anim override + app.SetAnimOverrideBitmask(packet->dwSkinID,packet->uiAnimOverrideBitmask); + + player->setCustomSkin(packet->dwSkinID); + + server->connection->handleTextureAndGeometryReceived(packet->textureName); + } +} + +void PlayerConnection::handleTextureReceived(const wstring &textureName) +{ + // This sends the server received texture out to any other players waiting for the data + AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); + if( it != m_texturesRequested.end() ) + { + PBYTE pbData=NULL; + DWORD dwBytes=0; + app.GetMemFileDetails(textureName,&pbData,&dwBytes); + + if(dwBytes!=0) + { + send( shared_ptr<TexturePacket>( new TexturePacket(textureName,pbData,dwBytes) ) ); + m_texturesRequested.erase(it); + } + } +} + +void PlayerConnection::handleTextureAndGeometryReceived(const wstring &textureName) +{ + // This sends the server received texture out to any other players waiting for the data + AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); + if( it != m_texturesRequested.end() ) + { + PBYTE pbData=NULL; + DWORD dwTextureBytes=0; + app.GetMemFileDetails(textureName,&pbData,&dwTextureBytes); + DLCSkinFile *pDLCSkinFile=app.m_dlcManager.getSkinFile(textureName); + + if(dwTextureBytes!=0) + { + if(pDLCSkinFile && (pDLCSkinFile->getAdditionalBoxesCount()!=0)) + { + send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); + } + else + { + // get the data from the app + DWORD dwSkinID = app.getSkinIdFromPath(textureName); + vector<SKIN_BOX *> *pvSkinBoxes = app.GetAdditionalSkinBoxes(dwSkinID); + unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(dwSkinID); + + send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes, pvSkinBoxes, uiAnimOverrideBitmask) ) ); + } + m_texturesRequested.erase(it); + } + } +} + +void PlayerConnection::handleTextureChange(shared_ptr<TextureChangePacket> packet) +{ + switch(packet->action) + { + case TextureChangePacket::e_TextureChange_Skin: + player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); +#ifndef _CONTENT_PACKAGE + wprintf(L"Skin for server player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); +#endif + break; + case TextureChangePacket::e_TextureChange_Cape: + player->setCustomCape( Player::getCapeIdFromPath( packet->path ) ); + //player->customTextureUrl2 = packet->path; +#ifndef _CONTENT_PACKAGE + wprintf(L"Cape for server player %ls has changed to %ls\n", player->name.c_str(), player->customTextureUrl2.c_str() ); +#endif + break; + } + if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) + { + if( server->connection->addPendingTextureRequest(packet->path)) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); +#endif + send(shared_ptr<TexturePacket>( new TexturePacket(packet->path,NULL,0) ) ); + } + } + else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(packet->path,NULL,0); + } + server->getPlayers()->broadcastAll( shared_ptr<TextureChangePacket>( new TextureChangePacket(player,packet->action,packet->path) ), player->dimension ); +} + +void PlayerConnection::handleTextureAndGeometryChange(shared_ptr<TextureAndGeometryChangePacket> packet) +{ + + player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); +#ifndef _CONTENT_PACKAGE + 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() ); +#endif + + + if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) + { + if( server->connection->addPendingTextureRequest(packet->path)) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); +#endif + send(shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->path,NULL,0) ) ); + } + } + else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(packet->path,NULL,0); + + player->setCustomSkin(packet->dwSkinID); + + // If we already have the texture, then we already have the model parts too + //app.SetAdditionalSkinBoxes(packet->dwSkinID,) + //DebugBreak(); + } + server->getPlayers()->broadcastAll( shared_ptr<TextureAndGeometryChangePacket>( new TextureAndGeometryChangePacket(player,packet->path) ), player->dimension ); +} + +void PlayerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChangedPacket> packet) +{ + if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS) + { + // Need to check that this player has permission to change each individual setting? + + INetworkPlayer *networkPlayer = getNetworkPlayer(); + if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) + { + app.SetGameHostOption(eGameHostOption_FireSpreads, app.GetGameHostOption(packet->data,eGameHostOption_FireSpreads)); + app.SetGameHostOption(eGameHostOption_TNT, app.GetGameHostOption(packet->data,eGameHostOption_TNT)); + + server->getPlayers()->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS,app.GetGameHostOption(eGameHostOption_All) ) ) ); + + // Update the QoS data + g_NetworkManager.UpdateAndSetGameSessionData(); + } + } +} + +void PlayerConnection::handleKickPlayer(shared_ptr<KickPlayerPacket> packet) +{ + INetworkPlayer *networkPlayer = getNetworkPlayer(); + if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) + { + server->getPlayers()->kickPlayerByShortId(packet->m_networkSmallId); + } +} + +void PlayerConnection::handleGameCommand(shared_ptr<GameCommandPacket> packet) +{ + MinecraftServer::getInstance()->getCommandDispatcher()->performCommand(player, packet->command, packet->data); +} + +void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packet) +{ + if (packet->action == ClientCommandPacket::PERFORM_RESPAWN) + { + if (player->wonGame) + { + player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true); + } + //else if (player.getLevel().getLevelData().isHardcore()) + //{ + // if (server.isSingleplayer() && player.name.equals(server.getSingleplayerName())) + // { + // player.connection.disconnect("You have died. Game over, man, it's game over!"); + // server.selfDestruct(); + // } + // else + // { + // BanEntry ban = new BanEntry(player.name); + // ban.setReason("Death in Hardcore"); + + // server.getPlayers().getBans().add(ban); + // player.connection.disconnect("You have died. Game over, man, it's game over!"); + // } + //} + else + { + if (player->getHealth() > 0) return; + player = server->getPlayers()->respawn(player, 0, false); + } + } +} + +void PlayerConnection::handleRespawn(shared_ptr<RespawnPacket> packet) +{ +} + +void PlayerConnection::handleContainerClose(shared_ptr<ContainerClosePacket> packet) +{ + player->doCloseContainer(); +} + +#ifndef _CONTENT_PACKAGE +void PlayerConnection::handleContainerSetSlot(shared_ptr<ContainerSetSlotPacket> packet) +{ + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED ) + { + player->inventory->setCarried(packet->item); + } + else + { + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY && packet->slot >= 36 && packet->slot < 36 + 9) + { + shared_ptr<ItemInstance> lastItem = player->inventoryMenu->getSlot(packet->slot)->getItem(); + if (packet->item != NULL) + { + if (lastItem == NULL || lastItem->count < packet->item->count) + { + packet->item->popTime = Inventory::POP_TIME_DURATION; + } + } + player->inventoryMenu->setItem(packet->slot, packet->item); + player->ignoreSlotUpdateHack = true; + player->containerMenu->broadcastChanges(); + player->broadcastCarriedItem(); + player->ignoreSlotUpdateHack = false; + } + else if (packet->containerId == player->containerMenu->containerId) + { + player->containerMenu->setItem(packet->slot, packet->item); + player->ignoreSlotUpdateHack = true; + player->containerMenu->broadcastChanges(); + player->broadcastCarriedItem(); + player->ignoreSlotUpdateHack = false; + } + } +} +#endif + +void PlayerConnection::handleContainerClick(shared_ptr<ContainerClickPacket> packet) +{ + if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) + { + shared_ptr<ItemInstance> clicked = player->containerMenu->clicked(packet->slotNum, packet->buttonNum, packet->quickKey?AbstractContainerMenu::CLICK_QUICK_MOVE:AbstractContainerMenu::CLICK_PICKUP, player); + + if (ItemInstance::matches(packet->item, clicked)) + { + // Yep, you sure did click what you claimed to click! + player->connection->send( shared_ptr<ContainerAckPacket>( new ContainerAckPacket(packet->containerId, packet->uid, true) ) ); + player->ignoreSlotUpdateHack = true; + player->containerMenu->broadcastChanges(); + player->broadcastCarriedItem(); + player->ignoreSlotUpdateHack = false; + } + else + { + // No, you clicked the wrong thing! + expectedAcks[player->containerMenu->containerId] = packet->uid; + player->connection->send( shared_ptr<ContainerAckPacket>( new ContainerAckPacket(packet->containerId, packet->uid, false) ) ); + player->containerMenu->setSynched(player, false); + + vector<shared_ptr<ItemInstance> > items; + for (unsigned int i = 0; i < player->containerMenu->slots->size(); i++) + { + items.push_back(player->containerMenu->slots->at(i)->getItem()); + } + player->refreshContainer(player->containerMenu, &items); + +// player.containerMenu.broadcastChanges(); + } + } + +} + +void PlayerConnection::handleContainerButtonClick(shared_ptr<ContainerButtonClickPacket> packet) +{ + if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) + { + player->containerMenu->clickMenuButton(player, packet->buttonId); + player->containerMenu->broadcastChanges(); + } +} + +void PlayerConnection::handleSetCreativeModeSlot(shared_ptr<SetCreativeModeSlotPacket> packet) +{ + if (player->gameMode->isCreative()) + { + bool drop = packet->slotNum < 0; + shared_ptr<ItemInstance> item = packet->item; + + if(item != NULL && item->id == Item::map_Id) + { + int mapScale = 3; +#ifdef _LARGE_WORLDS + int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); + int centreXC = (int) (Math::round(player->x / scale) * scale); + int centreZC = (int) (Math::round(player->z / scale) * scale); +#else + // 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 + int centreXC = 0; + int centreZC = 0; +#endif + item->setAuxValue( player->level->getAuxValueForMap(player->getXuid(), player->dimension, centreXC, centreZC, mapScale) ); + + shared_ptr<MapItemSavedData> data = MapItem::getSavedData(item->getAuxValue(), player->level); + // 4J Stu - We only have one map per player per dimension, so don't reset the one that they have + // when a new one is created + wchar_t buf[64]; + swprintf(buf,64,L"map_%d", item->getAuxValue()); + std::wstring id = wstring(buf); + if( data == NULL ) + { + data = shared_ptr<MapItemSavedData>( new MapItemSavedData(id) ); + } + player->level->setSavedData(id, (shared_ptr<SavedData> ) data); + + data->scale = mapScale; + // 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 + data->x = centreXC; + data->z = centreZC; + data->dimension = (byte) player->level->dimension->id; + data->setDirty(); + } + + bool validSlot = (packet->slotNum >= InventoryMenu::CRAFT_SLOT_START && packet->slotNum < (InventoryMenu::USE_ROW_SLOT_START + Inventory::getSelectionSize())); + bool validItem = item == NULL || (item->id < Item::items.length && item->id >= 0 && Item::items[item->id] != NULL); + bool validData = item == NULL || (item->getAuxValue() >= 0 && item->count > 0 && item->count <= 64); + + if (validSlot && validItem && validData) + { + if (item == NULL) + { + player->inventoryMenu->setItem(packet->slotNum, nullptr); + } + else + { + player->inventoryMenu->setItem(packet->slotNum, item ); + } + player->inventoryMenu->setSynched(player, true); + // player.slotChanged(player.inventoryMenu, packet.slotNum, player.inventoryMenu.getSlot(packet.slotNum).getItem()); + } + else if (drop && validItem && validData) + { + if (dropSpamTickCount < SharedConstants::TICKS_PER_SECOND * 10) + { + dropSpamTickCount += SharedConstants::TICKS_PER_SECOND; + // drop item + shared_ptr<ItemEntity> dropped = player->drop(item); + if (dropped != NULL) + { + dropped->setShortLifeTime(); + } + } + } + + if( item != NULL && item->id == Item::map_Id ) + { + // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong + // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem + vector<shared_ptr<ItemInstance> > items; + for (unsigned int i = 0; i < player->inventoryMenu->slots->size(); i++) + { + items.push_back(player->inventoryMenu->slots->at(i)->getItem()); + } + player->refreshContainer(player->inventoryMenu, &items); + } + } +} + +void PlayerConnection::handleContainerAck(shared_ptr<ContainerAckPacket> packet) +{ + AUTO_VAR(it, expectedAcks.find(player->containerMenu->containerId)); + + if (it != expectedAcks.end() && packet->uid == it->second && player->containerMenu->containerId == packet->containerId && !player->containerMenu->isSynched(player)) + { + player->containerMenu->setSynched(player, true); + } +} + +void PlayerConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet) +{ + app.DebugPrintf("PlayerConnection::handleSignUpdate\n"); + + ServerLevel *level = server->getLevel(player->dimension); + if (level->hasChunkAt(packet->x, packet->y, packet->z)) + { + shared_ptr<TileEntity> te = level->getTileEntity(packet->x, packet->y, packet->z); + + if (dynamic_pointer_cast<SignTileEntity>(te) != NULL) + { + shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te); + if (!ste->isEditable()) + { + server->warn(L"Player " + player->name + L" just tried to change non-editable sign"); + return; + } + } + + // 4J-JEV: Changed to allow characters to display as a []. + if (dynamic_pointer_cast<SignTileEntity>(te) != NULL) + { + int x = packet->x; + int y = packet->y; + int z = packet->z; + shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te); + for (int i = 0; i < 4; i++) + { + wstring lineText = packet->lines[i].substr(0,15); + ste->SetMessage( i, lineText ); + } + ste->SetVerified(false); + ste->setChanged(); + level->sendTileUpdated(x, y, z); + } + } + +} + +void PlayerConnection::handleKeepAlive(shared_ptr<KeepAlivePacket> packet) +{ + if (packet->id == lastKeepAliveId) + { + int time = (int) (System::nanoTime() / 1000000 - lastKeepAliveTime); + player->latency = (player->latency * 3 + time) / 4; + } +} + +void PlayerConnection::handlePlayerInfo(shared_ptr<PlayerInfoPacket> packet) +{ + // Need to check that this player has permission to change each individual setting? + + INetworkPlayer *networkPlayer = getNetworkPlayer(); + if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator() ) + { + shared_ptr<ServerPlayer> serverPlayer; + // Find the player being edited + for(AUTO_VAR(it, server->getPlayers()->players.begin()); it != server->getPlayers()->players.end(); ++it) + { + shared_ptr<ServerPlayer> checkingPlayer = *it; + if(checkingPlayer->connection->getNetworkPlayer() != NULL && checkingPlayer->connection->getNetworkPlayer()->GetSmallId() == packet->m_networkSmallId) + { + serverPlayer = checkingPlayer; + break; + } + } + + if(serverPlayer != NULL) + { + unsigned int origPrivs = serverPlayer->getAllPlayerGamePrivileges(); + + bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + if(serverPlayer == player) + { + GameType *gameType = Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ? GameType::CREATIVE : GameType::SURVIVAL; + gameType = LevelSettings::validateGameType(gameType->getId()); + if (serverPlayer->gameMode->getGameModeForPlayer() != gameType) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Setting %ls to game mode %d\n", serverPlayer->name.c_str(), gameType); +#endif + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CreativeMode,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ); + serverPlayer->gameMode->setGameModeForPlayer(gameType); + serverPlayer->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, gameType->getId()) )); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"%ls already has game mode %d\n", serverPlayer->name.c_str(), gameType); +#endif + } + if(cheats) + { + // Editing self + bool canBeInvisible = Player::getPlayerGamePrivilege(origPrivs, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; + if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invisible) ); + if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invulnerable) ); + + bool inCreativeMode = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(!inCreativeMode) + { + bool canFly = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleFly); + bool canChangeHunger = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleClassicHunger); + + if(canFly)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanFly) ); + if(canChangeHunger)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_ClassicHunger) ); + } + } + } + else + { + // Editing someone else + if(!trustPlayers && !serverPlayer->connection->getNetworkPlayer()->IsHost()) + { + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotMine) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotBuild) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackPlayers) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackAnimals) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseContainers) ); + } + + if(networkPlayer->IsHost()) + { + if(cheats) + { + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleInvisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleInvisible) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger) ); + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport) ); + } + serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Op) ); + } + } + + server->getPlayers()->broadcastAll( shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket( serverPlayer ) ) ); + } + } +} + +bool PlayerConnection::isServerPacketListener() +{ + return true; +} + +void PlayerConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> playerAbilitiesPacket) +{ + player->abilities.flying = playerAbilitiesPacket->isFlying() && player->abilities.mayfly; +} + +//void handleChatAutoComplete(ChatAutoCompletePacket packet) { +// StringBuilder result = new StringBuilder(); + +// for (String candidate : server.getAutoCompletions(player, packet.getMessage())) { +// if (result.length() > 0) result.append("\0"); + +// result.append(candidate); +// } + +// player.connection.send(new ChatAutoCompletePacket(result.toString())); +//} + +//void handleClientInformation(shared_ptr<ClientInformationPacket> packet) +//{ +// player->updateOptions(packet); +//} + +void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> customPayloadPacket) +{ +#if 0 + if (CustomPayloadPacket.CUSTOM_BOOK_PACKET.equals(customPayloadPacket.identifier)) + { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream input(&bais); + shared_ptr<ItemInstance> sentItem = Packet::readItem(input); + + if (!WritingBookItem.makeSureTagIsValid(sentItem.getTag())) + { + throw new IOException("Invalid book tag!"); + } + + // make sure the sent item is the currently carried item + ItemInstance carried = player.inventory.getSelected(); + if (sentItem != null && sentItem.id == Item.writingBook.id && sentItem.id == carried.id) + { + carried.setTag(sentItem.getTag()); + } + } + else if (CustomPayloadPacket.CUSTOM_BOOK_SIGN_PACKET.equals(customPayloadPacket.identifier)) + { + DataInputStream input = new DataInputStream(new ByteArrayInputStream(customPayloadPacket.data)); + ItemInstance sentItem = Packet.readItem(input); + + if (!WrittenBookItem.makeSureTagIsValid(sentItem.getTag())) + { + throw new IOException("Invalid book tag!"); + } + + // make sure the sent item is the currently carried item + ItemInstance carried = player.inventory.getSelected(); + if (sentItem != null && sentItem.id == Item.writtenBook.id && carried.id == Item.writingBook.id) + { + carried.setTag(sentItem.getTag()); + carried.id = Item.writtenBook.id; + } + } + else +#endif + if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) + { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream input(&bais); + int selection = input.readInt(); + + AbstractContainerMenu *menu = player->containerMenu; + if (dynamic_cast<MerchantMenu *>(menu)) + { + ((MerchantMenu *) menu)->setSelectionHint(selection); + } + } + else if (CustomPayloadPacket::SET_ITEM_NAME_PACKET.compare(customPayloadPacket->identifier) == 0) + { + RepairMenu *menu = dynamic_cast<RepairMenu *>( player->containerMenu); + if (menu) + { + if (customPayloadPacket->data.data == NULL || customPayloadPacket->data.length < 1) + { + menu->setItemName(L""); + } + else + { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream dis(&bais); + wstring name = dis.readUTF(); + if (name.length() <= 30) + { + menu->setItemName(name); + } + } + } + } +} + +// 4J Added + +void PlayerConnection::handleDebugOptions(shared_ptr<DebugOptionsPacket> packet) +{ + //Player player = dynamic_pointer_cast<Player>( player->shared_from_this() ); + player->SetDebugOptions(packet->m_uiVal); +} + +void PlayerConnection::handleCraftItem(shared_ptr<CraftItemPacket> packet) +{ + int iRecipe = packet->recipe; + + if(iRecipe == -1) + return; + + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + shared_ptr<ItemInstance> pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); + + if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<<eDebugSetting_CraftAnything))) + { + pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); + if(player->inventory->add(pTempItemInst)==false ) + { + // no room in inventory, so throw it down + player->drop(pTempItemInst); + } + } + else + { + + + // TODO 4J Stu - Assume at the moment that the client can work this out for us... + //if(pRecipeIngredientsRequired[iRecipe].bCanMake) + //{ + pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); + + // and remove those resources from your inventory + for(int i=0;i<pRecipeIngredientsRequired[iRecipe].iIngC;i++) + { + for(int j=0;j<pRecipeIngredientsRequired[iRecipe].iIngValA[i];j++) + { + shared_ptr<ItemInstance> ingItemInst = nullptr; + // do we need to remove a specific aux value? + if(pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]!=Recipes::ANY_AUX_VALUE) + { + ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i] ); + player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]); + } + else + { + ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i] ); + player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i]); + } + + // 4J Stu - Fix for #13097 - Bug: Milk Buckets are removed when crafting Cake + if (ingItemInst != NULL) + { + if (ingItemInst->getItem()->hasCraftingRemainingItem()) + { + // replace item with remaining result + player->inventory->add( shared_ptr<ItemInstance>( new ItemInstance(ingItemInst->getItem()->getCraftingRemainingItem()) ) ); + } + + } + } + } + + // 4J Stu - Fix for #13119 - We should add the item after we remove the ingredients + if(player->inventory->add(pTempItemInst)==false ) + { + // no room in inventory, so throw it down + player->drop(pTempItemInst); + } + + if( pTempItemInst->id == Item::map_Id ) + { + // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong + // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem + vector<shared_ptr<ItemInstance> > items; + for (unsigned int i = 0; i < player->containerMenu->slots->size(); i++) + { + items.push_back(player->containerMenu->slots->at(i)->getItem()); + } + player->refreshContainer(player->containerMenu, &items); + } + else + { + // 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 + // things but hasn't really. This will stop the client getting a message back confirming the current inventory items, which might then arrive + // after another local change has been made on the client and be stale. + player->ignoreSlotUpdateHack = true; + player->containerMenu->broadcastChanges(); + player->broadcastCarriedItem(); + player->ignoreSlotUpdateHack = false; + } + } + + // handle achievements + switch(pTempItemInst->id ) + { + case Tile::workBench_Id: player->awardStat(GenericStats::buildWorkbench(), GenericStats::param_buildWorkbench()); break; + case Item::pickAxe_wood_Id: player->awardStat(GenericStats::buildPickaxe(), GenericStats::param_buildPickaxe()); break; + case Tile::furnace_Id: player->awardStat(GenericStats::buildFurnace(), GenericStats::param_buildFurnace()); break; + case Item::hoe_wood_Id: player->awardStat(GenericStats::buildHoe(), GenericStats::param_buildHoe()); break; + case Item::bread_Id: player->awardStat(GenericStats::makeBread(), GenericStats::param_makeBread()); break; + case Item::cake_Id: player->awardStat(GenericStats::bakeCake(), GenericStats::param_bakeCake()); break; + case Item::pickAxe_stone_Id: player->awardStat(GenericStats::buildBetterPickaxe(), GenericStats::param_buildBetterPickaxe()); break; + case Item::sword_wood_Id: player->awardStat(GenericStats::buildSword(), GenericStats::param_buildSword()); break; + case Tile::dispenser_Id: player->awardStat(GenericStats::dispenseWithThis(), GenericStats::param_dispenseWithThis()); break; + case Tile::enchantTable_Id: player->awardStat(GenericStats::enchantments(), GenericStats::param_enchantments()); break; + case Tile::bookshelf_Id: player->awardStat(GenericStats::bookcase(), GenericStats::param_bookcase()); break; + } + //} + // ELSE The server thinks the client was wrong... +} + + +void PlayerConnection::handleTradeItem(shared_ptr<TradeItemPacket> packet) +{ + if (player->containerMenu->containerId == packet->containerId) + { + MerchantMenu *menu = (MerchantMenu *)player->containerMenu; + + MerchantRecipeList *offers = menu->getMerchant()->getOffers(player); + + if(offers) + { + int selectedShopItem = packet->offer; + if( selectedShopItem < offers->size() ) + { + MerchantRecipe *activeRecipe = offers->at(selectedShopItem); + if(!activeRecipe->isDeprecated()) + { + // Do we have the ingredients? + shared_ptr<ItemInstance> buyAItem = activeRecipe->getBuyAItem(); + shared_ptr<ItemInstance> buyBItem = activeRecipe->getBuyBItem(); + + int buyAMatches = player->inventory->countMatches(buyAItem); + int buyBMatches = player->inventory->countMatches(buyBItem); + if( (buyAItem != NULL && buyAMatches >= buyAItem->count) && (buyBItem == NULL || buyBMatches >= buyBItem->count) ) + { + menu->getMerchant()->notifyTrade(activeRecipe); + + // Remove the items we are purchasing with + player->inventory->removeResources(buyAItem); + player->inventory->removeResources(buyBItem); + + // Add the item we have purchased + shared_ptr<ItemInstance> result = activeRecipe->getSellItem()->copy(); + + // 4J JEV - Award itemsBought stat. + player->awardStat( + GenericStats::itemsBought(result->getItem()->id), + GenericStats::param_itemsBought( + result->getItem()->id, + result->getAuxValue(), + result->GetCount() + ) + ); + + if (!player->inventory->add(result)) + { + player->drop(result); + } + } + } + } + } + } +} + +INetworkPlayer *PlayerConnection::getNetworkPlayer() +{ + if( connection != NULL && connection->getSocket() != NULL) return connection->getSocket()->getPlayer(); + else return NULL; +} + +bool PlayerConnection::isLocal() +{ + if( connection->getSocket() == NULL ) + { + return false; + } + else + { + bool isLocal = connection->getSocket()->isLocal(); + return connection->getSocket()->isLocal(); + } +} + +bool PlayerConnection::isGuest() +{ + if( connection->getSocket() == NULL ) + { + return false; + } + else + { + INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer(); + bool isGuest = false; + if(networkPlayer != NULL) + { + isGuest = networkPlayer->IsGuest() == TRUE; + } + return isGuest; + } +} |
