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 "ClientConnection.h"
3#include "MultiPlayerLevel.h"
4#include "MultiPlayerLocalPlayer.h"
5#include "StatsCounter.h"
6#include "ReceivingLevelScreen.h"
7#include "RemotePlayer.h"
8#include "DisconnectedScreen.h"
9#include "TakeAnimationParticle.h"
10#include "CritParticle.h"
11#include "User.h"
12#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
13#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
14#include "..\Minecraft.World\net.minecraft.stats.h"
15#include "..\Minecraft.World\net.minecraft.world.entity.h"
16#include "..\Minecraft.World\net.minecraft.world.entity.ai.attributes.h"
17#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
18#include "..\Minecraft.World\net.minecraft.world.entity.animal.h"
19#include "..\Minecraft.World\net.minecraft.world.entity.npc.h"
20#include "..\Minecraft.World\net.minecraft.world.entity.item.h"
21#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h"
22#include "..\Minecraft.World\net.minecraft.world.entity.global.h"
23#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h"
24#include "..\Minecraft.World\net.minecraft.world.entity.monster.h"
25#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h"
26#include "..\Minecraft.World\net.minecraft.world.item.h"
27#include "..\Minecraft.World\net.minecraft.world.item.trading.h"
28#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
29#include "..\Minecraft.World\net.minecraft.world.inventory.h"
30#include "..\Minecraft.World\net.minecraft.world.h"
31#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h"
32#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
33#include "..\Minecraft.World\net.minecraft.world.effect.h"
34#include "..\Minecraft.World\net.minecraft.world.food.h"
35#include "..\Minecraft.World\SharedConstants.h"
36#include "..\Minecraft.World\AABB.h"
37#include "..\Minecraft.World\Pos.h"
38#include "..\Minecraft.World\Socket.h"
39#include "Minecraft.h"
40#include "ProgressRenderer.h"
41#include "LevelRenderer.h"
42#include "Options.h"
43#include "MinecraftServer.h"
44#include "ClientConstants.h"
45#include "..\Minecraft.World\SoundTypes.h"
46#include "..\Minecraft.World\BasicTypeContainers.h"
47#include "TexturePackRepository.h"
48#ifdef _XBOX
49#include "Common\XUI\XUI_Scene_Trading.h"
50#else
51#include "Common\UI\UI.h"
52#endif
53#ifdef __PS3__
54#include "PS3/Network/SonyVoiceChat.h"
55#endif
56#include "DLCTexturePack.h"
57
58#ifdef _DURANGO
59#include "..\Minecraft.World\DurangoStats.h"
60#include "..\Minecraft.World\GenericStats.h"
61#endif
62
63ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port)
64{
65 // 4J Stu - No longer used as we use the socket version below.
66 assert(FALSE);
67#if 0
68 // 4J - added initiliasers
69 random = new Random();
70 done = false;
71 level = false;
72 started = false;
73
74 this->minecraft = minecraft;
75
76 Socket *socket;
77 if( gNetworkManager.IsHost() )
78 {
79 socket = new Socket(); // 4J - Local connection
80 }
81 else
82 {
83 socket = new Socket(ip); // 4J - Connection over xrnm - hardcoded IP at present
84 }
85 createdOk = socket->createdOk;
86 if( createdOk )
87 {
88 connection = new Connection(socket, L"Client", this);
89 }
90 else
91 {
92 connection = NULL;
93 delete socket;
94 }
95#endif
96}
97
98ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUserIndex /*= -1*/)
99{
100 // 4J - added initiliasers
101 random = new Random();
102 done = false;
103 level = NULL;
104 started = false;
105 savedDataStorage = new SavedDataStorage(NULL);
106 maxPlayers = 20;
107
108 this->minecraft = minecraft;
109
110 if( iUserIndex < 0 )
111 {
112 m_userIndex = ProfileManager.GetPrimaryPad();
113 }
114 else
115 {
116 m_userIndex = iUserIndex;
117 }
118
119 if( socket == NULL )
120 {
121 socket = new Socket(); // 4J - Local connection
122 }
123
124 createdOk = socket->createdOk;
125 if( createdOk )
126 {
127 connection = new Connection(socket, L"Client", this);
128 }
129 else
130 {
131 connection = NULL;
132 // TODO 4J Stu - This will cause issues since the session player owns the socket
133 //delete socket;
134 }
135
136 deferredEntityLinkPackets = vector<DeferredEntityLinkPacket>();
137}
138
139ClientConnection::~ClientConnection()
140{
141 delete connection;
142 delete random;
143 delete savedDataStorage;
144}
145
146void ClientConnection::tick()
147{
148 if (!done) connection->tick();
149 connection->flush();
150}
151
152INetworkPlayer *ClientConnection::getNetworkPlayer()
153{
154 if( connection != NULL && connection->getSocket() != NULL) return connection->getSocket()->getPlayer();
155 else return NULL;
156}
157
158void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
159{
160 if (done) return;
161
162 PlayerUID OnlineXuid;
163 ProfileManager.GetXUID(m_userIndex,&OnlineXuid,true); // online xuid
164 MOJANG_DATA *pMojangData = NULL;
165
166 if(!g_NetworkManager.IsLocalGame())
167 {
168 pMojangData=app.GetMojangDataForXuid(OnlineXuid);
169 }
170
171 if(!g_NetworkManager.IsHost() )
172 {
173 Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCLoginReceived * 100)/ (eCCConnected));
174 }
175
176 // 4J-PB - load the local player skin (from the global title user storage area) if there is one
177 // the primary player on the host machine won't have a qnet player from the socket
178 INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer();
179 int iUserID=-1;
180
181 if( m_userIndex == ProfileManager.GetPrimaryPad() )
182 {
183 iUserID=m_userIndex;
184
185 TelemetryManager->SetMultiplayerInstanceId(packet->m_multiplayerInstanceId);
186 }
187 else
188 {
189 if(!networkPlayer->IsGuest() && networkPlayer->IsLocal())
190 {
191 // find the pad number of this local player
192 for(int i=0;i<XUSER_MAX_COUNT;i++)
193 {
194 INetworkPlayer *networkLocalPlayer=g_NetworkManager.GetLocalPlayerByUserIndex(i);
195 if(networkLocalPlayer==networkPlayer)
196 {
197 iUserID=i;
198 }
199 }
200 }
201 }
202
203 if(iUserID!=-1)
204 {
205 BYTE *pBuffer=NULL;
206 DWORD dwSize=0;
207 bool bRes;
208
209 // if there's a special skin or cloak for this player, add it in
210 if(pMojangData)
211 {
212 // a skin?
213 if(pMojangData->wchSkin[0]!=0L)
214 {
215 wstring wstr=pMojangData->wchSkin;
216 // check the file is not already in
217 bRes=app.IsFileInMemoryTextures(wstr);
218 if(!bRes)
219 {
220#ifdef _XBOX
221 C4JStorage::ETMSStatus eTMSStatus;
222 eTMSStatus=StorageManager.ReadTMSFile(iUserID,C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic,pMojangData->wchSkin,&pBuffer, &dwSize);
223
224 bRes=(eTMSStatus==C4JStorage::ETMSStatus_Idle);
225#endif
226 }
227
228 if(bRes)
229 {
230 app.AddMemoryTextureFile(wstr,pBuffer,dwSize);
231 }
232 }
233
234 // a cloak?
235 if(pMojangData->wchCape[0]!=0L)
236 {
237 wstring wstr=pMojangData->wchCape;
238 // check the file is not already in
239 bRes=app.IsFileInMemoryTextures(wstr);
240 if(!bRes)
241 {
242#ifdef _XBOX
243 C4JStorage::ETMSStatus eTMSStatus;
244 eTMSStatus=StorageManager.ReadTMSFile(iUserID,C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic,pMojangData->wchCape,&pBuffer, &dwSize);
245 bRes=(eTMSStatus==C4JStorage::ETMSStatus_Idle);
246#endif
247 }
248
249 if(bRes)
250 {
251 app.AddMemoryTextureFile(wstr,pBuffer,dwSize);
252 }
253 }
254 }
255
256 // If we're online, read the banned game list
257 app.ReadBannedList(iUserID);
258 // mark the level as not checked against banned levels - it'll be checked once the level starts
259 app.SetBanListCheck(iUserID,false);
260 }
261
262 if( m_userIndex == ProfileManager.GetPrimaryPad() )
263 {
264 if( app.GetTutorialMode() )
265 {
266 minecraft->gameMode = new FullTutorialMode(ProfileManager.GetPrimaryPad(), minecraft, this);
267 }
268 // check if we're in the trial version
269 else if(ProfileManager.IsFullVersion()==false)
270 {
271 minecraft->gameMode = new TrialMode(ProfileManager.GetPrimaryPad(), minecraft, this);
272 }
273 else
274 {
275 MemSect(13);
276 minecraft->gameMode = new ConsoleGameMode(ProfileManager.GetPrimaryPad(), minecraft, this);
277 MemSect(0);
278 }
279
280
281 Level *dimensionLevel = minecraft->getLevel( packet->dimension );
282 if( dimensionLevel == NULL )
283 {
284 level = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
285
286 // 4J Stu - We want to share the SavedDataStorage between levels
287 int otherDimensionId = packet->dimension == 0 ? -1 : 0;
288 Level *activeLevel = minecraft->getLevel(otherDimensionId);
289 if( activeLevel != NULL )
290 {
291 // Don't need to delete it here as it belongs to a client connection while will delete it when it's done
292 //if( level->savedDataStorage != NULL ) delete level->savedDataStorage;
293 level->savedDataStorage = activeLevel->savedDataStorage;
294 }
295
296 app.DebugPrintf("ClientConnection - DIFFICULTY --- %d\n",packet->difficulty);
297 level->difficulty = packet->difficulty; // 4J Added
298 level->isClientSide = true;
299 minecraft->setLevel(level);
300 }
301
302 minecraft->player->setPlayerIndex( packet->m_playerIndex );
303 minecraft->player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) );
304 minecraft->player->setCustomCape( app.GetPlayerCapeId(m_userIndex) );
305
306
307 minecraft->createPrimaryLocalPlayer(ProfileManager.GetPrimaryPad());
308
309 minecraft->player->dimension = packet->dimension;
310 //minecraft->setScreen(new ReceivingLevelScreen(this));
311 minecraft->player->entityId = packet->clientVersion;
312
313 BYTE networkSmallId = getSocket()->getSmallId();
314 app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges);
315 minecraft->player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
316
317 // Assume all privileges are on, so that the first message we see only indicates things that have been turned off
318 unsigned int startingPrivileges = 0;
319 Player::enableAllPlayerPrivileges(startingPrivileges,true);
320
321 if(networkPlayer->IsHost())
322 {
323 Player::setPlayerGamePrivilege(startingPrivileges, Player::ePlayerGamePrivilege_HOST,1);
324 }
325
326 displayPrivilegeChanges(minecraft->player,startingPrivileges);
327
328 // update the debugoptions
329 app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),app.GetGameSettingsDebugMask(-1,true));
330 }
331 else
332 {
333 // 4J-PB - this isn't the level we want
334 //level = (MultiPlayerLevel *)minecraft->level;
335 level = (MultiPlayerLevel *)minecraft->getLevel( packet->dimension );
336 shared_ptr<Player> player;
337
338 if(level==NULL)
339 {
340 int otherDimensionId = packet->dimension == 0 ? -1 : 0;
341 MultiPlayerLevel *activeLevel = minecraft->getLevel(otherDimensionId);
342
343 if(activeLevel == NULL)
344 {
345 otherDimensionId = packet->dimension == 0 ? 1 : (packet->dimension == -1 ? 1 : -1);
346 activeLevel = minecraft->getLevel(otherDimensionId);
347 }
348
349 MultiPlayerLevel *dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
350
351 dimensionLevel->savedDataStorage = activeLevel->savedDataStorage;
352
353 dimensionLevel->difficulty = packet->difficulty; // 4J Added
354 dimensionLevel->isClientSide = true;
355 level = dimensionLevel;
356 // 4J Stu - At time of writing ProfileManager.GetGamertag() does not always return the correct name,
357 // if sign-ins are turned off while the player signed in. Using the qnetPlayer instead.
358 // need to have a level before create extra local player
359 MultiPlayerLevel *levelpassedin=(MultiPlayerLevel *)level;
360 player = minecraft->createExtraLocalPlayer(m_userIndex, networkPlayer->GetOnlineName(), m_userIndex, packet->dimension, this,levelpassedin);
361
362 // need to have a player before the setlevel
363 shared_ptr<MultiplayerLocalPlayer> lastPlayer = minecraft->player;
364 minecraft->player = minecraft->localplayers[m_userIndex];
365 minecraft->setLevel(level);
366 minecraft->player = lastPlayer;
367 }
368 else
369 {
370 player = minecraft->createExtraLocalPlayer(m_userIndex, networkPlayer->GetOnlineName(), m_userIndex, packet->dimension, this);
371 }
372
373
374 //level->addClientConnection( this );
375 player->dimension = packet->dimension;
376 player->entityId = packet->clientVersion;
377
378 player->setPlayerIndex( packet->m_playerIndex );
379 player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) );
380 player->setCustomCape( app.GetPlayerCapeId(m_userIndex) );
381
382
383 BYTE networkSmallId = getSocket()->getSmallId();
384 app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges);
385 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
386
387 // Assume all privileges are on, so that the first message we see only indicates things that have been turned off
388 unsigned int startingPrivileges = 0;
389 Player::enableAllPlayerPrivileges(startingPrivileges,true);
390
391 displayPrivilegeChanges(minecraft->localplayers[m_userIndex],startingPrivileges);
392 }
393
394 maxPlayers = packet->maxPlayers;
395
396 // need to have a player before the setLocalCreativeMode
397 shared_ptr<MultiplayerLocalPlayer> lastPlayer = minecraft->player;
398 minecraft->player = minecraft->localplayers[m_userIndex];
399 ((MultiPlayerGameMode *)minecraft->localgameModes[m_userIndex])->setLocalMode(GameType::byId(packet->gameType));
400 minecraft->player = lastPlayer;
401
402 // make sure the UI offsets for this player are set correctly
403 if(iUserID!=-1)
404 {
405 ui.UpdateSelectedItemPos(iUserID);
406 }
407
408 TelemetryManager->RecordLevelStart(m_userIndex, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->getLevel(packet->dimension)->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount());
409}
410
411void ClientConnection::handleAddEntity(shared_ptr<AddEntityPacket> packet)
412{
413 double x = packet->x / 32.0;
414 double y = packet->y / 32.0;
415 double z = packet->z / 32.0;
416 shared_ptr<Entity> e;
417 bool setRot = true;
418
419 // 4J-PB - replacing this massive if nest with switch
420 switch(packet->type)
421 {
422 case AddEntityPacket::MINECART:
423 e = Minecart::createMinecart(level, x, y, z, packet->data);
424 break;
425 case AddEntityPacket::FISH_HOOK:
426 {
427 // 4J Stu - Brought forward from 1.4 to be able to drop XP from fishing
428 shared_ptr<Entity> owner = getEntity(packet->data);
429
430 // 4J - check all local players to find match
431 if( owner == NULL )
432 {
433 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
434 {
435 if( minecraft->localplayers[i] )
436 {
437 if( minecraft->localplayers[i]->entityId == packet->data )
438 {
439
440 owner = minecraft->localplayers[i];
441 break;
442 }
443 }
444 }
445 }
446
447 if (owner->instanceof(eTYPE_PLAYER))
448 {
449 shared_ptr<Player> player = dynamic_pointer_cast<Player>(owner);
450 shared_ptr<FishingHook> hook = shared_ptr<FishingHook>( new FishingHook(level, x, y, z, player) );
451 e = hook;
452 // 4J Stu - Move the player->fishing out of the ctor as we cannot reference 'this'
453 player->fishing = hook;
454 }
455 packet->data = 0;
456 }
457 break;
458 case AddEntityPacket::ARROW:
459 e = shared_ptr<Entity>( new Arrow(level, x, y, z) );
460 break;
461 case AddEntityPacket::SNOWBALL:
462 e = shared_ptr<Entity>( new Snowball(level, x, y, z) );
463 break;
464 case AddEntityPacket::ITEM_FRAME:
465 {
466 int ix=(int) x;
467 int iy=(int) y;
468 int iz = (int) z;
469 app.DebugPrintf("ClientConnection ITEM_FRAME xyz %d,%d,%d\n",ix,iy,iz);
470 }
471 e = shared_ptr<Entity>(new ItemFrame(level, (int) x, (int) y, (int) z, packet->data));
472 packet->data = 0;
473 setRot = false;
474 break;
475 case AddEntityPacket::THROWN_ENDERPEARL:
476 e = shared_ptr<Entity>( new ThrownEnderpearl(level, x, y, z) );
477 break;
478 case AddEntityPacket::EYEOFENDERSIGNAL:
479 e = shared_ptr<Entity>( new EyeOfEnderSignal(level, x, y, z) );
480 break;
481 case AddEntityPacket::FIREBALL:
482 e = shared_ptr<Entity>( new LargeFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) );
483 packet->data = 0;
484 break;
485 case AddEntityPacket::SMALL_FIREBALL:
486 e = shared_ptr<Entity>( new SmallFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) );
487 packet->data = 0;
488 break;
489 case AddEntityPacket::DRAGON_FIRE_BALL:
490 e = shared_ptr<Entity>( new DragonFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) );
491 packet->data = 0;
492 break;
493 case AddEntityPacket::EGG:
494 e = shared_ptr<Entity>( new ThrownEgg(level, x, y, z) );
495 break;
496 case AddEntityPacket::THROWN_POTION:
497 e = shared_ptr<Entity>( new ThrownPotion(level, x, y, z, packet->data) );
498 packet->data = 0;
499 break;
500 case AddEntityPacket::THROWN_EXPBOTTLE:
501 e = shared_ptr<Entity>( new ThrownExpBottle(level, x, y, z) );
502 packet->data = 0;
503 break;
504 case AddEntityPacket::BOAT:
505 e = shared_ptr<Entity>( new Boat(level, x, y, z) );
506 break;
507 case AddEntityPacket::PRIMED_TNT:
508 e = shared_ptr<Entity>( new PrimedTnt(level, x, y, z, nullptr) );
509 break;
510 case AddEntityPacket::ENDER_CRYSTAL:
511 e = shared_ptr<Entity>( new EnderCrystal(level, x, y, z) );
512 break;
513 case AddEntityPacket::ITEM:
514 e = shared_ptr<Entity>( new ItemEntity(level, x, y, z) );
515 break;
516 case AddEntityPacket::FALLING:
517 e = shared_ptr<Entity>( new FallingTile(level, x, y, z, packet->data & 0xFFFF, packet->data >> 16) );
518 packet->data = 0;
519 break;
520 case AddEntityPacket::WITHER_SKULL:
521 e = shared_ptr<Entity>(new WitherSkull(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0));
522 packet->data = 0;
523 break;
524 case AddEntityPacket::FIREWORKS:
525 e = shared_ptr<Entity>(new FireworksRocketEntity(level, x, y, z, nullptr));
526 break;
527 case AddEntityPacket::LEASH_KNOT:
528 e = shared_ptr<Entity>(new LeashFenceKnotEntity(level, (int) x, (int) y, (int) z));
529 packet->data = 0;
530 break;
531#ifndef _FINAL_BUILD
532 default:
533 // Not a known entity (?)
534 assert(0);
535#endif
536 }
537
538 /* if (packet->type == AddEntityPacket::MINECART_RIDEABLE) e = shared_ptr<Entity>( new Minecart(level, x, y, z, Minecart::RIDEABLE) );
539 if (packet->type == AddEntityPacket::MINECART_CHEST) e = shared_ptr<Entity>( new Minecart(level, x, y, z, Minecart::CHEST) );
540 if (packet->type == AddEntityPacket::MINECART_FURNACE) e = shared_ptr<Entity>( new Minecart(level, x, y, z, Minecart::FURNACE) );
541 if (packet->type == AddEntityPacket::FISH_HOOK)
542 {
543 // 4J Stu - Brought forward from 1.4 to be able to drop XP from fishing
544 shared_ptr<Entity> owner = getEntity(packet->data);
545
546 // 4J - check all local players to find match
547 if( owner == NULL )
548 {
549 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
550 {
551 if( minecraft->localplayers[i] )
552 {
553 if( minecraft->localplayers[i]->entityId == packet->data )
554 {
555
556 owner = minecraft->localplayers[i];
557 break;
558 }
559 }
560 }
561 }
562 shared_ptr<Player> player = dynamic_pointer_cast<Player>(owner);
563 if (player != NULL)
564 {
565 shared_ptr<FishingHook> hook = shared_ptr<FishingHook>( new FishingHook(level, x, y, z, player) );
566 e = hook;
567 // 4J Stu - Move the player->fishing out of the ctor as we cannot reference 'this'
568 player->fishing = hook;
569 }
570 packet->data = 0;
571 }
572
573 if (packet->type == AddEntityPacket::ARROW) e = shared_ptr<Entity>( new Arrow(level, x, y, z) );
574 if (packet->type == AddEntityPacket::SNOWBALL) e = shared_ptr<Entity>( new Snowball(level, x, y, z) );
575 if (packet->type == AddEntityPacket::THROWN_ENDERPEARL) e = shared_ptr<Entity>( new ThrownEnderpearl(level, x, y, z) );
576 if (packet->type == AddEntityPacket::EYEOFENDERSIGNAL) e = shared_ptr<Entity>( new EyeOfEnderSignal(level, x, y, z) );
577 if (packet->type == AddEntityPacket::FIREBALL)
578 {
579 e = shared_ptr<Entity>( new Fireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) );
580 packet->data = 0;
581 }
582 if (packet->type == AddEntityPacket::SMALL_FIREBALL)
583 {
584 e = shared_ptr<Entity>( new SmallFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) );
585 packet->data = 0;
586 }
587 if (packet->type == AddEntityPacket::EGG) e = shared_ptr<Entity>( new ThrownEgg(level, x, y, z) );
588 if (packet->type == AddEntityPacket::THROWN_POTION)
589 {
590 e = shared_ptr<Entity>( new ThrownPotion(level, x, y, z, packet->data) );
591 packet->data = 0;
592 }
593 if (packet->type == AddEntityPacket::THROWN_EXPBOTTLE)
594 {
595 e = shared_ptr<Entity>( new ThrownExpBottle(level, x, y, z) );
596 packet->data = 0;
597 }
598 if (packet->type == AddEntityPacket::BOAT) e = shared_ptr<Entity>( new Boat(level, x, y, z) );
599 if (packet->type == AddEntityPacket::PRIMED_TNT) e = shared_ptr<Entity>( new PrimedTnt(level, x, y, z) );
600 if (packet->type == AddEntityPacket::ENDER_CRYSTAL) e = shared_ptr<Entity>( new EnderCrystal(level, x, y, z) );
601 if (packet->type == AddEntityPacket::FALLING_SAND) e = shared_ptr<Entity>( new FallingTile(level, x, y, z, Tile::sand->id) );
602 if (packet->type == AddEntityPacket::FALLING_GRAVEL) e = shared_ptr<Entity>( new FallingTile(level, x, y, z, Tile::gravel->id) );
603 if (packet->type == AddEntityPacket::FALLING_EGG) e = shared_ptr<Entity>( new FallingTile(level, x, y, z, Tile::dragonEgg_Id) );
604
605 */
606
607 if (e != NULL)
608 {
609 e->xp = packet->x;
610 e->yp = packet->y;
611 e->zp = packet->z;
612
613 float yRot = packet->yRot * 360 / 256.0f;
614 float xRot = packet->xRot * 360 / 256.0f;
615 e->yRotp = packet->yRot;
616 e->xRotp = packet->xRot;
617
618 if (setRot)
619 {
620 e->yRot = 0.0f;
621 e->xRot = 0.0f;
622 }
623
624 vector<shared_ptr<Entity> > *subEntities = e->getSubEntities();
625 if (subEntities != NULL)
626 {
627 int offs = packet->id - e->entityId;
628 //for (int i = 0; i < subEntities.length; i++)
629 for(AUTO_VAR(it, subEntities->begin()); it != subEntities->end(); ++it)
630 {
631 (*it)->entityId += offs;
632 //subEntities[i].entityId += offs;
633 //System.out.println(subEntities[i].entityId);
634 }
635 }
636
637 if (packet->type == AddEntityPacket::LEASH_KNOT)
638 {
639 // 4J: "Move" leash knot to it's current position, this sets old position (like frame, leash has adjusted position)
640 e->absMoveTo(e->x, e->y, e->z, yRot, xRot);
641 }
642 else if(packet->type == AddEntityPacket::ITEM_FRAME)
643 {
644 // Not doing this move for frame, as the ctor for these objects does some adjustments on the position based on direction to move the object out slightly from what it is attached to, and this just overwrites it
645 }
646 else
647 {
648 // For everything else, set position
649 e->absMoveTo(x, y, z, yRot, xRot);
650 }
651 e->entityId = packet->id;
652 level->putEntity(packet->id, e);
653
654 if (packet->data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0
655 {
656
657 if (packet->type == AddEntityPacket::ARROW)
658 {
659 shared_ptr<Entity> owner = getEntity(packet->data);
660
661 // 4J - check all local players to find match
662 if( owner == NULL )
663 {
664 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
665 {
666 if( minecraft->localplayers[i] )
667 {
668 if( minecraft->localplayers[i]->entityId == packet->data )
669 {
670 owner = minecraft->localplayers[i];
671 break;
672 }
673 }
674 }
675 }
676
677 if ( owner != NULL && owner->instanceof(eTYPE_LIVINGENTITY) )
678 {
679 dynamic_pointer_cast<Arrow>(e)->owner = dynamic_pointer_cast<LivingEntity>(owner);
680 }
681 }
682
683 e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0);
684 }
685
686 // 4J: Check our deferred entity link packets
687 checkDeferredEntityLinkPackets(e->entityId);
688 }
689}
690
691void ClientConnection::handleAddExperienceOrb(shared_ptr<AddExperienceOrbPacket> packet)
692{
693 shared_ptr<Entity> e = shared_ptr<ExperienceOrb>( new ExperienceOrb(level, packet->x / 32.0, packet->y / 32.0, packet->z / 32.0, packet->value) );
694 e->xp = packet->x;
695 e->yp = packet->y;
696 e->zp = packet->z;
697 e->yRot = 0;
698 e->xRot = 0;
699 e->entityId = packet->id;
700 level->putEntity(packet->id, e);
701}
702
703void ClientConnection::handleAddGlobalEntity(shared_ptr<AddGlobalEntityPacket> packet)
704{
705 double x = packet->x / 32.0;
706 double y = packet->y / 32.0;
707 double z = packet->z / 32.0;
708 shared_ptr<Entity> e;// = nullptr;
709 if (packet->type == AddGlobalEntityPacket::LIGHTNING) e = shared_ptr<LightningBolt>( new LightningBolt(level, x, y, z) );
710 if (e != NULL)
711 {
712 e->xp = packet->x;
713 e->yp = packet->y;
714 e->zp = packet->z;
715 e->yRot = 0;
716 e->xRot = 0;
717 e->entityId = packet->id;
718 level->addGlobalEntity(e);
719 }
720}
721
722void ClientConnection::handleAddPainting(shared_ptr<AddPaintingPacket> packet)
723{
724 shared_ptr<Painting> painting = shared_ptr<Painting>( new Painting(level, packet->x, packet->y, packet->z, packet->dir, packet->motive) );
725 level->putEntity(packet->id, painting);
726}
727
728void ClientConnection::handleSetEntityMotion(shared_ptr<SetEntityMotionPacket> packet)
729{
730 shared_ptr<Entity> e = getEntity(packet->id);
731 if (e == NULL) return;
732 e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0);
733}
734
735void ClientConnection::handleSetEntityData(shared_ptr<SetEntityDataPacket> packet)
736{
737 shared_ptr<Entity> e = getEntity(packet->id);
738 if (e != NULL && packet->getUnpackedData() != NULL)
739 {
740 e->getEntityData()->assignValues(packet->getUnpackedData());
741 }
742}
743
744void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
745{
746 // Some remote players could actually be local players that are already added
747 for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
748 {
749 // need to use the XUID here
750 PlayerUID playerXUIDOnline = INVALID_XUID, playerXUIDOffline = INVALID_XUID;
751 ProfileManager.GetXUID(idx,&playerXUIDOnline,true);
752 ProfileManager.GetXUID(idx,&playerXUIDOffline,false);
753 if( (playerXUIDOnline != INVALID_XUID && ProfileManager.AreXUIDSEqual(playerXUIDOnline,packet->xuid) ) ||
754 (playerXUIDOffline != INVALID_XUID && ProfileManager.AreXUIDSEqual(playerXUIDOffline,packet->xuid) ) )
755 {
756 app.DebugPrintf("AddPlayerPacket received with XUID of local player\n");
757 return;
758 }
759 }
760/*#ifdef _WINDOWS64
761 // On Windows64 all XUIDs are INVALID_XUID so the XUID check above never fires.
762 // packet->m_playerIndex is the server-assigned sequential index (set via LoginPacket),
763 // NOT the controller slot — so we must scan all local player slots and match by
764 // their stored server index rather than using it directly as an array subscript.
765 for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
766 {
767 if(minecraft->localplayers[idx] != NULL &&
768 minecraft->localplayers[idx]->getPlayerIndex() == packet->m_playerIndex)
769 {
770 app.DebugPrintf("AddPlayerPacket received for local player (controller %d, server index %d), skipping RemotePlayer creation\n", idx, packet->m_playerIndex);
771 return;
772 }
773 }
774#endif*/
775
776 double x = packet->x / 32.0;
777 double y = packet->y / 32.0;
778 double z = packet->z / 32.0;
779 float yRot = packet->yRot * 360 / 256.0f;
780 float xRot = packet->xRot * 360 / 256.0f;
781 shared_ptr<RemotePlayer> player = shared_ptr<RemotePlayer>( new RemotePlayer(minecraft->level, packet->name) );
782 player->xo = player->xOld = player->xp = packet->x;
783 player->yo = player->yOld = player->yp = packet->y;
784 player->zo = player->zOld = player->zp = packet->z;
785 player->xRotp = packet->xRot;
786 player->yRotp = packet->yRot;
787 player->yHeadRot = packet->yHeadRot * 360 / 256.0f;
788 player->setXuid(packet->xuid);
789
790#ifdef _DURANGO
791 // On Durango request player display name from network manager
792 INetworkPlayer *networkPlayer = g_NetworkManager.GetPlayerByXuid(player->getXuid());
793 if (networkPlayer != NULL) player->m_displayName = networkPlayer->GetDisplayName();
794#else
795 // On all other platforms display name is just gamertag so don't check with the network manager
796 player->m_displayName = player->name;
797#endif
798
799 // printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot);
800
801 int item = packet->carriedItem;
802 if (item == 0)
803 {
804 player->inventory->items[player->inventory->selected] = shared_ptr<ItemInstance>(); // NULL;
805 }
806 else
807 {
808 player->inventory->items[player->inventory->selected] = shared_ptr<ItemInstance>( new ItemInstance(item, 1, 0) );
809 }
810 player->absMoveTo(x, y, z, yRot, xRot);
811
812 player->setPlayerIndex( packet->m_playerIndex );
813 player->setCustomSkin( packet->m_skinId );
814 player->setCustomCape( packet->m_capeId );
815 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges);
816
817 if(!player->customTextureUrl.empty() && player->customTextureUrl.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl))
818 {
819 if( minecraft->addPendingClientTextureRequest(player->customTextureUrl) )
820 {
821 app.DebugPrintf("Client sending TextureAndGeometryPacket to get custom skin %ls for player %ls\n",player->customTextureUrl.c_str(), player->name.c_str());
822
823 send(shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(player->customTextureUrl,NULL,0) ) );
824 }
825 }
826 else if(!player->customTextureUrl.empty() && app.IsFileInMemoryTextures(player->customTextureUrl))
827 {
828 // Update the ref count on the memory texture data
829 app.AddMemoryTextureFile(player->customTextureUrl,NULL,0);
830 }
831
832 app.DebugPrintf("Custom skin for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl.c_str());
833
834 if(!player->customTextureUrl2.empty() && player->customTextureUrl2.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl2))
835 {
836 if( minecraft->addPendingClientTextureRequest(player->customTextureUrl2) )
837 {
838 app.DebugPrintf("Client sending texture packet to get custom cape %ls for player %ls\n",player->customTextureUrl2.c_str(), player->name.c_str());
839 send(shared_ptr<TexturePacket>( new TexturePacket(player->customTextureUrl2,NULL,0) ) );
840 }
841 }
842 else if(!player->customTextureUrl2.empty() && app.IsFileInMemoryTextures(player->customTextureUrl2))
843 {
844 // Update the ref count on the memory texture data
845 app.AddMemoryTextureFile(player->customTextureUrl2,NULL,0);
846 }
847
848 app.DebugPrintf("Custom cape for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl2.c_str());
849
850 level->putEntity(packet->id, player);
851
852 vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData();
853 if (unpackedData != NULL)
854 {
855 player->getEntityData()->assignValues(unpackedData);
856 }
857
858}
859
860void ClientConnection::handleTeleportEntity(shared_ptr<TeleportEntityPacket> packet)
861{
862 shared_ptr<Entity> e = getEntity(packet->id);
863 if (e == NULL) return;
864 e->xp = packet->x;
865 e->yp = packet->y;
866 e->zp = packet->z;
867 double x = e->xp / 32.0;
868 double y = e->yp / 32.0 + 1 / 64.0f;
869 double z = e->zp / 32.0;
870 // 4J - make sure xRot stays within -90 -> 90 range
871 int ixRot = packet->xRot;
872 if( ixRot >= 128 ) ixRot -= 256;
873 float yRot = packet->yRot * 360 / 256.0f;
874 float xRot = ixRot * 360 / 256.0f;
875 e->yRotp = packet->yRot;
876 e->xRotp = ixRot;
877
878// printf("\t\t\t\t%d: Teleport to %d (lerp to %f)\n",packet->id,packet->yRot,yRot);
879 e->lerpTo(x, y, z, yRot, xRot, 3);
880}
881
882void ClientConnection::handleSetCarriedItem(shared_ptr<SetCarriedItemPacket> packet)
883{
884 if (packet->slot >= 0 && packet->slot < Inventory::getSelectionSize()) {
885 Minecraft::GetInstance()->localplayers[m_userIndex].get()->inventory->selected = packet->slot;
886 }
887}
888
889void ClientConnection::handleMoveEntity(shared_ptr<MoveEntityPacket> packet)
890{
891 shared_ptr<Entity> e = getEntity(packet->id);
892 if (e == NULL) return;
893 e->xp += packet->xa;
894 e->yp += packet->ya;
895 e->zp += packet->za;
896 double x = e->xp / 32.0;
897 // 4J - The original code did not add the 1/64.0f like the teleport above did, which caused minecarts to fall through the ground
898 double y = e->yp / 32.0 + 1 / 64.0f;
899 double z = e->zp / 32.0;
900 // 4J - have changed rotation to be relative here too
901 e->yRotp += packet->yRot;
902 e->xRotp += packet->xRot;
903 float yRot = ( e->yRotp * 360 ) / 256.0f;
904 float xRot = ( e->xRotp * 360 ) / 256.0f;
905// float yRot = packet->hasRot ? packet->yRot * 360 / 256.0f : e->yRot;
906// float xRot = packet->hasRot ? packet->xRot * 360 / 256.0f : e->xRot;
907 e->lerpTo(x, y, z, yRot, xRot, 3);
908}
909
910void ClientConnection::handleRotateMob(shared_ptr<RotateHeadPacket> packet)
911{
912 shared_ptr<Entity> e = getEntity(packet->id);
913 if (e == NULL) return;
914 float yHeadRot = packet->yHeadRot * 360 / 256.f;
915 e->setYHeadRot(yHeadRot);
916}
917
918void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> packet)
919{
920 shared_ptr<Entity> e = getEntity(packet->id);
921 if (e == NULL) return;
922 e->xp += packet->xa;
923 e->yp += packet->ya;
924 e->zp += packet->za;
925 double x = e->xp / 32.0;
926 // 4J - The original code did not add the 1/64.0f like the teleport above did, which caused minecarts to fall through the ground
927 double y = e->yp / 32.0 + 1 / 64.0f;
928 double z = e->zp / 32.0;
929 // 4J - have changed rotation to be relative here too
930 e->yRotp += packet->yRot;
931 e->xRotp += packet->xRot;
932 float yRot = ( e->yRotp * 360 ) / 256.0f;
933 float xRot = ( e->xRotp * 360 ) / 256.0f;
934// float yRot = packet->hasRot ? packet->yRot * 360 / 256.0f : e->yRot;
935// float xRot = packet->hasRot ? packet->xRot * 360 / 256.0f : e->xRot;
936 e->lerpTo(x, y, z, yRot, xRot, 3);
937}
938
939void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packet)
940{
941 for (int i = 0; i < packet->ids.length; i++)
942 {
943 level->removeEntity(packet->ids[i]);
944 }
945}
946
947void ClientConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
948{
949 shared_ptr<Player> player = minecraft->localplayers[m_userIndex]; //minecraft->player;
950
951 double x = player->x;
952 double y = player->y;
953 double z = player->z;
954 float yRot = player->yRot;
955 float xRot = player->xRot;
956
957 if (packet->hasPos)
958 {
959 x = packet->x;
960 y = packet->y;
961 z = packet->z;
962 }
963 if (packet->hasRot)
964 {
965 yRot = packet->yRot;
966 xRot = packet->xRot;
967 }
968
969 player->ySlideOffset = 0;
970 player->xd = player->yd = player->zd = 0;
971 player->absMoveTo(x, y, z, yRot, xRot);
972 packet->x = player->x;
973 packet->y = player->bb->y0;
974 packet->z = player->z;
975 packet->yView = player->y;
976 connection->send(packet);
977 if (!started)
978 {
979
980 if(!g_NetworkManager.IsHost() )
981 {
982 Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCConnected * 100)/ (eCCConnected));
983 }
984 player->xo = player->x;
985 player->yo = player->y;
986 player->zo = player->z;
987 // 4J - added setting xOld/yOld/zOld here too, as otherwise at the start of the game we interpolate the player position from the origin to wherever its first position really is
988 player->xOld = player->x;
989 player->yOld = player->y;
990 player->zOld = player->z;
991
992 started = true;
993 minecraft->setScreen(NULL);
994
995 // Fix for #105852 - TU12: Content: Gameplay: Local splitscreen Players are spawned at incorrect places after re-joining previously saved and loaded "Mass Effect World".
996 // Move this check from Minecraft::createExtraLocalPlayer
997 // 4J-PB - can't call this when this function is called from the qnet thread (GetGameStarted will be false)
998 if(app.GetGameStarted())
999 {
1000 ui.CloseUIScenes(m_userIndex);
1001 }
1002 }
1003
1004}
1005
1006// 4J Added
1007void ClientConnection::handleChunkVisibilityArea(shared_ptr<ChunkVisibilityAreaPacket> packet)
1008{
1009 for(int z = packet->m_minZ; z <= packet->m_maxZ; ++z)
1010 for(int x = packet->m_minX; x <= packet->m_maxX; ++x)
1011 level->setChunkVisible(x, z, true);
1012}
1013
1014void ClientConnection::handleChunkVisibility(shared_ptr<ChunkVisibilityPacket> packet)
1015{
1016 level->setChunkVisible(packet->x, packet->z, packet->visible);
1017}
1018
1019void ClientConnection::handleChunkTilesUpdate(shared_ptr<ChunkTilesUpdatePacket> packet)
1020{
1021 // 4J - changed to encode level in packet
1022 MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
1023 if( dimensionLevel )
1024 {
1025 PIXBeginNamedEvent(0,"Handle chunk tiles update");
1026 LevelChunk *lc = dimensionLevel->getChunk(packet->xc, packet->zc);
1027 int xo = packet->xc * 16;
1028 int zo = packet->zc * 16;
1029 // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us
1030 // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water.
1031 // This is quite expensive to do, so only consider unsharing if this tile setting is going to actually
1032 // change something
1033 bool forcedUnshare = false;
1034 for (int i = 0; i < packet->count; i++)
1035 {
1036 int pos = packet->positions[i];
1037 int tile = packet->blocks[i] & 0xff;
1038 int data = packet->data[i];
1039
1040
1041 int x = (pos >> 12) & 15;
1042 int z = (pos >> 8) & 15;
1043 int y = ((pos) & 255);
1044
1045 // If this is going to actually change a tile, we'll need to unshare
1046 int prevTile = lc->getTile(x, y, z);
1047 if( ( tile != prevTile && !forcedUnshare ) )
1048 {
1049 PIXBeginNamedEvent(0,"Chunk data unsharing\n");
1050 dimensionLevel->unshareChunkAt(xo,zo);
1051 PIXEndNamedEvent();
1052 forcedUnshare = true;
1053 }
1054
1055 // 4J - Changes now that lighting is done at the client side of things...
1056 // Note - the java version now calls the doSetTileAndData method from the level here rather than the levelchunk, which ultimately ends up
1057 // calling checkLight for the altered tile. For us this doesn't always work as when sharing tile data between a local server & client, the
1058 // tile might not be considered to be being changed on the client as the server already has changed the shared data, and so the checkLight
1059 // doesn't happen. Hence doing an explicit checkLight here instead.
1060 lc->setTileAndData(x, y, z, tile, data);
1061 dimensionLevel->checkLight(x + xo, y, z + zo);
1062
1063 dimensionLevel->clearResetRegion(x + xo, y, z + zo, x + xo, y, z + zo);
1064
1065 // Don't bother setting this to dirty if it isn't going to visually change - we get a lot of
1066 // water changing from static to dynamic for instance
1067 if(!( ( ( prevTile == Tile::water_Id ) && ( tile == Tile::calmWater_Id ) ) ||
1068 ( ( prevTile == Tile::calmWater_Id ) && ( tile == Tile::water_Id ) ) ||
1069 ( ( prevTile == Tile::lava_Id ) && ( tile == Tile::calmLava_Id ) ) ||
1070 ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::calmLava_Id ) ) ||
1071 ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::lava_Id ) ) ) )
1072 {
1073 dimensionLevel->setTilesDirty(x + xo, y, z + zo, x + xo, y, z + zo);
1074 }
1075
1076 // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray
1077 // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT
1078 dimensionLevel->removeUnusedTileEntitiesInRegion(xo + x, y, zo + z, xo + x+1, y+1, zo + z+1);
1079 }
1080 PIXBeginNamedEvent(0,"Chunk data sharing\n");
1081 dimensionLevel->shareChunkAt(xo,zo); // 4J - added - only shares if chunks are same on server & client
1082 PIXEndNamedEvent();
1083
1084 PIXEndNamedEvent();
1085 }
1086}
1087
1088void ClientConnection::handleBlockRegionUpdate(shared_ptr<BlockRegionUpdatePacket> packet)
1089{
1090 // 4J - changed to encode level in packet
1091 MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
1092 if( dimensionLevel )
1093 {
1094 PIXBeginNamedEvent(0,"Handle block region update");
1095
1096 int y1 = packet->y + packet->ys;
1097 if(packet->bIsFullChunk)
1098 {
1099 y1 = Level::maxBuildHeight;
1100 if(packet->buffer.length > 0)
1101 {
1102 PIXBeginNamedEvent(0, "Reordering to XZY");
1103 LevelChunk::reorderBlocksAndDataToXZY(packet->y, packet->xs, packet->ys, packet->zs, &packet->buffer);
1104 PIXEndNamedEvent();
1105 }
1106 }
1107 PIXBeginNamedEvent(0,"Clear rest region");
1108 dimensionLevel->clearResetRegion(packet->x, packet->y, packet->z, packet->x + packet->xs - 1, y1 - 1, packet->z + packet->zs - 1);
1109 PIXEndNamedEvent();
1110
1111 PIXBeginNamedEvent(0,"setBlocksAndData");
1112 // Only full chunks send lighting information now - added flag to end of this call
1113 dimensionLevel->setBlocksAndData(packet->x, packet->y, packet->z, packet->xs, packet->ys, packet->zs, packet->buffer, packet->bIsFullChunk);
1114 PIXEndNamedEvent();
1115
1116// OutputDebugString("END BRU\n");
1117
1118 PIXBeginNamedEvent(0,"removeUnusedTileEntitiesInRegion");
1119 // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray
1120 // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT
1121 dimensionLevel->removeUnusedTileEntitiesInRegion(packet->x, packet->y, packet->z, packet->x + packet->xs, y1, packet->z + packet->zs );
1122 PIXEndNamedEvent();
1123
1124 // If this is a full packet for a chunk, make sure that the cache now considers that it has data for this chunk - this is used to determine whether to bother
1125 // rendering mobs or not, so we don't have them in crazy positions before the data is there
1126 if( packet->bIsFullChunk )
1127 {
1128 PIXBeginNamedEvent(0,"dateReceivedForChunk");
1129 dimensionLevel->dataReceivedForChunk( packet->x >> 4, packet->z >> 4 );
1130 PIXEndNamedEvent();
1131 }
1132 PIXEndNamedEvent();
1133 }
1134}
1135
1136void ClientConnection::handleTileUpdate(shared_ptr<TileUpdatePacket> packet)
1137{
1138 // 4J added - using a block of 255 to signify that this is a packet for destroying a tile, where we need to inform the level renderer that we are about to do so.
1139 // This is used in creative mode as the point where a tile is first destroyed at the client end of things. Packets formed like this are potentially sent from
1140 // ServerPlayerGameMode::destroyBlock
1141 bool destroyTilePacket = false;
1142 if( packet->block == 255 )
1143 {
1144 packet->block = 0;
1145 destroyTilePacket = true;
1146 }
1147 // 4J - changed to encode level in packet
1148 MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx];
1149 if( dimensionLevel )
1150 {
1151 PIXBeginNamedEvent(0,"Handle tile update");
1152
1153 if( g_NetworkManager.IsHost() )
1154 {
1155 // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us
1156 // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water.
1157 // This is quite expensive to do, so only consider unsharing if this tile setting is going to actually
1158 // change something
1159 int prevTile = dimensionLevel->getTile(packet->x, packet->y, packet->z);
1160 int prevData = dimensionLevel->getData(packet->x, packet->y, packet->z);
1161 if( packet->block != prevTile || packet->data != prevData )
1162 {
1163 PIXBeginNamedEvent(0,"Chunk data unsharing\n");
1164 dimensionLevel->unshareChunkAt(packet->x,packet->z);
1165 PIXEndNamedEvent();
1166 }
1167 }
1168
1169 // 4J - In creative mode, we don't update the tile locally then get it confirmed by the server - the first point that we know we are about to destroy a tile is here. Let
1170 // the rendering side of thing know so we can synchronise collision with async render data upates.
1171 if( destroyTilePacket )
1172 {
1173 minecraft->levelRenderer->destroyedTileManager->destroyingTileAt(dimensionLevel, packet->x, packet->y, packet->z);
1174 }
1175
1176 PIXBeginNamedEvent(0,"Setting data\n");
1177 bool tileWasSet = dimensionLevel->doSetTileAndData(packet->x, packet->y, packet->z, packet->block, packet->data);
1178
1179 PIXEndNamedEvent();
1180
1181 // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray
1182 // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT
1183 dimensionLevel->removeUnusedTileEntitiesInRegion(packet->x, packet->y, packet->z, packet->x+1, packet->y+1, packet->z+1 );
1184
1185 PIXBeginNamedEvent(0,"Sharing data\n");
1186 dimensionLevel->shareChunkAt(packet->x,packet->z); // 4J - added - only shares if chunks are same on server & client
1187 PIXEndNamedEvent();
1188
1189 PIXEndNamedEvent();
1190 }
1191}
1192
1193void ClientConnection::handleDisconnect(shared_ptr<DisconnectPacket> packet)
1194{
1195 connection->close(DisconnectPacket::eDisconnect_Kicked);
1196 done = true;
1197
1198 Minecraft *pMinecraft = Minecraft::GetInstance();
1199 pMinecraft->connectionDisconnected( m_userIndex , packet->reason );
1200 app.SetDisconnectReason( packet->reason );
1201
1202 app.SetAction(m_userIndex,eAppAction_ExitWorld,(void *)TRUE);
1203 //minecraft->setLevel(NULL);
1204 //minecraft->setScreen(new DisconnectedScreen(L"disconnect.disconnected", L"disconnect.genericReason", &packet->reason));
1205
1206}
1207
1208void ClientConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects)
1209{
1210 if (done) return;
1211 done = true;
1212
1213 Minecraft *pMinecraft = Minecraft::GetInstance();
1214 pMinecraft->connectionDisconnected( m_userIndex , reason );
1215
1216 // 4J Stu - TU-1 hotfix
1217 // Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
1218 // In the (now unlikely) event that the host connections times out, allow the player to save their game
1219 if(g_NetworkManager.IsHost() &&
1220 (reason == DisconnectPacket::eDisconnect_TimeOut || reason == DisconnectPacket::eDisconnect_Overflow) &&
1221 m_userIndex == ProfileManager.GetPrimaryPad() &&
1222 !MinecraftServer::saveOnExitAnswered() )
1223 {
1224 UINT uiIDA[1];
1225 uiIDA[0]=IDS_CONFIRM_OK;
1226 ui.RequestErrorMessage(IDS_EXITING_GAME, IDS_GENERIC_ERROR, uiIDA, 1, ProfileManager.GetPrimaryPad(),&ClientConnection::HostDisconnectReturned,NULL);
1227 }
1228 else
1229 {
1230 app.SetAction(m_userIndex,eAppAction_ExitWorld,(void *)TRUE);
1231 }
1232
1233 //minecraft->setLevel(NULL);
1234 //minecraft->setScreen(new DisconnectedScreen(L"disconnect.lost", reason, reasonObjects));
1235}
1236
1237void ClientConnection::sendAndDisconnect(shared_ptr<Packet> packet)
1238{
1239 if (done) return;
1240 connection->send(packet);
1241 connection->sendAndQuit();
1242}
1243
1244void ClientConnection::send(shared_ptr<Packet> packet)
1245{
1246 if (done) return;
1247 connection->send(packet);
1248}
1249
1250void ClientConnection::handleTakeItemEntity(shared_ptr<TakeItemEntityPacket> packet)
1251{
1252 shared_ptr<Entity> from = getEntity(packet->itemId);
1253 shared_ptr<LivingEntity> to = dynamic_pointer_cast<LivingEntity>(getEntity(packet->playerId));
1254
1255 // 4J - the original game could assume that if getEntity didn't find the player, it must be the local player. We
1256 // need to search all local players
1257 bool isLocalPlayer = false;
1258 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
1259 {
1260 if( minecraft->localplayers[i] )
1261 {
1262 if( minecraft->localplayers[i]->entityId == packet->playerId )
1263 {
1264 isLocalPlayer = true;
1265 to = minecraft->localplayers[i];
1266 break;
1267 }
1268 }
1269 }
1270
1271 if (to == NULL)
1272 {
1273 // Don't know if this should ever really happen, but seems safest to try and remove the entity that has been collected even if we can't
1274 // create a particle as we don't know what really collected it
1275 level->removeEntity(packet->itemId);
1276 return;
1277 }
1278
1279 if (from != NULL)
1280 {
1281 // If this is a local player, then we only want to do processing for it if this connection is associated with the player it is for. In
1282 // particular, we don't want to remove the item entity until we are processing it for the right connection, or else we won't have a valid
1283 // "from" reference if we've already removed the item for an earlier processed connection
1284 if( isLocalPlayer )
1285 {
1286 shared_ptr<LocalPlayer> player = dynamic_pointer_cast<LocalPlayer>(to);
1287
1288 // 4J Stu - Fix for #10213 - UI: Local clients cannot progress through the tutorial normally.
1289 // We only send this packet once if many local players can see the event, so make sure we update
1290 // the tutorial for the player that actually picked up the item
1291 int playerPad = player->GetXboxPad();
1292
1293 if( minecraft->localgameModes[playerPad] != NULL )
1294 {
1295 // 4J-PB - add in the XP orb sound
1296 if(from->GetType() == eTYPE_EXPERIENCEORB)
1297 {
1298 float fPitch=((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f;
1299 app.DebugPrintf("XP Orb with pitch %f\n",fPitch);
1300 level->playSound(from, eSoundType_RANDOM_ORB, 0.2f, fPitch);
1301 }
1302 else
1303 {
1304 level->playSound(from, eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f);
1305 }
1306
1307 minecraft->particleEngine->add( shared_ptr<TakeAnimationParticle>( new TakeAnimationParticle(minecraft->level, from, to, -0.5f) ) );
1308 level->removeEntity(packet->itemId);
1309 }
1310 else
1311 {
1312 // Don't know if this should ever really happen, but seems safest to try and remove the entity that has been collected even if it
1313 // somehow isn't an itementity
1314 level->removeEntity(packet->itemId);
1315 }
1316 }
1317 else
1318 {
1319 level->playSound(from, eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f);
1320 minecraft->particleEngine->add( shared_ptr<TakeAnimationParticle>( new TakeAnimationParticle(minecraft->level, from, to, -0.5f) ) );
1321 level->removeEntity(packet->itemId);
1322 }
1323 }
1324
1325}
1326
1327void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
1328{
1329 wstring message;
1330 int iPos;
1331 bool displayOnGui = true;
1332
1333 bool replacePlayer = false;
1334 bool replaceEntitySource = false;
1335 bool replaceItem = false;
1336
1337 wstring playerDisplayName = L"";
1338 wstring sourceDisplayName = L"";
1339
1340 // On platforms other than Xbox One this just sets display name to gamertag
1341 if (packet->m_stringArgs.size() >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]);
1342 if (packet->m_stringArgs.size() >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]);
1343
1344 switch(packet->m_messageType)
1345 {
1346 case ChatPacket::e_ChatBedOccupied:
1347 message = app.GetString(IDS_TILE_BED_OCCUPIED);
1348 break;
1349 case ChatPacket::e_ChatBedNoSleep:
1350 message = app.GetString(IDS_TILE_BED_NO_SLEEP);
1351 break;
1352 case ChatPacket::e_ChatBedNotValid:
1353 message = app.GetString(IDS_TILE_BED_NOT_VALID);
1354 break;
1355 case ChatPacket::e_ChatBedNotSafe:
1356 message = app.GetString(IDS_TILE_BED_NOTSAFE);
1357 break;
1358 case ChatPacket::e_ChatBedPlayerSleep:
1359 message=app.GetString(IDS_TILE_BED_PLAYERSLEEP);
1360 iPos=message.find(L"%s");
1361 message.replace(iPos,2,playerDisplayName);
1362 break;
1363 case ChatPacket::e_ChatBedMeSleep:
1364 message=app.GetString(IDS_TILE_BED_MESLEEP);
1365 break;
1366 case ChatPacket::e_ChatPlayerJoinedGame:
1367 message=app.GetString(IDS_PLAYER_JOINED);
1368 iPos=message.find(L"%s");
1369 message.replace(iPos,2,playerDisplayName);
1370 break;
1371 case ChatPacket::e_ChatPlayerLeftGame:
1372 message=app.GetString(IDS_PLAYER_LEFT);
1373 iPos=message.find(L"%s");
1374 message.replace(iPos,2,playerDisplayName);
1375 break;
1376 case ChatPacket::e_ChatPlayerKickedFromGame:
1377 message=app.GetString(IDS_PLAYER_KICKED);
1378 iPos=message.find(L"%s");
1379 message.replace(iPos,2,playerDisplayName);
1380 break;
1381 case ChatPacket::e_ChatCannotPlaceLava:
1382 displayOnGui = false;
1383 app.SetGlobalXuiAction(eAppAction_DisplayLavaMessage);
1384 break;
1385 case ChatPacket::e_ChatDeathInFire:
1386 message=app.GetString(IDS_DEATH_INFIRE);
1387 replacePlayer = true;
1388 break;
1389 case ChatPacket::e_ChatDeathOnFire:
1390 message=app.GetString(IDS_DEATH_ONFIRE);
1391 replacePlayer = true;
1392 break;
1393 case ChatPacket::e_ChatDeathLava:
1394 message=app.GetString(IDS_DEATH_LAVA);
1395 replacePlayer = true;
1396 break;
1397 case ChatPacket::e_ChatDeathInWall:
1398 message=app.GetString(IDS_DEATH_INWALL);
1399 replacePlayer = true;
1400 break;
1401 case ChatPacket::e_ChatDeathDrown:
1402 message=app.GetString(IDS_DEATH_DROWN);
1403 replacePlayer = true;
1404 break;
1405 case ChatPacket::e_ChatDeathStarve:
1406 message=app.GetString(IDS_DEATH_STARVE);
1407 replacePlayer = true;
1408 break;
1409 case ChatPacket::e_ChatDeathCactus:
1410 message=app.GetString(IDS_DEATH_CACTUS);
1411 replacePlayer = true;
1412 break;
1413 case ChatPacket::e_ChatDeathFall:
1414 message=app.GetString(IDS_DEATH_FALL);
1415 replacePlayer = true;
1416 break;
1417 case ChatPacket::e_ChatDeathOutOfWorld:
1418 message=app.GetString(IDS_DEATH_OUTOFWORLD);
1419 replacePlayer = true;
1420 break;
1421 case ChatPacket::e_ChatDeathGeneric:
1422 message=app.GetString(IDS_DEATH_GENERIC);
1423 replacePlayer = true;
1424 break;
1425 case ChatPacket::e_ChatDeathExplosion:
1426 message=app.GetString(IDS_DEATH_EXPLOSION);
1427 replacePlayer = true;
1428 break;
1429 case ChatPacket::e_ChatDeathMagic:
1430 message=app.GetString(IDS_DEATH_MAGIC);
1431 replacePlayer = true;
1432 break;
1433 case ChatPacket::e_ChatDeathAnvil:
1434 message=app.GetString(IDS_DEATH_FALLING_ANVIL);
1435 replacePlayer = true;
1436 break;
1437 case ChatPacket::e_ChatDeathFallingBlock:
1438 message=app.GetString(IDS_DEATH_FALLING_TILE);
1439 replacePlayer = true;
1440 break;
1441 case ChatPacket::e_ChatDeathDragonBreath:
1442 message=app.GetString(IDS_DEATH_DRAGON_BREATH);
1443 replacePlayer = true;
1444 break;
1445 case ChatPacket::e_ChatDeathMob:
1446 message=app.GetString(IDS_DEATH_MOB);
1447 replacePlayer = true;
1448 replaceEntitySource = true;
1449 break;
1450 case ChatPacket::e_ChatDeathPlayer:
1451 message=app.GetString(IDS_DEATH_PLAYER);
1452 replacePlayer = true;
1453 replaceEntitySource = true;
1454 break;
1455 case ChatPacket::e_ChatDeathArrow:
1456 message=app.GetString(IDS_DEATH_ARROW);
1457 replacePlayer = true;
1458 replaceEntitySource = true;
1459 break;
1460 case ChatPacket::e_ChatDeathFireball:
1461 message=app.GetString(IDS_DEATH_FIREBALL);
1462 replacePlayer = true;
1463 replaceEntitySource = true;
1464 break;
1465 case ChatPacket::e_ChatDeathThrown:
1466 message=app.GetString(IDS_DEATH_THROWN);
1467 replacePlayer = true;
1468 replaceEntitySource = true;
1469 break;
1470 case ChatPacket::e_ChatDeathIndirectMagic:
1471 message=app.GetString(IDS_DEATH_INDIRECT_MAGIC);
1472 replacePlayer = true;
1473 replaceEntitySource = true;
1474 break;
1475 case ChatPacket::e_ChatDeathThorns:
1476 message=app.GetString(IDS_DEATH_THORNS);
1477 replacePlayer = true;
1478 replaceEntitySource = true;
1479 break;
1480
1481
1482 case ChatPacket::e_ChatDeathFellAccidentLadder:
1483 message=app.GetString(IDS_DEATH_FELL_ACCIDENT_LADDER);
1484 replacePlayer = true;
1485 break;
1486 case ChatPacket::e_ChatDeathFellAccidentVines:
1487 message=app.GetString(IDS_DEATH_FELL_ACCIDENT_VINES);
1488 replacePlayer = true;
1489 break;
1490 case ChatPacket::e_ChatDeathFellAccidentWater:
1491 message=app.GetString(IDS_DEATH_FELL_ACCIDENT_WATER);
1492 replacePlayer = true;
1493 break;
1494 case ChatPacket::e_ChatDeathFellAccidentGeneric:
1495 message=app.GetString(IDS_DEATH_FELL_ACCIDENT_GENERIC);
1496 replacePlayer = true;
1497 break;
1498 case ChatPacket::e_ChatDeathFellKiller:
1499 //message=app.GetString(IDS_DEATH_FELL_KILLER);
1500 //replacePlayer = true;
1501 //replaceEntitySource = true;
1502
1503 // 4J Stu - The correct string for here, IDS_DEATH_FELL_KILLER is incorrect. We can't change localisation, so use a different string for now
1504 message=app.GetString(IDS_DEATH_FALL);
1505 replacePlayer = true;
1506 break;
1507 case ChatPacket::e_ChatDeathFellAssist:
1508 message=app.GetString(IDS_DEATH_FELL_ASSIST);
1509 replacePlayer = true;
1510 replaceEntitySource = true;
1511 break;
1512 case ChatPacket::e_ChatDeathFellAssistItem:
1513 message=app.GetString(IDS_DEATH_FELL_ASSIST_ITEM);
1514 replacePlayer = true;
1515 replaceEntitySource = true;
1516 replaceItem = true;
1517 break;
1518 case ChatPacket::e_ChatDeathFellFinish:
1519 message=app.GetString(IDS_DEATH_FELL_FINISH);
1520 replacePlayer = true;
1521 replaceEntitySource = true;
1522 break;
1523 case ChatPacket::e_ChatDeathFellFinishItem:
1524 message=app.GetString(IDS_DEATH_FELL_FINISH_ITEM);
1525 replacePlayer = true;
1526 replaceEntitySource = true;
1527 replaceItem = true;
1528 break;
1529 case ChatPacket::e_ChatDeathInFirePlayer:
1530 message=app.GetString(IDS_DEATH_INFIRE_PLAYER);
1531 replacePlayer = true;
1532 replaceEntitySource = true;
1533 break;
1534 case ChatPacket::e_ChatDeathOnFirePlayer:
1535 message=app.GetString(IDS_DEATH_ONFIRE_PLAYER);
1536 replacePlayer = true;
1537 replaceEntitySource = true;
1538 break;
1539 case ChatPacket::e_ChatDeathLavaPlayer:
1540 message=app.GetString(IDS_DEATH_LAVA_PLAYER);
1541 replacePlayer = true;
1542 replaceEntitySource = true;
1543 break;
1544 case ChatPacket::e_ChatDeathDrownPlayer:
1545 message=app.GetString(IDS_DEATH_DROWN_PLAYER);
1546 replacePlayer = true;
1547 replaceEntitySource = true;
1548 break;
1549 case ChatPacket::e_ChatDeathCactusPlayer:
1550 message=app.GetString(IDS_DEATH_CACTUS_PLAYER);
1551 replacePlayer = true;
1552 replaceEntitySource = true;
1553 break;
1554 case ChatPacket::e_ChatDeathExplosionPlayer:
1555 message=app.GetString(IDS_DEATH_EXPLOSION_PLAYER);
1556 replacePlayer = true;
1557 replaceEntitySource = true;
1558 break;
1559 case ChatPacket::e_ChatDeathWither:
1560 message=app.GetString(IDS_DEATH_WITHER);
1561 replacePlayer = true;
1562 break;
1563 case ChatPacket::e_ChatDeathPlayerItem:
1564 message=app.GetString(IDS_DEATH_PLAYER_ITEM);
1565 replacePlayer = true;
1566 replaceEntitySource = true;
1567 replaceItem = true;
1568 break;
1569 case ChatPacket::e_ChatDeathArrowItem:
1570 message=app.GetString(IDS_DEATH_ARROW_ITEM);
1571 replacePlayer = true;
1572 replaceEntitySource = true;
1573 replaceItem = true;
1574 break;
1575 case ChatPacket::e_ChatDeathFireballItem:
1576 message=app.GetString(IDS_DEATH_FIREBALL_ITEM);
1577 replacePlayer = true;
1578 replaceEntitySource = true;
1579 replaceItem = true;
1580 break;
1581 case ChatPacket::e_ChatDeathThrownItem:
1582 message=app.GetString(IDS_DEATH_THROWN_ITEM);
1583 replacePlayer = true;
1584 replaceEntitySource = true;
1585 replaceItem = true;
1586 break;
1587 case ChatPacket::e_ChatDeathIndirectMagicItem:
1588 message=app.GetString(IDS_DEATH_INDIRECT_MAGIC_ITEM);
1589 replacePlayer = true;
1590 replaceEntitySource = true;
1591 replaceItem = true;
1592 break;
1593
1594 case ChatPacket::e_ChatPlayerEnteredEnd:
1595 message=app.GetString(IDS_PLAYER_ENTERED_END);
1596 iPos=message.find(L"%s");
1597 message.replace(iPos,2,playerDisplayName);
1598 break;
1599 case ChatPacket::e_ChatPlayerLeftEnd:
1600 message=app.GetString(IDS_PLAYER_LEFT_END);
1601 iPos=message.find(L"%s");
1602 message.replace(iPos,2,playerDisplayName);
1603 break;
1604
1605 case ChatPacket::e_ChatPlayerMaxEnemies:
1606 message=app.GetString(IDS_MAX_ENEMIES_SPAWNED);
1607 break;
1608 // Spawn eggs
1609 case ChatPacket::e_ChatPlayerMaxVillagers:
1610 message=app.GetString(IDS_MAX_VILLAGERS_SPAWNED);
1611 break;
1612 case ChatPacket::e_ChatPlayerMaxPigsSheepCows:
1613 message=app.GetString(IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED);
1614 break;
1615 case ChatPacket::e_ChatPlayerMaxChickens:
1616 message=app.GetString(IDS_MAX_CHICKENS_SPAWNED);
1617 break;
1618 case ChatPacket::e_ChatPlayerMaxSquid:
1619 message=app.GetString(IDS_MAX_SQUID_SPAWNED);
1620 break;
1621 case ChatPacket::e_ChatPlayerMaxMooshrooms:
1622 message=app.GetString(IDS_MAX_MOOSHROOMS_SPAWNED);
1623 break;
1624 case ChatPacket::e_ChatPlayerMaxWolves:
1625 message=app.GetString(IDS_MAX_WOLVES_SPAWNED);
1626 break;
1627 case ChatPacket::e_ChatPlayerMaxBats:
1628 message=app.GetString(IDS_MAX_BATS_SPAWNED);
1629 break;
1630
1631 // Breeding
1632 case ChatPacket::e_ChatPlayerMaxBredPigsSheepCows:
1633 message=app.GetString(IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED);
1634 break;
1635 case ChatPacket::e_ChatPlayerMaxBredChickens:
1636 message=app.GetString(IDS_MAX_CHICKENS_BRED);
1637 break;
1638 case ChatPacket::e_ChatPlayerMaxBredMooshrooms:
1639 message=app.GetString(IDS_MAX_MUSHROOMCOWS_BRED);
1640 break;
1641
1642 case ChatPacket::e_ChatPlayerMaxBredWolves:
1643 message=app.GetString(IDS_MAX_WOLVES_BRED);
1644 break;
1645
1646 // can't shear the mooshroom
1647 case ChatPacket::e_ChatPlayerCantShearMooshroom:
1648 message=app.GetString(IDS_CANT_SHEAR_MOOSHROOM);
1649 break;
1650
1651 // Paintings/Item Frames
1652 case ChatPacket::e_ChatPlayerMaxHangingEntities:
1653 message=app.GetString(IDS_MAX_HANGINGENTITIES);
1654 break;
1655 // Enemy spawn eggs in peaceful
1656 case ChatPacket::e_ChatPlayerCantSpawnInPeaceful:
1657 message=app.GetString(IDS_CANT_SPAWN_IN_PEACEFUL);
1658 break;
1659
1660 // Enemy spawn eggs in peaceful
1661 case ChatPacket::e_ChatPlayerMaxBoats:
1662 message=app.GetString(IDS_MAX_BOATS);
1663 break;
1664
1665 case ChatPacket::e_ChatCommandTeleportSuccess:
1666 message=app.GetString(IDS_COMMAND_TELEPORT_SUCCESS);
1667 replacePlayer = true;
1668 if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER)
1669 {
1670 message = replaceAll(message,L"{*DESTINATION*}", sourceDisplayName);
1671 }
1672 else
1673 {
1674 message = replaceAll(message,L"{*DESTINATION*}", app.getEntityName((eINSTANCEOF)packet->m_intArgs[0]));
1675 }
1676 break;
1677 case ChatPacket::e_ChatCommandTeleportMe:
1678 message=app.GetString(IDS_COMMAND_TELEPORT_ME);
1679 replacePlayer = true;
1680 break;
1681 case ChatPacket::e_ChatCommandTeleportToMe:
1682 message=app.GetString(IDS_COMMAND_TELEPORT_TO_ME);
1683 replacePlayer = true;
1684 break;
1685
1686 default:
1687 message = playerDisplayName;
1688 break;
1689 }
1690
1691
1692 if(replacePlayer)
1693 {
1694 message = replaceAll(message,L"{*PLAYER*}",playerDisplayName);
1695 }
1696
1697 if(replaceEntitySource)
1698 {
1699 if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER)
1700 {
1701 message = replaceAll(message,L"{*SOURCE*}", sourceDisplayName);
1702 }
1703 else
1704 {
1705 wstring entityName;
1706
1707 // Check for a custom mob name
1708 if (packet->m_stringArgs.size() >= 2 && !packet->m_stringArgs[1].empty())
1709 {
1710 entityName = packet->m_stringArgs[1];
1711 }
1712 else
1713 {
1714 entityName = app.getEntityName((eINSTANCEOF) packet->m_intArgs[0]);
1715 }
1716
1717 message = replaceAll(message,L"{*SOURCE*}", entityName);
1718 }
1719 }
1720
1721 if (replaceItem)
1722 {
1723 message = replaceAll(message,L"{*ITEM*}", packet->m_stringArgs[2]);
1724 }
1725
1726 // flag that a message is a death message
1727 bool bIsDeathMessage = (packet->m_messageType>=ChatPacket::e_ChatDeathInFire) && (packet->m_messageType<=ChatPacket::e_ChatDeathIndirectMagicItem);
1728
1729 if( displayOnGui ) minecraft->gui->addMessage(message,m_userIndex, bIsDeathMessage);
1730}
1731
1732void ClientConnection::handleAnimate(shared_ptr<AnimatePacket> packet)
1733{
1734 shared_ptr<Entity> e = getEntity(packet->id);
1735 if (e == NULL) return;
1736 if (packet->action == AnimatePacket::SWING)
1737 {
1738 if (e->instanceof(eTYPE_LIVINGENTITY)) dynamic_pointer_cast<LivingEntity>(e)->swing();
1739 }
1740 else if (packet->action == AnimatePacket::HURT)
1741 {
1742 e->animateHurt();
1743 }
1744 else if (packet->action == AnimatePacket::WAKE_UP)
1745 {
1746 if (e->instanceof(eTYPE_PLAYER)) dynamic_pointer_cast<Player>(e)->stopSleepInBed(false, false, false);
1747 }
1748 else if (packet->action == AnimatePacket::RESPAWN)
1749 {
1750 }
1751 else if (packet->action == AnimatePacket::CRITICAL_HIT)
1752 {
1753 shared_ptr<CritParticle> critParticle = shared_ptr<CritParticle>( new CritParticle(minecraft->level, e) );
1754 critParticle->CritParticlePostConstructor();
1755 minecraft->particleEngine->add( critParticle );
1756 }
1757 else if (packet->action == AnimatePacket::MAGIC_CRITICAL_HIT)
1758 {
1759 shared_ptr<CritParticle> critParticle = shared_ptr<CritParticle>( new CritParticle(minecraft->level, e, eParticleType_magicCrit) );
1760 critParticle->CritParticlePostConstructor();
1761 minecraft->particleEngine->add(critParticle);
1762 }
1763 else if ( (packet->action == AnimatePacket::EAT) && e->instanceof(eTYPE_REMOTEPLAYER) )
1764 {
1765
1766 }
1767}
1768
1769void ClientConnection::handleEntityActionAtPosition(shared_ptr<EntityActionAtPositionPacket> packet)
1770{
1771 shared_ptr<Entity> e = getEntity(packet->id);
1772 if (e == NULL) return;
1773 if (packet->action == EntityActionAtPositionPacket::START_SLEEP)
1774 {
1775 shared_ptr<Player> player = dynamic_pointer_cast<Player>(e);
1776 player->startSleepInBed(packet->x, packet->y, packet->z);
1777
1778 if( player == minecraft->localplayers[m_userIndex] )
1779 {
1780 TelemetryManager->RecordEnemyKilledOrOvercome(m_userIndex, 0, player->y, 0, 0, 0, 0, eTelemetryInGame_UseBed);
1781 }
1782 }
1783}
1784
1785void ClientConnection::handlePreLogin(shared_ptr<PreLoginPacket> packet)
1786{
1787// printf("Client: handlePreLogin\n");
1788#if 1
1789 // 4J - Check that we can play with all the players already in the game who have Friends-Only UGC set
1790 BOOL canPlay = TRUE;
1791 BOOL canPlayLocal = TRUE;
1792 BOOL isAtLeastOneFriend = g_NetworkManager.IsHost();
1793 BOOL isFriendsWithHost = TRUE;
1794 BOOL cantPlayContentRestricted = FALSE;
1795
1796 if(!g_NetworkManager.IsHost())
1797 {
1798 // set the game host settings
1799 app.SetGameHostOption(eGameHostOption_All,packet->m_serverSettings);
1800
1801 // 4J-PB - if we go straight in from the menus via an invite, we won't have the DLC info
1802 if(app.GetTMSGlobalFileListRead()==false)
1803 {
1804 app.SetTMSAction(ProfileManager.GetPrimaryPad(),eTMSAction_TMSPP_RetrieveFiles_RunPlayGame);
1805 }
1806 }
1807
1808#ifdef _XBOX
1809 if(!g_NetworkManager.IsHost() && !app.GetGameHostOption(eGameHostOption_FriendsOfFriends))
1810 {
1811 if(m_userIndex == ProfileManager.GetPrimaryPad() )
1812 {
1813 for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx)
1814 {
1815 if(ProfileManager.IsSignedIn(m_userIndex) && ProfileManager.IsGuest(idx))
1816 {
1817 canPlay = FALSE;
1818 isFriendsWithHost = FALSE;
1819 }
1820 else
1821 {
1822 PlayerUID playerXuid = INVALID_XUID;
1823 if( ProfileManager.IsSignedInLive(idx) )
1824 {
1825 ProfileManager.GetXUID(idx,&playerXuid,true);
1826 }
1827 if( playerXuid != INVALID_XUID )
1828 {
1829 // Is this user friends with the host player?
1830 BOOL result;
1831 DWORD error;
1832 error = XUserAreUsersFriends(idx,&packet->m_playerXuids[packet->m_hostIndex],1,&result,NULL);
1833 if(error == ERROR_SUCCESS && result != TRUE)
1834 {
1835 canPlay = FALSE;
1836 isFriendsWithHost = FALSE;
1837 }
1838 }
1839 }
1840 if(!canPlay) break;
1841 }
1842 }
1843 else
1844 {
1845 if(ProfileManager.IsSignedIn(m_userIndex) && ProfileManager.IsGuest(m_userIndex))
1846 {
1847 canPlay = FALSE;
1848 isFriendsWithHost = FALSE;
1849 }
1850 else
1851 {
1852 PlayerUID playerXuid = INVALID_XUID;
1853 if( ProfileManager.IsSignedInLive(m_userIndex) )
1854 {
1855 ProfileManager.GetXUID(m_userIndex,&playerXuid,true);
1856 }
1857 if( playerXuid != INVALID_XUID )
1858 {
1859 // Is this user friends with the host player?
1860 BOOL result;
1861 DWORD error;
1862 error = XUserAreUsersFriends(m_userIndex,&packet->m_playerXuids[packet->m_hostIndex],1,&result,NULL);
1863 if(error == ERROR_SUCCESS && result != TRUE)
1864 {
1865 canPlay = FALSE;
1866 isFriendsWithHost = FALSE;
1867 }
1868 }
1869 }
1870 }
1871 }
1872
1873 if( canPlay )
1874 {
1875 for(DWORD i = 0; i < packet->m_dwPlayerCount; ++i)
1876 {
1877 bool localPlayer = false;
1878 for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx)
1879 {
1880 if( ProfileManager.IsSignedInLive(idx) )
1881 {
1882 // need to use the XUID here
1883 PlayerUID playerXUID = INVALID_XUID;
1884 if( !ProfileManager.IsGuest( idx ) )
1885 {
1886 // Guest don't have an offline XUID as they cannot play offline, so use their online one
1887 ProfileManager.GetXUID(idx,&playerXUID,true);
1888 }
1889 if( ProfileManager.AreXUIDSEqual(playerXUID,packet->m_playerXuids[i]) ) localPlayer = true;
1890 }
1891 else if (ProfileManager.IsSignedIn(idx))
1892 {
1893 // If we aren't signed into live then they have to be a local player
1894 localPlayer = true;
1895 }
1896 }
1897 if(!localPlayer)
1898 {
1899 // First check our own permissions to see if we can play with this player
1900 if(m_userIndex == ProfileManager.GetPrimaryPad() )
1901 {
1902 canPlayLocal = ProfileManager.CanViewPlayerCreatedContent(m_userIndex,false,&packet->m_playerXuids[i],1);
1903
1904 // 4J Stu - Everyone joining needs to have at least one friend in the game
1905 // Local players are implied friends
1906 if( isAtLeastOneFriend != TRUE )
1907 {
1908 BOOL result;
1909 DWORD error;
1910 for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx)
1911 {
1912 if( ProfileManager.IsSignedIn(idx) && !ProfileManager.IsGuest(idx) )
1913 {
1914 error = XUserAreUsersFriends(idx,&packet->m_playerXuids[i],1,&result,NULL);
1915 if(error == ERROR_SUCCESS && result == TRUE) isAtLeastOneFriend = TRUE;
1916 }
1917 }
1918 }
1919 }
1920 else
1921 {
1922 // Friends with the primary player on this system
1923 isAtLeastOneFriend = true;
1924
1925 canPlayLocal = ProfileManager.CanViewPlayerCreatedContent(m_userIndex,true,&packet->m_playerXuids[i],1);
1926 }
1927
1928 // If we can play with them, then check if they can play with us
1929 if( canPlayLocal && ( packet->m_friendsOnlyBits & (1<<i) ) )
1930 {
1931 // If this is the primary pad then check all local players against the list in the packet (this happens when joining the game for the first time)
1932 // If not the primary pad, then just check against this index. It happens on joining at the start, but more importantly players trying to join during the game
1933 bool thisQuadrantOnly = true;
1934 if( m_userIndex == ProfileManager.GetPrimaryPad() ) thisQuadrantOnly = false;
1935
1936 BOOL result;
1937 DWORD error;
1938 for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx)
1939 {
1940 if( (!thisQuadrantOnly || m_userIndex == idx) && ProfileManager.IsSignedIn(idx) && !ProfileManager.IsGuest(idx) )
1941 {
1942 error = XUserAreUsersFriends(idx,&packet->m_playerXuids[i],1,&result,NULL);
1943 if(error == ERROR_SUCCESS) canPlay &= result;
1944 }
1945 if(!canPlay) break;
1946 }
1947 }
1948 if(!canPlay || !canPlayLocal) break;
1949 }
1950 }
1951 }
1952#else
1953 // TODO - handle this kind of things for non-360 platforms
1954 canPlay = TRUE;
1955 canPlayLocal = TRUE;
1956 isAtLeastOneFriend = TRUE;
1957 cantPlayContentRestricted= FALSE;
1958
1959#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__)
1960
1961 if(!g_NetworkManager.IsHost() && !app.GetGameHostOption(eGameHostOption_FriendsOfFriends))
1962 {
1963 bool bChatRestricted=false;
1964
1965 ProfileManager.GetChatAndContentRestrictions(m_userIndex,true,&bChatRestricted,NULL,NULL);
1966
1967 // Chat restricted orbis players can still play online
1968#ifndef __ORBIS__
1969 canPlay = !bChatRestricted;
1970#endif
1971
1972 if(m_userIndex == ProfileManager.GetPrimaryPad() )
1973 {
1974 // Is this user friends with the host player?
1975 bool isFriend = true;
1976 unsigned int friendCount = 0;
1977#ifdef __PS3__
1978 int ret = sceNpBasicGetFriendListEntryCount(&friendCount);
1979#elif defined __PSVITA__
1980 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::FriendsList> friendList;
1981 int ret = -1;
1982 if(!CGameNetworkManager::usingAdhocMode()) // we don't need to be friends in PSN for adhoc mode
1983 {
1984 int ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, false);
1985 if(ret == SCE_TOOLKIT_NP_SUCCESS)
1986 {
1987 if( friendList.hasResult() )
1988 {
1989 friendCount = friendList.get()->size();
1990 }
1991 }
1992 }
1993#else // __ORBIS__
1994
1995 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::FriendsList> friendList;
1996
1997 sce::Toolkit::NP::FriendInfoRequest requestParam;
1998 memset(&requestParam,0,sizeof(requestParam));
1999 requestParam.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_ALL;
2000 requestParam.limit = 0;
2001 requestParam.offset = 0;
2002 requestParam.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());
2003
2004 int ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, &requestParam, false);
2005 if( ret == 0 )
2006 {
2007 if( friendList.hasResult() )
2008 {
2009 friendCount = friendList.get()->size();
2010 }
2011 }
2012#endif
2013 if( ret == 0 )
2014 {
2015 isFriend = false;
2016 SceNpId npid;
2017 for( unsigned int i = 0; i < friendCount; i++ )
2018 {
2019#ifdef __PS3__
2020 ret = sceNpBasicGetFriendListEntry( i, &npid );
2021#else
2022 npid = friendList.get()->at(i).npid;
2023#endif
2024 if( ret == 0 )
2025 {
2026 if(strcmp(npid.handle.data, packet->m_playerXuids[packet->m_hostIndex].getOnlineID()) == 0)
2027 {
2028 isFriend = true;
2029 break;
2030 }
2031 }
2032 }
2033 }
2034
2035 if( !isFriend )
2036 {
2037 canPlay = FALSE;
2038 isFriendsWithHost = FALSE;
2039 }
2040 }
2041 }
2042 // is it an online game, and a player has chat restricted?
2043 else if(!g_NetworkManager.IsLocalGame())
2044 {
2045 // if the player is chat restricted, then they can't play an online game
2046 bool bChatRestricted=false;
2047 bool bContentRestricted=false;
2048
2049 // If this is a pre-login packet for the first player on the machine, then accumulate up these flags for everyone signed in. We can handle exiting the game
2050 // much more cleanly at this point by exiting the level, rather than waiting for a prelogin packet for the other players, when we have to exit the player
2051 // which seems to be very unstable at the point of starting up the game
2052 if(m_userIndex == ProfileManager.GetPrimaryPad())
2053 {
2054 ProfileManager.GetChatAndContentRestrictions(m_userIndex,false,&bChatRestricted,&bContentRestricted,NULL);
2055 }
2056 else
2057 {
2058 ProfileManager.GetChatAndContentRestrictions(m_userIndex,true,&bChatRestricted,&bContentRestricted,NULL);
2059 }
2060
2061 // Chat restricted orbis players can still play online
2062#ifndef __ORBIS__
2063 canPlayLocal = !bChatRestricted;
2064#endif
2065
2066 cantPlayContentRestricted = bContentRestricted ? 1 : 0;
2067 }
2068
2069
2070#endif
2071
2072#ifdef _XBOX_ONE
2073 if(!g_NetworkManager.IsHost() && m_userIndex == ProfileManager.GetPrimaryPad())
2074 {
2075 long long startTime = System::currentTimeMillis();
2076
2077 auto friendsXuids = DQRNetworkManager::GetFriends();
2078
2079 if (app.GetGameHostOption(eGameHostOption_FriendsOfFriends))
2080 {
2081 // Check that the user has at least one friend in the game
2082 isAtLeastOneFriend = false;
2083
2084 for (int i = 0; i < friendsXuids->Size; i++)
2085 {
2086 auto friendsXuid = friendsXuids->GetAt(i);
2087
2088 // Check this friend against each player, if we find them we have at least one friend
2089 for (int j = 0; j < g_NetworkManager.GetPlayerCount(); j++)
2090 {
2091 Platform::String^ xboxUserId = ref new Platform::String(g_NetworkManager.GetPlayerByIndex(j)->GetUID().toString().data());
2092 if (friendsXuid == xboxUserId)
2093 {
2094 isAtLeastOneFriend = true;
2095 break;
2096 }
2097 }
2098 }
2099
2100 app.DebugPrintf("ClientConnection::handlePreLogin: User has at least one friend? %s\n", isAtLeastOneFriend ? "Yes" : "No");
2101 }
2102 else
2103 {
2104 // Check that the user is friends with the host
2105 bool isFriend = false;
2106
2107 Platform::String^ hostXboxUserId = ref new Platform::String(g_NetworkManager.GetHostPlayer()->GetUID().toString().data());
2108
2109 for (int i = 0; i < friendsXuids->Size; i++)
2110 {
2111 if (friendsXuids->GetAt(i) == hostXboxUserId)
2112 {
2113 isFriend = true;
2114 break;
2115 }
2116 }
2117
2118 if( !isFriend )
2119 {
2120 canPlay = FALSE;
2121 isFriendsWithHost = FALSE;
2122 }
2123
2124 app.DebugPrintf("ClientConnection::handlePreLogin: User is friends with the host? %s\n", isFriendsWithHost ? "Yes" : "No");
2125 }
2126
2127 app.DebugPrintf("ClientConnection::handlePreLogin: Friendship checks took %i ms\n", System::currentTimeMillis() - startTime);
2128 }
2129#endif
2130
2131#endif // _XBOX
2132
2133 if(!canPlay || !canPlayLocal || !isAtLeastOneFriend || cantPlayContentRestricted)
2134 {
2135#ifndef __PS3__
2136 DisconnectPacket::eDisconnectReason reason = DisconnectPacket::eDisconnect_NoUGC_Remote;
2137#else
2138 DisconnectPacket::eDisconnectReason reason = DisconnectPacket::eDisconnect_None;
2139#endif
2140 if(m_userIndex == ProfileManager.GetPrimaryPad())
2141 {
2142 if(!isFriendsWithHost) reason = DisconnectPacket::eDisconnect_NotFriendsWithHost;
2143 else if(!isAtLeastOneFriend) reason = DisconnectPacket::eDisconnect_NoFriendsInGame;
2144 else if(!canPlayLocal) reason = DisconnectPacket::eDisconnect_NoUGC_AllLocal;
2145 else if(cantPlayContentRestricted) reason = DisconnectPacket::eDisconnect_ContentRestricted_AllLocal;
2146
2147 app.DebugPrintf("Exiting world on handling Pre-Login packet due UGC privileges: %d\n", reason);
2148 app.SetDisconnectReason( reason );
2149 app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
2150 }
2151 else
2152 {
2153 if(!isFriendsWithHost) reason = DisconnectPacket::eDisconnect_NotFriendsWithHost;
2154 else if(!canPlayLocal) reason = DisconnectPacket::eDisconnect_NoUGC_Single_Local;
2155 else if(cantPlayContentRestricted) reason = DisconnectPacket::eDisconnect_ContentRestricted_Single_Local;
2156
2157 app.DebugPrintf("Exiting player %d on handling Pre-Login packet due UGC privileges: %d\n", m_userIndex, reason);
2158 UINT uiIDA[1];
2159 uiIDA[0]=IDS_CONFIRM_OK;
2160 if(!isFriendsWithHost) ui.RequestErrorMessage( IDS_CANTJOIN_TITLE, IDS_NOTALLOWED_FRIENDSOFFRIENDS, uiIDA,1,m_userIndex);
2161 else ui.RequestErrorMessage( IDS_CANTJOIN_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL, uiIDA,1,m_userIndex);
2162
2163 app.SetDisconnectReason( reason );
2164
2165 // 4J-PB - this locks up on the read and write threads not closing down, because they are trying to lock the incoming critsec when it's already locked by this thread
2166// Minecraft::GetInstance()->connectionDisconnected( m_userIndex , reason );
2167// done = true;
2168// connection->flush();
2169// connection->close(reason);
2170// app.SetAction(m_userIndex,eAppAction_ExitPlayer);
2171
2172 // 4J-PB - doing this instead
2173 app.SetAction(m_userIndex,eAppAction_ExitPlayerPreLogin);
2174 }
2175 }
2176 else
2177 {
2178 // Texture pack handling
2179 // If we have the texture pack for the game, load it
2180 // If we don't then send a packet to the host to request it. We need to send this before the LoginPacket so that it gets handled first,
2181 // as once the LoginPacket is received on the client the game is close to starting
2182 if(m_userIndex == ProfileManager.GetPrimaryPad())
2183 {
2184 Minecraft *pMinecraft = Minecraft::GetInstance();
2185 if( pMinecraft->skins->selectTexturePackById(packet->m_texturePackId) )
2186 {
2187 app.DebugPrintf("Selected texture pack %d from Pre-Login packet\n", packet->m_texturePackId);
2188 }
2189 else
2190 {
2191 app.DebugPrintf("Could not select texture pack %d from Pre-Login packet, requesting from host\n", packet->m_texturePackId);
2192
2193 // 4J-PB - we need to upsell the texture pack to the player
2194#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
2195 app.SetAction(m_userIndex,eAppAction_TexturePackRequired);
2196#endif
2197 // Let the player go into the game, and we'll check that they are using the right texture pack when in
2198 }
2199 }
2200
2201 if(!g_NetworkManager.IsHost() )
2202 {
2203 Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCPreLoginReceived * 100)/ (eCCConnected));
2204 }
2205 // need to use the XUID here
2206 PlayerUID offlineXUID = INVALID_XUID;
2207 PlayerUID onlineXUID = INVALID_XUID;
2208 if( ProfileManager.IsSignedInLive(m_userIndex) )
2209 {
2210 // Guest don't have an offline XUID as they cannot play offline, so use their online one
2211 ProfileManager.GetXUID(m_userIndex,&onlineXUID,true);
2212 }
2213#ifdef __PSVITA__
2214 if(CGameNetworkManager::usingAdhocMode() && onlineXUID.getOnlineID()[0] == 0)
2215 {
2216 // player doesn't have an online UID, set it from the player name
2217 onlineXUID.setForAdhoc();
2218 }
2219#endif
2220
2221 // On PS3, all non-signed in players (even guests) can get a useful offlineXUID
2222#if !(defined __PS3__ || defined _DURANGO )
2223 if( !ProfileManager.IsGuest( m_userIndex ) )
2224#endif
2225 {
2226 // All other players we use their offline XUID so that they can play the game offline
2227 ProfileManager.GetXUID(m_userIndex,&offlineXUID,false);
2228 }
2229 BOOL allAllowed, friendsAllowed;
2230 ProfileManager.AllowedPlayerCreatedContent(m_userIndex,true,&allAllowed,&friendsAllowed);
2231 send( shared_ptr<LoginPacket>( new LoginPacket(minecraft->user->name, SharedConstants::NETWORK_PROTOCOL_VERSION, offlineXUID, onlineXUID, (allAllowed!=TRUE && friendsAllowed==TRUE),
2232 packet->m_ugcPlayersVersion, app.GetPlayerSkinId(m_userIndex), app.GetPlayerCapeId(m_userIndex), ProfileManager.IsGuest( m_userIndex ))));
2233
2234 if(!g_NetworkManager.IsHost() )
2235 {
2236 Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCLoginSent * 100)/ (eCCConnected));
2237 }
2238 }
2239#else
2240 // 4J - removed
2241 if (packet->loginKey.equals("-")) {
2242 send(new LoginPacket(minecraft->user.name, SharedConstants.NETWORK_PROTOCOL_VERSION));
2243 } else {
2244 try {
2245 URL url = new URL("http://www.minecraft->net/game/joinserver.jsp?user=" + minecraft->user.name + "&sessionId=" + minecraft->user.sessionId + "&serverId=" + packet->loginKey);
2246 BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
2247 String msg = br.readLine();
2248 br.close();
2249
2250 if (msg.equalsIgnoreCase("ok")) {
2251 send(new LoginPacket(minecraft->user.name, SharedConstants.NETWORK_PROTOCOL_VERSION));
2252 } else {
2253 connection.close("disconnect.loginFailedInfo", msg);
2254 }
2255 } catch (Exception e) {
2256 e.printStackTrace();
2257 connection.close("disconnect.genericReason", "Internal client error: " + e.toString());
2258 }
2259 }
2260#endif
2261}
2262
2263void ClientConnection::close()
2264{
2265 // If it's already done, then we don't need to do anything here. And in fact trying to do something could cause a crash
2266 if(done) return;
2267 done = true;
2268 connection->flush();
2269 connection->close(DisconnectPacket::eDisconnect_Closed);
2270}
2271
2272void ClientConnection::handleAddMob(shared_ptr<AddMobPacket> packet)
2273{
2274 double x = packet->x / 32.0;
2275 double y = packet->y / 32.0;
2276 double z = packet->z / 32.0;
2277 float yRot = packet->yRot * 360 / 256.0f;
2278 float xRot = packet->xRot * 360 / 256.0f;
2279
2280 shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(EntityIO::newById(packet->type, level));
2281 mob->xp = packet->x;
2282 mob->yp = packet->y;
2283 mob->zp = packet->z;
2284 mob->yHeadRot = packet->yHeadRot * 360 / 256.0f;
2285 mob->yRotp = packet->yRot;
2286 mob->xRotp = packet->xRot;
2287
2288 vector<shared_ptr<Entity> > *subEntities = mob->getSubEntities();
2289 if (subEntities != NULL)
2290 {
2291 int offs = packet->id - mob->entityId;
2292 //for (int i = 0; i < subEntities.length; i++)
2293 for(AUTO_VAR(it, subEntities->begin()); it != subEntities->end(); ++it)
2294 {
2295 //subEntities[i].entityId += offs;
2296 (*it)->entityId += offs;
2297 }
2298 }
2299
2300 mob->entityId = packet->id;
2301
2302// printf("\t\t\t\t%d: Add mob rot %d\n",packet->id,packet->yRot);
2303
2304 mob->absMoveTo(x, y, z, yRot, xRot);
2305 mob->xd = packet->xd / 8000.0f;
2306 mob->yd = packet->yd / 8000.0f;
2307 mob->zd = packet->zd / 8000.0f;
2308 level->putEntity(packet->id, mob);
2309
2310 vector<shared_ptr<SynchedEntityData::DataItem> > *unpackedData = packet->getUnpackedData();
2311 if (unpackedData != NULL)
2312 {
2313 mob->getEntityData()->assignValues(unpackedData);
2314 }
2315
2316 // Fix for #65236 - TU8: Content: Gameplay: Magma Cubes' have strange hit boxes.
2317 // 4J Stu - Slimes have a different BB depending on their size which is set in the entity data, so update the BB
2318 if(mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME)
2319 {
2320 shared_ptr<Slime> slime = dynamic_pointer_cast<Slime>(mob);
2321 slime->setSize( slime->getSize() );
2322 }
2323}
2324
2325void ClientConnection::handleSetTime(shared_ptr<SetTimePacket> packet)
2326{
2327 minecraft->level->setGameTime(packet->gameTime);
2328 minecraft->level->setDayTime(packet->dayTime);
2329}
2330
2331void ClientConnection::handleSetSpawn(shared_ptr<SetSpawnPositionPacket> packet)
2332{
2333 //minecraft->player->setRespawnPosition(new Pos(packet->x, packet->y, packet->z));
2334 minecraft->localplayers[m_userIndex]->setRespawnPosition(new Pos(packet->x, packet->y, packet->z), true);
2335 minecraft->level->getLevelData()->setSpawn(packet->x, packet->y, packet->z);
2336
2337}
2338
2339void ClientConnection::handleEntityLinkPacket(shared_ptr<SetEntityLinkPacket> packet)
2340{
2341 shared_ptr<Entity> sourceEntity = getEntity(packet->sourceId);
2342 shared_ptr<Entity> destEntity = getEntity(packet->destId);
2343
2344 // 4J: If the destination entity couldn't be found, defer handling of this packet
2345 // This was added to support leashing (the entity link packet is sent before the add entity packet)
2346 if (destEntity == NULL && packet->destId >= 0)
2347 {
2348 // We don't handle missing source entities because it shouldn't happen
2349 assert(!(sourceEntity == NULL && packet->sourceId >= 0));
2350
2351 deferredEntityLinkPackets.push_back(DeferredEntityLinkPacket(packet));
2352 return;
2353 }
2354
2355 if (packet->type == SetEntityLinkPacket::RIDING)
2356 {
2357 bool displayMountMessage = false;
2358 if (packet->sourceId == Minecraft::GetInstance()->localplayers[m_userIndex].get()->entityId)
2359 {
2360 sourceEntity = Minecraft::GetInstance()->localplayers[m_userIndex];
2361
2362 if (destEntity != NULL && destEntity->instanceof(eTYPE_BOAT)) (dynamic_pointer_cast<Boat>(destEntity))->setDoLerp(false);
2363
2364 displayMountMessage = (sourceEntity->riding == NULL && destEntity != NULL);
2365 }
2366 else if (destEntity != NULL && destEntity->instanceof(eTYPE_BOAT))
2367 {
2368 (dynamic_pointer_cast<Boat>(destEntity))->setDoLerp(true);
2369 }
2370
2371 if (sourceEntity == NULL) return;
2372
2373 sourceEntity->ride(destEntity);
2374
2375 // 4J TODO: pretty sure this message is a tooltip so not needed
2376 /*
2377 if (displayMountMessage) {
2378 Options options = minecraft.options;
2379 minecraft.gui.setOverlayMessage(I18n.get("mount.onboard", Options.getTranslatedKeyMessage(options.keySneak.key)), false);
2380 }
2381 */
2382 }
2383 else if (packet->type == SetEntityLinkPacket::LEASH)
2384 {
2385 if ( (sourceEntity != NULL) && sourceEntity->instanceof(eTYPE_MOB) )
2386 {
2387 if (destEntity != NULL)
2388 {
2389
2390 (dynamic_pointer_cast<Mob>(sourceEntity))->setLeashedTo(destEntity, false);
2391 }
2392 else
2393 {
2394 (dynamic_pointer_cast<Mob>(sourceEntity))->dropLeash(false, false);
2395 }
2396 }
2397 }
2398}
2399
2400void ClientConnection::handleEntityEvent(shared_ptr<EntityEventPacket> packet)
2401{
2402 shared_ptr<Entity> e = getEntity(packet->entityId);
2403 if (e != NULL) e->handleEntityEvent(packet->eventId);
2404}
2405
2406shared_ptr<Entity> ClientConnection::getEntity(int entityId)
2407{
2408 //if (entityId == minecraft->player->entityId)
2409 if(entityId == minecraft->localplayers[m_userIndex]->entityId)
2410 {
2411 //return minecraft->player;
2412 return minecraft->localplayers[m_userIndex];
2413 }
2414 return level->getEntity(entityId);
2415}
2416
2417void ClientConnection::handleSetHealth(shared_ptr<SetHealthPacket> packet)
2418{
2419 //minecraft->player->hurtTo(packet->health);
2420 minecraft->localplayers[m_userIndex]->hurtTo(packet->health,packet->damageSource);
2421 minecraft->localplayers[m_userIndex]->getFoodData()->setFoodLevel(packet->food);
2422 minecraft->localplayers[m_userIndex]->getFoodData()->setSaturation(packet->saturation);
2423
2424 // We need food
2425 if(packet->food < FoodConstants::HEAL_LEVEL - 1)
2426 {
2427 if(minecraft->localgameModes[m_userIndex] != NULL && !minecraft->localgameModes[m_userIndex]->hasInfiniteItems() )
2428 {
2429 minecraft->localgameModes[m_userIndex]->getTutorial()->changeTutorialState(e_Tutorial_State_Food_Bar);
2430 }
2431 }
2432}
2433
2434void ClientConnection::handleSetExperience(shared_ptr<SetExperiencePacket> packet)
2435{
2436 minecraft->localplayers[m_userIndex]->setExperienceValues(packet->experienceProgress, packet->totalExperience, packet->experienceLevel);
2437}
2438
2439void ClientConnection::handleTexture(shared_ptr<TexturePacket> packet)
2440{
2441 // Both PlayerConnection and ClientConnection should handle this mostly the same way
2442 // Server side also needs to store a list of those clients waiting to get a texture the server doesn't have yet
2443 // so that it can send it out to them when it comes in
2444
2445 if(packet->dwBytes==0)
2446 {
2447 // Request for texture
2448#ifndef _CONTENT_PACKAGE
2449 wprintf(L"Client received request for custom texture %ls\n",packet->textureName.c_str());
2450#endif
2451 PBYTE pbData=NULL;
2452 DWORD dwBytes=0;
2453 app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes);
2454
2455 if(dwBytes!=0)
2456 {
2457 send( shared_ptr<TexturePacket>( new TexturePacket(packet->textureName,pbData,dwBytes) ) );
2458 }
2459 }
2460 else
2461 {
2462 // Response with texture data
2463#ifndef _CONTENT_PACKAGE
2464 wprintf(L"Client received custom texture %ls\n",packet->textureName.c_str());
2465#endif
2466 app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwBytes);
2467 Minecraft::GetInstance()->handleClientTextureReceived(packet->textureName);
2468 }
2469}
2470
2471void ClientConnection::handleTextureAndGeometry(shared_ptr<TextureAndGeometryPacket> packet)
2472{
2473 // Both PlayerConnection and ClientConnection should handle this mostly the same way
2474 // Server side also needs to store a list of those clients waiting to get a texture the server doesn't have yet
2475 // so that it can send it out to them when it comes in
2476
2477 if(packet->dwTextureBytes==0)
2478 {
2479 // Request for texture
2480#ifndef _CONTENT_PACKAGE
2481 wprintf(L"Client received request for custom texture and geometry %ls\n",packet->textureName.c_str());
2482#endif
2483 PBYTE pbData=NULL;
2484 DWORD dwBytes=0;
2485 app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes);
2486 DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(packet->textureName);
2487
2488 if(dwBytes!=0)
2489 {
2490 if(pDLCSkinFile)
2491 {
2492 if(pDLCSkinFile->getAdditionalBoxesCount()!=0)
2493 {
2494 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes,pDLCSkinFile) ) );
2495 }
2496 else
2497 {
2498 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes) ) );
2499 }
2500 }
2501 else
2502 {
2503 unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(packet->dwSkinID);
2504
2505 send( shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes,app.GetAdditionalSkinBoxes(packet->dwSkinID),uiAnimOverrideBitmask) ) );
2506 }
2507 }
2508 }
2509 else
2510 {
2511 // Response with texture data
2512#ifndef _CONTENT_PACKAGE
2513 wprintf(L"Client received custom TextureAndGeometry %ls\n",packet->textureName.c_str());
2514#endif
2515 // Add the texture data
2516 app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwTextureBytes);
2517 // Add the geometry data
2518 if(packet->dwBoxC!=0)
2519 {
2520 app.SetAdditionalSkinBoxes(packet->dwSkinID,packet->BoxDataA,packet->dwBoxC);
2521 }
2522 // Add the anim override
2523 app.SetAnimOverrideBitmask(packet->dwSkinID,packet->uiAnimOverrideBitmask);
2524
2525 // clear out the pending texture request
2526 Minecraft::GetInstance()->handleClientTextureReceived(packet->textureName);
2527 }
2528}
2529
2530void ClientConnection::handleTextureChange(shared_ptr<TextureChangePacket> packet)
2531{
2532 shared_ptr<Entity> e = getEntity(packet->id);
2533 if ( (e == NULL) || !e->instanceof(eTYPE_PLAYER) ) return;
2534 shared_ptr<Player> player = dynamic_pointer_cast<Player>(e);
2535
2536 bool isLocalPlayer = false;
2537 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
2538 {
2539 if( minecraft->localplayers[i] )
2540 {
2541 if( minecraft->localplayers[i]->entityId == packet->id )
2542 {
2543 isLocalPlayer = true;
2544 break;
2545 }
2546 }
2547 }
2548 if(isLocalPlayer) return;
2549
2550 switch(packet->action)
2551 {
2552 case TextureChangePacket::e_TextureChange_Skin:
2553 player->setCustomSkin( app.getSkinIdFromPath( packet->path ) );
2554#ifndef _CONTENT_PACKAGE
2555 wprintf(L"Skin for remote player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() );
2556#endif
2557 break;
2558 case TextureChangePacket::e_TextureChange_Cape:
2559 player->setCustomCape( Player::getCapeIdFromPath( packet->path ) );
2560 //player->customTextureUrl2 = packet->path;
2561#ifndef _CONTENT_PACKAGE
2562 wprintf(L"Cape for remote player %ls has changed to %ls\n", player->name.c_str(), player->customTextureUrl2.c_str() );
2563#endif
2564 break;
2565 }
2566
2567 if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path))
2568 {
2569 if( minecraft->addPendingClientTextureRequest(packet->path) )
2570 {
2571#ifndef _CONTENT_PACKAGE
2572 wprintf(L"handleTextureChange - Client sending texture packet to get custom skin %ls for player %ls\n",packet->path.c_str(), player->name.c_str());
2573#endif
2574 send(shared_ptr<TexturePacket>( new TexturePacket(packet->path,NULL,0) ) );
2575 }
2576 }
2577 else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path))
2578 {
2579 // Update the ref count on the memory texture data
2580 app.AddMemoryTextureFile(packet->path,NULL,0);
2581 }
2582}
2583
2584void ClientConnection::handleTextureAndGeometryChange(shared_ptr<TextureAndGeometryChangePacket> packet)
2585{
2586 shared_ptr<Entity> e = getEntity(packet->id);
2587 if (e == NULL) return;
2588 shared_ptr<Player> player = dynamic_pointer_cast<Player>(e);
2589 if( e == NULL) return;
2590
2591 bool isLocalPlayer = false;
2592 for( int i = 0; i < XUSER_MAX_COUNT; i++ )
2593 {
2594 if( minecraft->localplayers[i] )
2595 {
2596 if( minecraft->localplayers[i]->entityId == packet->id )
2597 {
2598 isLocalPlayer = true;
2599 break;
2600 }
2601 }
2602 }
2603 if(isLocalPlayer) return;
2604
2605
2606 player->setCustomSkin( app.getSkinIdFromPath( packet->path ) );
2607
2608#ifndef _CONTENT_PACKAGE
2609 wprintf(L"Skin for remote player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() );
2610#endif
2611
2612 if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path))
2613 {
2614 if( minecraft->addPendingClientTextureRequest(packet->path) )
2615 {
2616#ifndef _CONTENT_PACKAGE
2617 wprintf(L"handleTextureAndGeometryChange - Client sending TextureAndGeometryPacket to get custom skin %ls for player %ls\n",packet->path.c_str(), player->name.c_str());
2618#endif
2619 send(shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(packet->path,NULL,0) ) );
2620 }
2621 }
2622 else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path))
2623 {
2624 // Update the ref count on the memory texture data
2625 app.AddMemoryTextureFile(packet->path,NULL,0);
2626
2627 }
2628}
2629
2630void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
2631{
2632 //if (packet->dimension != minecraft->player->dimension)
2633 if( packet->dimension != minecraft->localplayers[m_userIndex]->dimension || packet->mapSeed != minecraft->localplayers[m_userIndex]->level->getSeed() )
2634 {
2635 int oldDimension = minecraft->localplayers[m_userIndex]->dimension;
2636 started = false;
2637
2638 // Remove client connection from this level
2639 level->removeClientConnection(this, false);
2640
2641 MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->getLevel( packet->dimension );
2642 if( dimensionLevel == NULL )
2643 {
2644 dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->mapSeed, packet->playerGameType, false, minecraft->level->getLevelData()->isHardcore(), packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty);
2645
2646 // 4J Stu - We want to shared the savedDataStorage between both levels
2647 //if( dimensionLevel->savedDataStorage != NULL )
2648 //{
2649 // Don't need to delete it here as it belongs to a client connection while will delete it when it's done
2650 // delete dimensionLevel->savedDataStorage;+
2651 //}
2652 dimensionLevel->savedDataStorage = level->savedDataStorage;
2653
2654 dimensionLevel->difficulty = packet->difficulty; // 4J Added
2655 app.DebugPrintf("dimensionLevel->difficulty - Difficulty = %d\n",packet->difficulty);
2656
2657 dimensionLevel->isClientSide = true;
2658 }
2659 else
2660 {
2661 dimensionLevel->addClientConnection(this);
2662 }
2663
2664 // Remove the player entity from the current level
2665 level->removeEntity( shared_ptr<Entity>(minecraft->localplayers[m_userIndex]) );
2666
2667 level = dimensionLevel;
2668
2669 // Whilst calling setLevel, make sure that minecraft::player is set up to be correct for this
2670 // connection
2671 shared_ptr<MultiplayerLocalPlayer> lastPlayer = minecraft->player;
2672 minecraft->player = minecraft->localplayers[m_userIndex];
2673 minecraft->setLevel(dimensionLevel);
2674 minecraft->player = lastPlayer;
2675
2676 TelemetryManager->RecordLevelExit(m_userIndex, eSen_LevelExitStatus_Succeeded);
2677
2678 //minecraft->player->dimension = packet->dimension;
2679 minecraft->localplayers[m_userIndex]->dimension = packet->dimension;
2680 //minecraft->setScreen(new ReceivingLevelScreen(this));
2681// minecraft->addPendingLocalConnection(m_userIndex, this);
2682
2683#ifdef _XBOX
2684 TelemetryManager->RecordLevelStart(m_userIndex, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->getLevel(packet->dimension)->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount());
2685#endif
2686
2687 if( minecraft->localgameModes[m_userIndex] != NULL )
2688 {
2689 TutorialMode *gameMode = (TutorialMode *)minecraft->localgameModes[m_userIndex];
2690 gameMode->getTutorial()->showTutorialPopup(false);
2691 }
2692
2693 // 4J-JEV: Fix for Durango #156334 - Content: UI: Rich Presence 'In the Nether' message is updating with a 3 to 10 minute delay.
2694 minecraft->localplayers[m_userIndex]->updateRichPresence();
2695
2696 ConnectionProgressParams *param = new ConnectionProgressParams();
2697 param->iPad = m_userIndex;
2698 if( packet->dimension == -1)
2699 {
2700 param->stringId = IDS_PROGRESS_ENTERING_NETHER;
2701 }
2702 else if( oldDimension == -1)
2703 {
2704 param->stringId = IDS_PROGRESS_LEAVING_NETHER;
2705 }
2706 else if( packet->dimension == 1)
2707 {
2708 param->stringId = IDS_PROGRESS_ENTERING_END;
2709 }
2710 else if( oldDimension == 1)
2711 {
2712 param->stringId = IDS_PROGRESS_LEAVING_END;
2713 }
2714 param->showTooltips = false;
2715 param->setFailTimer = false;
2716
2717 // 4J Stu - Fix for #13543 - Crash: Game crashes if entering a portal with the inventory menu open
2718 ui.CloseUIScenes( m_userIndex );
2719
2720 if(app.GetLocalPlayerCount()>1)
2721 {
2722 ui.NavigateToScene(m_userIndex, eUIScene_ConnectingProgress, param);
2723 }
2724 else
2725 {
2726 ui.NavigateToScene(m_userIndex, eUIScene_ConnectingProgress, param);
2727 }
2728
2729 app.SetAction( m_userIndex, eAppAction_WaitForDimensionChangeComplete);
2730 }
2731
2732 //minecraft->respawnPlayer(minecraft->player->GetXboxPad(),true, packet->dimension);
2733
2734 // Wrap respawnPlayer call up in code to set & restore the player/gamemode etc. as some things
2735 // in there assume that we are set up for the player that the respawn is coming in for
2736 int oldIndex = minecraft->getLocalPlayerIdx();
2737 minecraft->setLocalPlayerIdx(m_userIndex);
2738 minecraft->respawnPlayer(minecraft->localplayers[m_userIndex]->GetXboxPad(),packet->dimension,packet->m_newEntityId);
2739 ((MultiPlayerGameMode *) minecraft->localgameModes[m_userIndex])->setLocalMode(packet->playerGameType);
2740 minecraft->setLocalPlayerIdx(oldIndex);
2741}
2742
2743void ClientConnection::handleExplosion(shared_ptr<ExplodePacket> packet)
2744{
2745 if(!packet->m_bKnockbackOnly)
2746 {
2747 //app.DebugPrintf("Received ExplodePacket with explosion data\n");
2748 PIXBeginNamedEvent(0,"Handling explosion");
2749 Explosion *e = new Explosion(minecraft->level, nullptr, packet->x, packet->y, packet->z, packet->r);
2750 PIXBeginNamedEvent(0,"Finalizing");
2751
2752 // Fix for #81758 - TCR 006 BAS Non-Interactive Pause: TU9: Performance: Gameplay: After detonating bunch of TNT, game enters unresponsive state for couple of seconds.
2753 // The changes we are making here have been decided by the server, so we don't need to add them to the vector that resets tiles changes made
2754 // on the client as we KNOW that the server is matching these changes
2755 MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)minecraft->level;
2756 mpLevel->enableResetChanges(false);
2757 // 4J - now directly pass a pointer to the toBlow array in the packet rather than copying around
2758 e->finalizeExplosion(true, &packet->toBlow);
2759 mpLevel->enableResetChanges(true);
2760 PIXEndNamedEvent();
2761 PIXEndNamedEvent();
2762 delete e;
2763 }
2764 else
2765 {
2766 //app.DebugPrintf("Received ExplodePacket with knockback only data\n");
2767 }
2768
2769 //app.DebugPrintf("Adding knockback (%f,%f,%f) for player %d\n", packet->getKnockbackX(), packet->getKnockbackY(), packet->getKnockbackZ(), m_userIndex);
2770 minecraft->localplayers[m_userIndex]->xd += packet->getKnockbackX();
2771 minecraft->localplayers[m_userIndex]->yd += packet->getKnockbackY();
2772 minecraft->localplayers[m_userIndex]->zd += packet->getKnockbackZ();
2773}
2774
2775void ClientConnection::handleContainerOpen(shared_ptr<ContainerOpenPacket> packet)
2776{
2777 bool failed = false;
2778 shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
2779 switch(packet->type)
2780 {
2781 case ContainerOpenPacket::BONUS_CHEST:
2782 case ContainerOpenPacket::LARGE_CHEST:
2783 case ContainerOpenPacket::ENDER_CHEST:
2784 case ContainerOpenPacket::CONTAINER:
2785 case ContainerOpenPacket::MINECART_CHEST:
2786 {
2787 int chestString;
2788 switch (packet->type)
2789 {
2790 case ContainerOpenPacket::MINECART_CHEST: chestString = IDS_ITEM_MINECART; break;
2791 case ContainerOpenPacket::BONUS_CHEST: chestString = IDS_BONUS_CHEST; break;
2792 case ContainerOpenPacket::LARGE_CHEST: chestString = IDS_CHEST_LARGE; break;
2793 case ContainerOpenPacket::ENDER_CHEST: chestString = IDS_TILE_ENDERCHEST; break;
2794 case ContainerOpenPacket::CONTAINER: chestString = IDS_CHEST; break;
2795 default: assert(false); chestString = -1; break;
2796 }
2797
2798 if( player->openContainer(shared_ptr<SimpleContainer>( new SimpleContainer(chestString, packet->title, packet->customName, packet->size) )))
2799 {
2800 player->containerMenu->containerId = packet->containerId;
2801 }
2802 else
2803 {
2804 failed = true;
2805 }
2806 }
2807 break;
2808 case ContainerOpenPacket::HOPPER:
2809 {
2810 shared_ptr<HopperTileEntity> hopper = shared_ptr<HopperTileEntity>(new HopperTileEntity());
2811 if (packet->customName) hopper->setCustomName(packet->title);
2812 if(player->openHopper(hopper))
2813 {
2814 player->containerMenu->containerId = packet->containerId;
2815 }
2816 else
2817 {
2818 failed = true;
2819 }
2820 }
2821 break;
2822 case ContainerOpenPacket::FURNACE:
2823 {
2824 shared_ptr<FurnaceTileEntity> furnace = shared_ptr<FurnaceTileEntity>(new FurnaceTileEntity());
2825 if (packet->customName) furnace->setCustomName(packet->title);
2826 if(player->openFurnace(furnace))
2827 {
2828 player->containerMenu->containerId = packet->containerId;
2829 }
2830 else
2831 {
2832 failed = true;
2833 }
2834 }
2835 break;
2836 case ContainerOpenPacket::BREWING_STAND:
2837 {
2838 shared_ptr<BrewingStandTileEntity> brewingStand = shared_ptr<BrewingStandTileEntity>(new BrewingStandTileEntity());
2839 if (packet->customName) brewingStand->setCustomName(packet->title);
2840
2841 if( player->openBrewingStand(brewingStand))
2842 {
2843 player->containerMenu->containerId = packet->containerId;
2844 }
2845 else
2846 {
2847 failed = true;
2848 }
2849 }
2850 break;
2851 case ContainerOpenPacket::DROPPER:
2852 {
2853 shared_ptr<DropperTileEntity> dropper = shared_ptr<DropperTileEntity>(new DropperTileEntity());
2854 if (packet->customName) dropper->setCustomName(packet->title);
2855
2856 if( player->openTrap(dropper))
2857 {
2858 player->containerMenu->containerId = packet->containerId;
2859 }
2860 else
2861 {
2862 failed = true;
2863 }
2864 }
2865 break;
2866 case ContainerOpenPacket::TRAP:
2867 {
2868 shared_ptr<DispenserTileEntity> dispenser = shared_ptr<DispenserTileEntity>(new DispenserTileEntity());
2869 if (packet->customName) dispenser->setCustomName(packet->title);
2870
2871 if( player->openTrap(dispenser))
2872 {
2873 player->containerMenu->containerId = packet->containerId;
2874 }
2875 else
2876 {
2877 failed = true;
2878 }
2879 }
2880 break;
2881 case ContainerOpenPacket::WORKBENCH:
2882 {
2883 if( player->startCrafting(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)) )
2884 {
2885 player->containerMenu->containerId = packet->containerId;
2886 }
2887 else
2888 {
2889 failed = true;
2890 }
2891 }
2892 break;
2893 case ContainerOpenPacket::ENCHANTMENT:
2894 {
2895 if( player->startEnchanting(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z), packet->customName ? packet->title : L"") )
2896 {
2897 player->containerMenu->containerId = packet->containerId;
2898 }
2899 else
2900 {
2901 failed = true;
2902 }
2903 }
2904 break;
2905 case ContainerOpenPacket::TRADER_NPC:
2906 {
2907 shared_ptr<ClientSideMerchant> csm = shared_ptr<ClientSideMerchant>(new ClientSideMerchant(player, packet->title));
2908 csm->createContainer();
2909 if(player->openTrading(csm, packet->customName ? packet->title : L""))
2910 {
2911 player->containerMenu->containerId = packet->containerId;
2912 }
2913 else
2914 {
2915 failed = true;
2916 }
2917 }
2918 break;
2919 case ContainerOpenPacket::BEACON:
2920 {
2921 shared_ptr<BeaconTileEntity> beacon = shared_ptr<BeaconTileEntity>(new BeaconTileEntity());
2922 if (packet->customName) beacon->setCustomName(packet->title);
2923
2924 if(player->openBeacon(beacon))
2925 {
2926 player->containerMenu->containerId = packet->containerId;
2927 }
2928 else
2929 {
2930 failed = true;
2931 }
2932 }
2933 break;
2934 case ContainerOpenPacket::REPAIR_TABLE:
2935 {
2936 if(player->startRepairing(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)))
2937 {
2938 player->containerMenu->containerId = packet->containerId;
2939 }
2940 else
2941 {
2942 failed = true;
2943 }
2944 }
2945 break;
2946 case ContainerOpenPacket::HORSE:
2947 {
2948 shared_ptr<EntityHorse> entity = dynamic_pointer_cast<EntityHorse>( getEntity(packet->entityId) );
2949 int iTitle = IDS_CONTAINER_ANIMAL;
2950 switch(entity->getType())
2951 {
2952 case EntityHorse::TYPE_DONKEY:
2953 iTitle = IDS_DONKEY;
2954 break;
2955 case EntityHorse::TYPE_MULE:
2956 iTitle = IDS_MULE;
2957 break;
2958 };
2959 if(player->openHorseInventory(dynamic_pointer_cast<EntityHorse>(entity), shared_ptr<AnimalChest>(new AnimalChest(iTitle, packet->title, packet->customName, packet->size))))
2960 {
2961 player->containerMenu->containerId = packet->containerId;
2962 }
2963 else
2964 {
2965 failed = true;
2966 }
2967 }
2968 break;
2969 case ContainerOpenPacket::FIREWORKS:
2970 {
2971 if( player->openFireworks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)) )
2972 {
2973 player->containerMenu->containerId = packet->containerId;
2974 }
2975 else
2976 {
2977 failed = true;
2978 }
2979 }
2980 break;
2981 }
2982
2983 if(failed)
2984 {
2985 // Failed - if we've got a non-inventory container currently here, close that, which locally should put us back
2986 // to not having a container open, and should send a containerclose to the server so it doesn't have a container open.
2987 // If we don't have a non-inventory container open, just send the packet, and again we ought to be in sync with the server.
2988 if( player->containerMenu != player->inventoryMenu )
2989 {
2990 ui.CloseUIScenes(m_userIndex);
2991 }
2992 else
2993 {
2994 send(shared_ptr<ContainerClosePacket>(new ContainerClosePacket(packet->containerId)));
2995 }
2996 }
2997}
2998
2999void ClientConnection::handleContainerSetSlot(shared_ptr<ContainerSetSlotPacket> packet)
3000{
3001 shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
3002 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED )
3003 {
3004 player->inventory->setCarried(packet->item);
3005 }
3006 else
3007 {
3008 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY)
3009 {
3010 // 4J Stu - Reworked a bit to fix a bug where things being collected while the creative menu was up replaced items in the creative menu
3011 if(packet->slot >= 36 && packet->slot < 36 + 9)
3012 {
3013 shared_ptr<ItemInstance> lastItem = player->inventoryMenu->getSlot(packet->slot)->getItem();
3014 if (packet->item != NULL)
3015 {
3016 if (lastItem == NULL || lastItem->count < packet->item->count)
3017 {
3018 packet->item->popTime = Inventory::POP_TIME_DURATION;
3019 }
3020 }
3021 }
3022 player->inventoryMenu->setItem(packet->slot, packet->item);
3023 }
3024 else if (packet->containerId == player->containerMenu->containerId)
3025 {
3026 player->containerMenu->setItem(packet->slot, packet->item);
3027 }
3028 }
3029}
3030
3031void ClientConnection::handleContainerAck(shared_ptr<ContainerAckPacket> packet)
3032{
3033 shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
3034 AbstractContainerMenu *menu = NULL;
3035 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY)
3036 {
3037 menu = player->inventoryMenu;
3038 }
3039 else if (packet->containerId == player->containerMenu->containerId)
3040 {
3041 menu = player->containerMenu;
3042 }
3043 if (menu != NULL)
3044 {
3045 if (!packet->accepted)
3046 {
3047 send( shared_ptr<ContainerAckPacket>( new ContainerAckPacket(packet->containerId, packet->uid, true) ));
3048 }
3049 }
3050}
3051
3052void ClientConnection::handleContainerContent(shared_ptr<ContainerSetContentPacket> packet)
3053{
3054 shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
3055 if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY)
3056 {
3057 player->inventoryMenu->setAll(&packet->items);
3058 }
3059 else if (packet->containerId == player->containerMenu->containerId)
3060 {
3061 player->containerMenu->setAll(&packet->items);
3062 }
3063}
3064
3065void ClientConnection::handleTileEditorOpen(shared_ptr<TileEditorOpenPacket> packet)
3066{
3067 shared_ptr<TileEntity> tileEntity = level->getTileEntity(packet->x, packet->y, packet->z);
3068 if (tileEntity != NULL)
3069 {
3070 minecraft->localplayers[m_userIndex]->openTextEdit(tileEntity);
3071 }
3072 else if (packet->editorType == TileEditorOpenPacket::SIGN)
3073 {
3074 shared_ptr<SignTileEntity> localSignDummy = shared_ptr<SignTileEntity>(new SignTileEntity());
3075 localSignDummy->setLevel(level);
3076 localSignDummy->x = packet->x;
3077 localSignDummy->y = packet->y;
3078 localSignDummy->z = packet->z;
3079 minecraft->player->openTextEdit(localSignDummy);
3080 }
3081}
3082
3083void ClientConnection::handleSignUpdate(shared_ptr<SignUpdatePacket> packet)
3084{
3085 app.DebugPrintf("ClientConnection::handleSignUpdate - ");
3086 if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
3087 {
3088 shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z);
3089
3090 // 4J-PB - on a client connecting, the line below fails
3091 if (dynamic_pointer_cast<SignTileEntity>(te) != NULL)
3092 {
3093 shared_ptr<SignTileEntity> ste = dynamic_pointer_cast<SignTileEntity>(te);
3094 for (int i = 0; i < MAX_SIGN_LINES; i++)
3095 {
3096 ste->SetMessage(i,packet->lines[i]);
3097 }
3098
3099 app.DebugPrintf("verified = %d\tCensored = %d\n",packet->m_bVerified,packet->m_bCensored);
3100 ste->SetVerified(packet->m_bVerified);
3101 ste->SetCensored(packet->m_bCensored);
3102
3103 ste->setChanged();
3104 }
3105 else
3106 {
3107 app.DebugPrintf("dynamic_pointer_cast<SignTileEntity>(te) == NULL\n");
3108 }
3109 }
3110 else
3111 {
3112 app.DebugPrintf("hasChunkAt failed\n");
3113 }
3114}
3115
3116void ClientConnection::handleTileEntityData(shared_ptr<TileEntityDataPacket> packet)
3117{
3118 if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z))
3119 {
3120 shared_ptr<TileEntity> te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z);
3121
3122 if (te != NULL)
3123 {
3124 if (packet->type == TileEntityDataPacket::TYPE_MOB_SPAWNER && dynamic_pointer_cast<MobSpawnerTileEntity>(te) != NULL)
3125 {
3126 dynamic_pointer_cast<MobSpawnerTileEntity>(te)->load(packet->tag);
3127 }
3128 else if (packet->type == TileEntityDataPacket::TYPE_ADV_COMMAND && dynamic_pointer_cast<CommandBlockEntity>(te) != NULL)
3129 {
3130 dynamic_pointer_cast<CommandBlockEntity>(te)->load(packet->tag);
3131 }
3132 else if (packet->type == TileEntityDataPacket::TYPE_BEACON && dynamic_pointer_cast<BeaconTileEntity>(te) != NULL)
3133 {
3134 dynamic_pointer_cast<BeaconTileEntity>(te)->load(packet->tag);
3135 }
3136 else if (packet->type == TileEntityDataPacket::TYPE_SKULL && dynamic_pointer_cast<SkullTileEntity>(te) != NULL)
3137 {
3138 dynamic_pointer_cast<SkullTileEntity>(te)->load(packet->tag);
3139 }
3140 }
3141 }
3142}
3143
3144void ClientConnection::handleContainerSetData(shared_ptr<ContainerSetDataPacket> packet)
3145{
3146 onUnhandledPacket(packet);
3147 if (minecraft->localplayers[m_userIndex]->containerMenu != NULL && minecraft->localplayers[m_userIndex]->containerMenu->containerId == packet->containerId)
3148 {
3149 minecraft->localplayers[m_userIndex]->containerMenu->setData(packet->id, packet->value);
3150 }
3151}
3152
3153void ClientConnection::handleSetEquippedItem(shared_ptr<SetEquippedItemPacket> packet)
3154{
3155 shared_ptr<Entity> entity = getEntity(packet->entity);
3156 if (entity != NULL)
3157 {
3158 // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game
3159 entity->setEquippedSlot(packet->slot, packet->getItem() );
3160 }
3161}
3162
3163void ClientConnection::handleContainerClose(shared_ptr<ContainerClosePacket> packet)
3164{
3165 minecraft->localplayers[m_userIndex]->clientSideCloseContainer();
3166}
3167
3168void ClientConnection::handleTileEvent(shared_ptr<TileEventPacket> packet)
3169{
3170 PIXBeginNamedEvent(0,"Handle tile event\n");
3171 minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->tile, packet->b0, packet->b1);
3172 PIXEndNamedEvent();
3173}
3174
3175void ClientConnection::handleTileDestruction(shared_ptr<TileDestructionPacket> packet)
3176{
3177 minecraft->level->destroyTileProgress(packet->getEntityId(), packet->getX(), packet->getY(), packet->getZ(), packet->getState());
3178}
3179
3180bool ClientConnection::canHandleAsyncPackets()
3181{
3182 return minecraft != NULL && minecraft->level != NULL && minecraft->localplayers[m_userIndex] != NULL && level != NULL;
3183}
3184
3185void ClientConnection::handleGameEvent(shared_ptr<GameEventPacket> gameEventPacket)
3186{
3187 int event = gameEventPacket->_event;
3188 int param = gameEventPacket->param;
3189 if (event >= 0 && event < GameEventPacket::EVENT_LANGUAGE_ID_LENGTH)
3190 {
3191 if (GameEventPacket::EVENT_LANGUAGE_ID[event] > 0) // 4J - was NULL check
3192 {
3193 minecraft->localplayers[m_userIndex]->displayClientMessage(GameEventPacket::EVENT_LANGUAGE_ID[event]);
3194 }
3195 }
3196 if (event == GameEventPacket::START_RAINING)
3197 {
3198 level->getLevelData()->setRaining(true);
3199 level->setRainLevel(1);
3200 }
3201 else if (event == GameEventPacket::STOP_RAINING)
3202 {
3203 level->getLevelData()->setRaining(false);
3204 level->setRainLevel(0);
3205 }
3206 else if (event == GameEventPacket::CHANGE_GAME_MODE)
3207 {
3208 minecraft->localgameModes[m_userIndex]->setLocalMode(GameType::byId(param));
3209 }
3210 else if (event == GameEventPacket::WIN_GAME)
3211 {
3212 ui.SetWinUserIndex( (BYTE)gameEventPacket->param );
3213
3214#ifdef _XBOX
3215
3216 // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen
3217 ui.HideAllGameUIElements();
3218
3219 // Hide the other players scenes
3220 ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false);
3221
3222 // This just allows it to be shown
3223 if(minecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) minecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false);
3224 // Temporarily make this scene fullscreen
3225 CXuiSceneBase::SetPlayerBaseScenePosition( ProfileManager.GetPrimaryPad(), CXuiSceneBase::e_BaseScene_Fullscreen );
3226
3227 app.CloseXuiScenesAndNavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_EndPoem);
3228#else
3229 app.DebugPrintf("handleGameEvent packet for WIN_GAME - %d\n", m_userIndex);
3230 // This just allows it to be shown
3231 if(minecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) minecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false);
3232 ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_EndPoem, NULL, eUILayer_Scene, eUIGroup_Fullscreen);
3233#endif
3234 }
3235 else if( event == GameEventPacket::START_SAVING )
3236 {
3237 if(!g_NetworkManager.IsHost())
3238 {
3239 // Move app started to here so that it happens immediately otherwise back-to-back START/STOP packets
3240 // leave the client stuck in the loading screen
3241 app.SetGameStarted(false);
3242 app.SetAction( ProfileManager.GetPrimaryPad(), eAppAction_RemoteServerSave );
3243 }
3244 }
3245 else if( event == GameEventPacket::STOP_SAVING )
3246 {
3247 if(!g_NetworkManager.IsHost() ) app.SetGameStarted(true);
3248 }
3249 else if ( event == GameEventPacket::SUCCESSFUL_BOW_HIT )
3250 {
3251 shared_ptr<MultiplayerLocalPlayer> player = minecraft->localplayers[m_userIndex];
3252 level->playLocalSound(player->x, player->y + player->getHeadHeight(), player->z, eSoundType_RANDOM_BOW_HIT , 0.18f, 0.45f, false);
3253 }
3254}
3255
3256void ClientConnection::handleComplexItemData(shared_ptr<ComplexItemDataPacket> packet)
3257{
3258 if (packet->itemType == Item::map->id)
3259 {
3260 MapItem::getSavedData(packet->itemId, minecraft->level)->handleComplexItemData(packet->data);
3261 }
3262 else
3263 {
3264// System.out.println("Unknown itemid: " + packet->itemId); // 4J removed
3265 }
3266}
3267
3268
3269
3270void ClientConnection::handleLevelEvent(shared_ptr<LevelEventPacket> packet)
3271{
3272 if (packet->type == LevelEvent::SOUND_DRAGON_DEATH)
3273 {
3274 for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
3275 {
3276 if(minecraft->localplayers[i] != NULL && minecraft->localplayers[i]->level != NULL && minecraft->localplayers[i]->level->dimension->id == 1)
3277 {
3278 minecraft->localplayers[i]->awardStat(GenericStats::completeTheEnd(),GenericStats::param_noArgs());
3279 }
3280 }
3281 }
3282
3283 if (packet->isGlobalEvent())
3284 {
3285 minecraft->level->globalLevelEvent(packet->type, packet->x, packet->y, packet->z, packet->data);
3286 }
3287 else
3288 {
3289 minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data);
3290 }
3291
3292 minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data);
3293}
3294
3295void ClientConnection::handleAwardStat(shared_ptr<AwardStatPacket> packet)
3296{
3297 minecraft->localplayers[m_userIndex]->awardStatFromServer(GenericStats::stat(packet->statId), packet->getParamData());
3298}
3299
3300void ClientConnection::handleUpdateMobEffect(shared_ptr<UpdateMobEffectPacket> packet)
3301{
3302 shared_ptr<Entity> e = getEntity(packet->entityId);
3303 if ( (e == NULL) || !e->instanceof(eTYPE_LIVINGENTITY) ) return;
3304
3305 //( dynamic_pointer_cast<LivingEntity>(e) )->addEffect(new MobEffectInstance(packet->effectId, packet->effectDurationTicks, packet->effectAmplifier));
3306
3307 MobEffectInstance *mobEffectInstance = new MobEffectInstance(packet->effectId, packet->effectDurationTicks, packet->effectAmplifier);
3308 mobEffectInstance->setNoCounter(packet->isSuperLongDuration());
3309 dynamic_pointer_cast<LivingEntity>(e)->addEffect(mobEffectInstance);
3310}
3311
3312void ClientConnection::handleRemoveMobEffect(shared_ptr<RemoveMobEffectPacket> packet)
3313{
3314 shared_ptr<Entity> e = getEntity(packet->entityId);
3315 if ( (e == NULL) || !e->instanceof(eTYPE_LIVINGENTITY) ) return;
3316
3317 ( dynamic_pointer_cast<LivingEntity>(e) )->removeEffectNoUpdate(packet->effectId);
3318}
3319
3320bool ClientConnection::isServerPacketListener()
3321{
3322 return false;
3323}
3324
3325void ClientConnection::handlePlayerInfo(shared_ptr<PlayerInfoPacket> packet)
3326{
3327 unsigned int startingPrivileges = app.GetPlayerPrivileges(packet->m_networkSmallId);
3328
3329 INetworkPlayer *networkPlayer = g_NetworkManager.GetPlayerBySmallId(packet->m_networkSmallId);
3330
3331 if(networkPlayer != NULL && networkPlayer->IsHost())
3332 {
3333 // Some settings should always be considered on for the host player
3334 Player::enableAllPlayerPrivileges(startingPrivileges,true);
3335 Player::setPlayerGamePrivilege(startingPrivileges,Player::ePlayerGamePrivilege_HOST,1);
3336 }
3337
3338 // 4J Stu - Repurposed this packet for player info that we want
3339 app.UpdatePlayerInfo(packet->m_networkSmallId, packet->m_playerColourIndex, packet->m_playerPrivileges);
3340
3341 shared_ptr<Entity> entity = getEntity(packet->m_entityId);
3342 if(entity != NULL && entity->instanceof(eTYPE_PLAYER))
3343 {
3344 shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
3345 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_playerPrivileges);
3346 }
3347 if(networkPlayer != NULL && networkPlayer->IsLocal())
3348 {
3349 for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
3350 {
3351 shared_ptr<MultiplayerLocalPlayer> localPlayer = minecraft->localplayers[i];
3352 if(localPlayer != NULL && localPlayer->connection != NULL && localPlayer->connection->getNetworkPlayer() == networkPlayer )
3353 {
3354 localPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All,packet->m_playerPrivileges);
3355 displayPrivilegeChanges(localPlayer,startingPrivileges);
3356 break;
3357 }
3358 }
3359 }
3360
3361 // 4J Stu - I don't think we care about this, so not converting it (came from 1.8.2)
3362#if 0
3363 PlayerInfo pi = playerInfoMap.get(packet.name);
3364 if (pi == null && packet.add) {
3365 pi = new PlayerInfo(packet.name);
3366 playerInfoMap.put(packet.name, pi);
3367 playerInfos.add(pi);
3368 }
3369 if (pi != null && !packet.add) {
3370 playerInfoMap.remove(packet.name);
3371 playerInfos.remove(pi);
3372 }
3373 if (packet.add && pi != null) {
3374 pi.latency = packet.latency;
3375 }
3376#endif
3377}
3378
3379
3380void ClientConnection::displayPrivilegeChanges(shared_ptr<MultiplayerLocalPlayer> player, unsigned int oldPrivileges)
3381{
3382 int userIndex = player->GetXboxPad();
3383 unsigned int newPrivileges = player->getAllPlayerGamePrivileges();
3384 Player::EPlayerGamePrivileges priv = (Player::EPlayerGamePrivileges)0;
3385 bool privOn = false;
3386 for(unsigned int i = 0; i < Player::ePlayerGamePrivilege_MAX; ++i)
3387 {
3388 priv = (Player::EPlayerGamePrivileges) i;
3389 if( Player::getPlayerGamePrivilege(newPrivileges,priv) != Player::getPlayerGamePrivilege(oldPrivileges,priv))
3390 {
3391 privOn = Player::getPlayerGamePrivilege(newPrivileges,priv);
3392 wstring message = L"";
3393 if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
3394 {
3395 switch(priv)
3396 {
3397 case Player::ePlayerGamePrivilege_CannotMine:
3398 if(privOn) message = app.GetString(IDS_PRIV_MINE_TOGGLE_ON);
3399 else message = app.GetString(IDS_PRIV_MINE_TOGGLE_OFF);
3400 break;
3401 case Player::ePlayerGamePrivilege_CannotBuild:
3402 if(privOn) message = app.GetString(IDS_PRIV_BUILD_TOGGLE_ON);
3403 else message = app.GetString(IDS_PRIV_BUILD_TOGGLE_OFF);
3404 break;
3405 case Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches:
3406 if(privOn) message = app.GetString(IDS_PRIV_USE_DOORS_TOGGLE_ON);
3407 else message = app.GetString(IDS_PRIV_USE_DOORS_TOGGLE_OFF);
3408 break;
3409 case Player::ePlayerGamePrivilege_CanUseContainers:
3410 if(privOn) message = app.GetString(IDS_PRIV_USE_CONTAINERS_TOGGLE_ON);
3411 else message = app.GetString(IDS_PRIV_USE_CONTAINERS_TOGGLE_OFF);
3412 break;
3413 case Player::ePlayerGamePrivilege_CannotAttackAnimals:
3414 if(privOn) message = app.GetString(IDS_PRIV_ATTACK_ANIMAL_TOGGLE_ON);
3415 else message = app.GetString(IDS_PRIV_ATTACK_ANIMAL_TOGGLE_OFF);
3416 break;
3417 case Player::ePlayerGamePrivilege_CannotAttackMobs:
3418 if(privOn) message = app.GetString(IDS_PRIV_ATTACK_MOB_TOGGLE_ON);
3419 else message = app.GetString(IDS_PRIV_ATTACK_MOB_TOGGLE_OFF);
3420 break;
3421 case Player::ePlayerGamePrivilege_CannotAttackPlayers:
3422 if(privOn) message = app.GetString(IDS_PRIV_ATTACK_PLAYER_TOGGLE_ON);
3423 else message = app.GetString(IDS_PRIV_ATTACK_PLAYER_TOGGLE_OFF);
3424 break;
3425 };
3426 }
3427 switch(priv)
3428 {
3429 case Player::ePlayerGamePrivilege_Op:
3430 if(privOn) message = app.GetString(IDS_PRIV_MODERATOR_TOGGLE_ON);
3431 else message = app.GetString(IDS_PRIV_MODERATOR_TOGGLE_OFF);
3432 break;
3433 };
3434 if(app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0)
3435 {
3436 switch(priv)
3437 {
3438 case Player::ePlayerGamePrivilege_CanFly:
3439 if(privOn) message = app.GetString(IDS_PRIV_FLY_TOGGLE_ON);
3440 else message = app.GetString(IDS_PRIV_FLY_TOGGLE_OFF);
3441 break;
3442 case Player::ePlayerGamePrivilege_ClassicHunger:
3443 if(privOn) message = app.GetString(IDS_PRIV_EXHAUSTION_TOGGLE_ON);
3444 else message = app.GetString(IDS_PRIV_EXHAUSTION_TOGGLE_OFF);
3445 break;
3446 case Player::ePlayerGamePrivilege_Invisible:
3447 if(privOn) message = app.GetString(IDS_PRIV_INVISIBLE_TOGGLE_ON);
3448 else message = app.GetString(IDS_PRIV_INVISIBLE_TOGGLE_OFF);
3449 break;
3450 case Player::ePlayerGamePrivilege_Invulnerable:
3451 if(privOn) message = app.GetString(IDS_PRIV_INVULNERABLE_TOGGLE_ON);
3452 else message = app.GetString(IDS_PRIV_INVULNERABLE_TOGGLE_OFF);
3453 break;
3454 case Player::ePlayerGamePrivilege_CanToggleInvisible:
3455 if(privOn) message = app.GetString(IDS_PRIV_CAN_INVISIBLE_TOGGLE_ON);
3456 else message = app.GetString(IDS_PRIV_CAN_INVISIBLE_TOGGLE_OFF);
3457 break;
3458 case Player::ePlayerGamePrivilege_CanToggleFly:
3459 if(privOn) message = app.GetString(IDS_PRIV_CAN_FLY_TOGGLE_ON);
3460 else message = app.GetString(IDS_PRIV_CAN_FLY_TOGGLE_OFF);
3461 break;
3462 case Player::ePlayerGamePrivilege_CanToggleClassicHunger:
3463 if(privOn) message = app.GetString(IDS_PRIV_CAN_EXHAUSTION_TOGGLE_ON);
3464 else message = app.GetString(IDS_PRIV_CAN_EXHAUSTION_TOGGLE_OFF);
3465 break;
3466 case Player::ePlayerGamePrivilege_CanTeleport:
3467 if(privOn) message = app.GetString(IDS_PRIV_CAN_TELEPORT_TOGGLE_ON);
3468 else message = app.GetString(IDS_PRIV_CAN_TELEPORT_TOGGLE_OFF);
3469 break;
3470 };
3471 }
3472 if(!message.empty()) minecraft->gui->addMessage(message,userIndex);
3473 }
3474 }
3475}
3476
3477void ClientConnection::handleKeepAlive(shared_ptr<KeepAlivePacket> packet)
3478{
3479 send(shared_ptr<KeepAlivePacket>(new KeepAlivePacket(packet->id)));
3480}
3481
3482void ClientConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> playerAbilitiesPacket)
3483{
3484 shared_ptr<Player> player = minecraft->localplayers[m_userIndex];
3485 player->abilities.flying = playerAbilitiesPacket->isFlying();
3486 player->abilities.instabuild = playerAbilitiesPacket->canInstabuild();
3487 player->abilities.invulnerable = playerAbilitiesPacket->isInvulnerable();
3488 player->abilities.mayfly = playerAbilitiesPacket->canFly();
3489 player->abilities.setFlyingSpeed(playerAbilitiesPacket->getFlyingSpeed());
3490 player->abilities.setWalkingSpeed(playerAbilitiesPacket->getWalkingSpeed());
3491}
3492
3493void ClientConnection::handleSoundEvent(shared_ptr<LevelSoundPacket> packet)
3494{
3495 minecraft->level->playLocalSound(packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), packet->getVolume(), packet->getPitch(), false);
3496}
3497
3498void ClientConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> customPayloadPacket)
3499{
3500 if (CustomPayloadPacket::TRADER_LIST_PACKET.compare(customPayloadPacket->identifier) == 0)
3501 {
3502 ByteArrayInputStream bais(customPayloadPacket->data);
3503 DataInputStream input(&bais);
3504 int containerId = input.readInt();
3505 if (ui.IsSceneInStack(m_userIndex, eUIScene_TradingMenu) && containerId == minecraft->localplayers[m_userIndex]->containerMenu->containerId)
3506 {
3507 shared_ptr<Merchant> trader = nullptr;
3508
3509#ifdef _XBOX
3510 HXUIOBJ scene = app.GetCurrentScene(m_userIndex);
3511 HXUICLASS thisClass = XuiFindClass( L"CXuiSceneTrading" );
3512 HXUICLASS objClass = XuiGetObjectClass( scene );
3513
3514 // Also returns TRUE if they are the same (which is what we want)
3515 if( XuiClassDerivesFrom( objClass, thisClass ) )
3516 {
3517 CXuiSceneTrading *screen;
3518 HRESULT hr = XuiObjectFromHandle(scene, (void **) &screen);
3519 if (FAILED(hr)) return;
3520 trader = screen->getMerchant();
3521 }
3522#else
3523 UIScene *scene = ui.GetTopScene(m_userIndex, eUILayer_Scene);
3524 UIScene_TradingMenu *screen = (UIScene_TradingMenu *)scene;
3525 trader = screen->getMerchant();
3526#endif
3527
3528 MerchantRecipeList *recipeList = MerchantRecipeList::createFromStream(&input);
3529 trader->overrideOffers(recipeList);
3530 }
3531 }
3532}
3533
3534Connection *ClientConnection::getConnection()
3535{
3536 return connection;
3537}
3538
3539// 4J Added
3540void ClientConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChangedPacket> packet)
3541{
3542 if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS)
3543 {
3544 app.SetGameHostOption(eGameHostOption_All, packet->data);
3545 }
3546 else if(packet->action==ServerSettingsChangedPacket::HOST_DIFFICULTY)
3547 {
3548 for(unsigned int i = 0; i < minecraft->levels.length; ++i)
3549 {
3550 if( minecraft->levels[i] != NULL )
3551 {
3552 app.DebugPrintf("ClientConnection::handleServerSettingsChanged - Difficulty = %d",packet->data);
3553 minecraft->levels[i]->difficulty = packet->data;
3554 }
3555 }
3556 }
3557 else
3558 {
3559 //options
3560 //minecraft->options->SetGamertagSetting((packet->data==0)?false:true);
3561 app.SetGameHostOption(eGameHostOption_Gamertags, packet->data);
3562 }
3563
3564}
3565
3566void ClientConnection::handleXZ(shared_ptr<XZPacket> packet)
3567{
3568 if(packet->action==XZPacket::STRONGHOLD)
3569 {
3570 minecraft->levels[0]->getLevelData()->setXStronghold(packet->x);
3571 minecraft->levels[0]->getLevelData()->setZStronghold(packet->z);
3572 minecraft->levels[0]->getLevelData()->setHasStronghold();
3573 }
3574}
3575
3576void ClientConnection::handleUpdateProgress(shared_ptr<UpdateProgressPacket> packet)
3577{
3578 if(!g_NetworkManager.IsHost() ) Minecraft::GetInstance()->progressRenderer->progressStagePercentage( packet->m_percentage );
3579}
3580
3581void ClientConnection::handleUpdateGameRuleProgressPacket(shared_ptr<UpdateGameRuleProgressPacket> packet)
3582{
3583 LPCWSTR string = app.GetGameRulesString(packet->m_messageId);
3584 if(string != NULL)
3585 {
3586 wstring message(string);
3587 message = GameRuleDefinition::generateDescriptionString(packet->m_definitionType,message,packet->m_data.data,packet->m_data.length);
3588 if(minecraft->localgameModes[m_userIndex]!=NULL)
3589 {
3590 minecraft->localgameModes[m_userIndex]->getTutorial()->setMessage(message, packet->m_icon, packet->m_auxValue);
3591 }
3592 }
3593 // If this rule has a data tag associated with it, then we save that in user profile data
3594 if(packet->m_dataTag > 0 && packet->m_dataTag <= 32)
3595 {
3596 app.DebugPrintf("handleUpdateGameRuleProgressPacket: Data tag is in range, so updating profile data\n");
3597 app.SetSpecialTutorialCompletionFlag(m_userIndex, packet->m_dataTag - 1);
3598 }
3599 delete [] packet->m_data.data;
3600}
3601
3602// 4J Stu - TU-1 hotfix
3603// Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
3604int ClientConnection::HostDisconnectReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
3605{
3606 // 4J-PB - if they have a trial texture pack, they don't get to save the world
3607 if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin())
3608 {
3609 TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected();
3610 DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack;
3611
3612 DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack();
3613 if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" ))
3614 {
3615 // no upsell, we're about to quit
3616 MinecraftServer::getInstance()->setSaveOnExit( false );
3617 // flag a app action of exit game
3618 app.SetAction(iPad,eAppAction_ExitWorld);
3619 }
3620 }
3621
3622#if defined(_XBOX_ONE) || defined(__ORBIS__)
3623 // Give the player the option to save their game
3624 // does the save exist?
3625 bool bSaveExists;
3626 StorageManager.DoesSaveExist(&bSaveExists);
3627 // 4J-PB - we check if the save exists inside the libs
3628 // we need to ask if they are sure they want to overwrite the existing game
3629 if(bSaveExists && StorageManager.GetSaveDisabled())
3630 {
3631 UINT uiIDA[2];
3632 uiIDA[0]=IDS_CONFIRM_CANCEL;
3633 uiIDA[1]=IDS_CONFIRM_OK;
3634 ui.RequestErrorMessage(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&ClientConnection::ExitGameAndSaveReturned,NULL);
3635 }
3636 else
3637#else
3638 // Give the player the option to save their game
3639 // does the save exist?
3640 bool bSaveExists;
3641 StorageManager.DoesSaveExist(&bSaveExists);
3642 // 4J-PB - we check if the save exists inside the libs
3643 // we need to ask if they are sure they want to overwrite the existing game
3644 if(bSaveExists)
3645 {
3646 UINT uiIDA[2];
3647 uiIDA[0]=IDS_CONFIRM_CANCEL;
3648 uiIDA[1]=IDS_CONFIRM_OK;
3649 ui.RequestErrorMessage(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&ClientConnection::ExitGameAndSaveReturned,NULL);
3650 }
3651 else
3652#endif
3653 {
3654#if defined(_XBOX_ONE) || defined(__ORBIS__)
3655 StorageManager.SetSaveDisabled(false);
3656#endif
3657 MinecraftServer::getInstance()->setSaveOnExit( true );
3658 // flag a app action of exit game
3659 app.SetAction(iPad,eAppAction_ExitWorld);
3660 }
3661
3662 return 0;
3663}
3664
3665int ClientConnection::ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
3666{
3667 // results switched for this dialog
3668 if(result==C4JStorage::EMessage_ResultDecline)
3669 {
3670 //INT saveOrCheckpointId = 0;
3671 //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId);
3672 //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId);
3673#if defined(_XBOX_ONE) || defined(__ORBIS__)
3674 StorageManager.SetSaveDisabled(false);
3675#endif
3676 MinecraftServer::getInstance()->setSaveOnExit( true );
3677 }
3678 else
3679 {
3680 MinecraftServer::getInstance()->setSaveOnExit( false );
3681 }
3682 // flag a app action of exit game
3683 app.SetAction(iPad,eAppAction_ExitWorld);
3684 return 0;
3685}
3686
3687//
3688wstring ClientConnection::GetDisplayNameByGamertag(wstring gamertag)
3689{
3690#ifdef _DURANGO
3691 wstring displayName = g_NetworkManager.GetDisplayNameByGamertag(gamertag);
3692 return displayName;
3693#else
3694 return gamertag;
3695#endif
3696}
3697
3698void ClientConnection::handleAddObjective(shared_ptr<SetObjectivePacket> packet)
3699{
3700#if 0
3701 Scoreboard scoreboard = level->getScoreboard();
3702
3703 if (packet->method == SetObjectivePacket::METHOD_ADD)
3704 {
3705 Objective objective = scoreboard->addObjective(packet->objectiveName, ObjectiveCriteria::DUMMY);
3706 objective->setDisplayName(packet->displayName);
3707 }
3708 else
3709 {
3710 Objective objective = scoreboard->getObjective(packet->objectiveName);
3711
3712 if (packet->method == SetObjectivePacket::METHOD_REMOVE)
3713 {
3714 scoreboard->removeObjective(objective);
3715 }
3716 else if (packet->method == SetObjectivePacket::METHOD_CHANGE)
3717 {
3718 objective->setDisplayName(packet->displayName);
3719 }
3720 }
3721#endif
3722}
3723
3724void ClientConnection::handleSetScore(shared_ptr<SetScorePacket> packet)
3725{
3726#if 0
3727 Scoreboard scoreboard = level->getScoreboard();
3728 Objective objective = scoreboard->getObjective(packet->objectiveName);
3729
3730 if (packet->method == SetScorePacket::METHOD_CHANGE)
3731 {
3732 Score score = scoreboard->getPlayerScore(packet->owner, objective);
3733 score->setScore(packet->score);
3734 }
3735 else if (packet->method == SetScorePacket::METHOD_REMOVE)
3736 {
3737 scoreboard->resetPlayerScore(packet->owner);
3738 }
3739#endif
3740}
3741
3742void ClientConnection::handleSetDisplayObjective(shared_ptr<SetDisplayObjectivePacket> packet)
3743{
3744#if 0
3745 Scoreboard scoreboard = level->getScoreboard();
3746
3747 if (packet->objectiveName->length() == 0)
3748 {
3749 scoreboard->setDisplayObjective(packet->slot, null);
3750 }
3751 else
3752 {
3753 Objective objective = scoreboard->getObjective(packet->objectiveName);
3754 scoreboard->setDisplayObjective(packet->slot, objective);
3755 }
3756#endif
3757}
3758
3759void ClientConnection::handleSetPlayerTeamPacket(shared_ptr<SetPlayerTeamPacket> packet)
3760{
3761#if 0
3762 Scoreboard scoreboard = level->getScoreboard();
3763 PlayerTeam *team;
3764
3765 if (packet->method == SetPlayerTeamPacket::METHOD_ADD)
3766 {
3767 team = scoreboard->addPlayerTeam(packet->name);
3768 }
3769 else
3770 {
3771 team = scoreboard->getPlayerTeam(packet->name);
3772 }
3773
3774 if (packet->method == SetPlayerTeamPacket::METHOD_ADD || packet->method == SetPlayerTeamPacket::METHOD_CHANGE)
3775 {
3776 team->setDisplayName(packet->displayName);
3777 team->setPrefix(packet->prefix);
3778 team->setSuffix(packet->suffix);
3779 team->unpackOptions(packet->options);
3780 }
3781
3782 if (packet->method == SetPlayerTeamPacket::METHOD_ADD || packet->method == SetPlayerTeamPacket::METHOD_JOIN)
3783 {
3784 for (int i = 0; i < packet->players.size(); i++)
3785 {
3786 scoreboard->addPlayerToTeam(packet->players[i], team);
3787 }
3788 }
3789
3790 if (packet->method == SetPlayerTeamPacket::METHOD_LEAVE)
3791 {
3792 for (int i = 0; i < packet->players.size(); i++)
3793 {
3794 scoreboard->removePlayerFromTeam(packet->players[i], team);
3795 }
3796 }
3797
3798 if (packet->method == SetPlayerTeamPacket::METHOD_REMOVE)
3799 {
3800 scoreboard->removePlayerTeam(team);
3801 }
3802#endif
3803}
3804
3805void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet)
3806{
3807 for (int i = 0; i < packet->getCount(); i++)
3808 {
3809 double xVarience = random->nextGaussian() * packet->getXDist();
3810 double yVarience = random->nextGaussian() * packet->getYDist();
3811 double zVarience = random->nextGaussian() * packet->getZDist();
3812 double xa = random->nextGaussian() * packet->getMaxSpeed();
3813 double ya = random->nextGaussian() * packet->getMaxSpeed();
3814 double za = random->nextGaussian() * packet->getMaxSpeed();
3815
3816 // TODO: determine particle ID from name
3817 assert(0);
3818 ePARTICLE_TYPE particleId = eParticleType_heart;
3819
3820 level->addParticle(particleId, packet->getX() + xVarience, packet->getY() + yVarience, packet->getZ() + zVarience, xa, ya, za);
3821 }
3822}
3823
3824void ClientConnection::handleUpdateAttributes(shared_ptr<UpdateAttributesPacket> packet)
3825{
3826 shared_ptr<Entity> entity = getEntity(packet->getEntityId());
3827 if (entity == NULL) return;
3828
3829 if ( !entity->instanceof(eTYPE_LIVINGENTITY) )
3830 {
3831 // Entity is not a living entity!
3832 assert(0);
3833 }
3834
3835 BaseAttributeMap *attributes = (dynamic_pointer_cast<LivingEntity>(entity))->getAttributes();
3836 unordered_set<UpdateAttributesPacket::AttributeSnapshot *> attributeSnapshots = packet->getValues();
3837 for (AUTO_VAR(it,attributeSnapshots.begin()); it != attributeSnapshots.end(); ++it)
3838 {
3839 UpdateAttributesPacket::AttributeSnapshot *attribute = *it;
3840 AttributeInstance *instance = attributes->getInstance(attribute->getId());
3841
3842 if (instance == NULL)
3843 {
3844 // 4J - TODO: revisit, not familiar with the attribute system, why are we passing in MIN_NORMAL (Java's smallest non-zero value conforming to IEEE Standard 754 (?)) and MAX_VALUE
3845 instance = attributes->registerAttribute(new RangedAttribute(attribute->getId(), 0, Double::MIN_NORMAL, Double::MAX_VALUE));
3846 }
3847
3848 instance->setBaseValue(attribute->getBase());
3849 instance->removeModifiers();
3850
3851 unordered_set<AttributeModifier *> *modifiers = attribute->getModifiers();
3852
3853 for (AUTO_VAR(it2,modifiers->begin()); it2 != modifiers->end(); ++it2)
3854 {
3855 AttributeModifier* modifier = *it2;
3856 instance->addModifier(new AttributeModifier(modifier->getId(), modifier->getAmount(), modifier->getOperation() ) );
3857 }
3858 }
3859}
3860
3861// 4J: Check for deferred entity link packets related to this entity ID and handle them
3862void ClientConnection::checkDeferredEntityLinkPackets(int newEntityId)
3863{
3864 if (deferredEntityLinkPackets.empty()) return;
3865
3866 for (int i = 0; i < deferredEntityLinkPackets.size(); i++)
3867 {
3868 DeferredEntityLinkPacket *deferred = &deferredEntityLinkPackets[i];
3869
3870 bool remove = false;
3871
3872 // Only consider recently deferred packets
3873 int tickInterval = GetTickCount() - deferred->m_recievedTick;
3874 if (tickInterval < MAX_ENTITY_LINK_DEFERRAL_INTERVAL)
3875 {
3876 // Note: we assume it's the destination entity
3877 if (deferred->m_packet->destId == newEntityId)
3878 {
3879 handleEntityLinkPacket(deferred->m_packet);
3880 remove = true;
3881 }
3882 }
3883 else
3884 {
3885 // This is an old packet, remove (shouldn't really come up but seems prudent)
3886 remove = true;
3887 }
3888
3889 if (remove)
3890 {
3891 deferredEntityLinkPackets.erase(deferredEntityLinkPackets.begin() + i);
3892 i--;
3893 }
3894 }
3895}
3896
3897ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket(shared_ptr<SetEntityLinkPacket> packet)
3898{
3899 m_recievedTick = GetTickCount();
3900 m_packet = packet;
3901}