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