the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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}