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 "MultiPlayerGameMode.h"
3#include "CreativeMode.h"
4#include "MultiPlayerLocalPlayer.h"
5#include "MultiPlayerLevel.h"
6#include "Minecraft.h"
7#include "ClientConnection.h"
8#include "LevelRenderer.h"
9#include "Common\Network\GameNetworkManager.h"
10#include "..\Minecraft.World\net.minecraft.world.level.h"
11#include "..\Minecraft.World\net.minecraft.world.item.h"
12#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
13#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
14#include "..\Minecraft.World\net.minecraft.world.inventory.h"
15#include "..\Minecraft.World\net.minecraft.h"
16
17MultiPlayerGameMode::MultiPlayerGameMode(Minecraft *minecraft, ClientConnection *connection)
18{
19 // 4J - added initialisers
20 xDestroyBlock = -1;
21 yDestroyBlock = -1;
22 zDestroyBlock = -1;
23 destroyingItem = nullptr;
24 destroyProgress = 0;
25 destroyTicks = 0;
26 destroyDelay = 0;
27 isDestroying = false;
28 carriedItem = 0;
29 localPlayerMode = GameType::SURVIVAL;
30 this->minecraft = minecraft;
31 this->connection = connection;
32}
33
34void MultiPlayerGameMode::creativeDestroyBlock(Minecraft *minecraft, MultiPlayerGameMode *gameMode, int x, int y, int z, int face)
35{
36 if (!minecraft->level->extinguishFire(minecraft->player, x, y, z, face))
37 {
38 gameMode->destroyBlock(x, y, z, face);
39 }
40}
41
42void MultiPlayerGameMode::adjustPlayer(shared_ptr<Player> player)
43{
44 localPlayerMode->updatePlayerAbilities(&player->abilities);
45}
46
47bool MultiPlayerGameMode::isCutScene()
48{
49 return false;
50}
51
52void MultiPlayerGameMode::setLocalMode(GameType *mode)
53{
54 localPlayerMode = mode;
55 localPlayerMode->updatePlayerAbilities(&minecraft->player->abilities);
56}
57
58void MultiPlayerGameMode::initPlayer(shared_ptr<Player> player)
59{
60 player->yRot = -180;
61}
62
63bool MultiPlayerGameMode::canHurtPlayer()
64{
65 return localPlayerMode->isSurvival();
66}
67
68bool MultiPlayerGameMode::destroyBlock(int x, int y, int z, int face)
69{
70 if (localPlayerMode->isAdventureRestricted()) {
71 if (!minecraft->player->mayDestroyBlockAt(x, y, z)) {
72 return false;
73 }
74 }
75
76 if (localPlayerMode->isCreative())
77 {
78 if (minecraft->player->getCarriedItem() != NULL && dynamic_cast<WeaponItem *>(minecraft->player->getCarriedItem()->getItem()) != NULL)
79 {
80 return false;
81 }
82 }
83
84 Level *level = minecraft->level;
85 Tile *oldTile = Tile::tiles[level->getTile(x, y, z)];
86
87 if (oldTile == NULL) return false;
88
89#ifdef _WINDOWS64
90 if (g_NetworkManager.IsHost())
91 {
92 level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT));
93 return true;
94 }
95#endif
96
97 level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT));
98
99 int data = level->getData(x, y, z);
100 bool changed = level->removeTile(x, y, z);
101 if (changed)
102 {
103 oldTile->destroy(level, x, y, z, data);
104 }
105 yDestroyBlock = -1;
106
107 if (!localPlayerMode->isCreative())
108 {
109 shared_ptr<ItemInstance> item = minecraft->player->getSelectedItem();
110 if (item != NULL)
111 {
112 item->mineBlock(level, oldTile->id, x, y, z, minecraft->player);
113 if (item->count == 0)
114 {
115 minecraft->player->removeSelectedItem();
116 }
117 }
118 }
119
120 return changed;
121}
122
123void MultiPlayerGameMode::startDestroyBlock(int x, int y, int z, int face)
124{
125 if(!minecraft->player->isAllowedToMine()) return;
126
127 if (localPlayerMode->isAdventureRestricted())
128 {
129 if (!minecraft->player->mayDestroyBlockAt(x, y, z))
130 {
131 return;
132 }
133 }
134
135 if (localPlayerMode->isCreative())
136 {
137 connection->send(shared_ptr<PlayerActionPacket>( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) ));
138 creativeDestroyBlock(minecraft, this, x, y, z, face);
139 destroyDelay = 5;
140 }
141 else if (!isDestroying || !sameDestroyTarget(x, y, z))
142 {
143 if (isDestroying)
144 {
145 connection->send(shared_ptr<PlayerActionPacket>(new PlayerActionPacket(PlayerActionPacket::ABORT_DESTROY_BLOCK, xDestroyBlock, yDestroyBlock, zDestroyBlock, face)));
146 }
147 connection->send( shared_ptr<PlayerActionPacket>( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) ) );
148 int t = minecraft->level->getTile(x, y, z);
149 if (t > 0 && destroyProgress == 0) Tile::tiles[t]->attack(minecraft->level, x, y, z, minecraft->player);
150 if (t > 0 &&
151 (Tile::tiles[t]->getDestroyProgress(minecraft->player, minecraft->player->level, x, y, z) >= 1
152 // ||(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_InstantDestroy))
153 )
154 )
155 {
156 destroyBlock(x, y, z, face);
157 }
158 else
159 {
160 isDestroying = true;
161 xDestroyBlock = x;
162 yDestroyBlock = y;
163 zDestroyBlock = z;
164 destroyingItem = minecraft->player->getCarriedItem();
165 destroyProgress = 0;
166 destroyTicks = 0;
167 minecraft->level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, (int)(destroyProgress * 10) - 1);
168 }
169 }
170
171}
172
173void MultiPlayerGameMode::stopDestroyBlock()
174{
175 if (isDestroying)
176 {
177 connection->send(shared_ptr<PlayerActionPacket>(new PlayerActionPacket(PlayerActionPacket::ABORT_DESTROY_BLOCK, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1)));
178 }
179
180 isDestroying = false;
181 destroyProgress = 0;
182 minecraft->level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1);
183}
184
185void MultiPlayerGameMode::continueDestroyBlock(int x, int y, int z, int face)
186{
187 if(!minecraft->player->isAllowedToMine()) return;
188 ensureHasSentCarriedItem();
189// connection.send(new PlayerActionPacket(PlayerActionPacket.CONTINUE_DESTROY_BLOCK, x, y, z, face));
190
191 if (destroyDelay > 0)
192 {
193 destroyDelay--;
194 return;
195 }
196
197 if (localPlayerMode->isCreative())
198 {
199 destroyDelay = 5;
200 connection->send(shared_ptr<PlayerActionPacket>( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) ) );
201 creativeDestroyBlock(minecraft, this, x, y, z, face);
202 return;
203 }
204
205 if (sameDestroyTarget(x, y, z))
206 {
207 int t = minecraft->level->getTile(x, y, z);
208 if (t == 0)
209 {
210 isDestroying = false;
211 return;
212 }
213 Tile *tile = Tile::tiles[t];
214
215 destroyProgress += tile->getDestroyProgress(minecraft->player, minecraft->player->level, x, y, z);
216
217 if (destroyTicks % 4 == 0)
218 {
219 if (tile != NULL)
220 {
221 int iStepSound=tile->soundType->getStepSound();
222
223 minecraft->soundEngine->play(iStepSound, x + 0.5f, y + 0.5f, z + 0.5f, (tile->soundType->getVolume() + 1) / 8, tile->soundType->getPitch() * 0.5f);
224 }
225 }
226
227 destroyTicks++;
228
229 if (destroyProgress >= 1)
230 {
231 isDestroying = false;
232 connection->send( shared_ptr<PlayerActionPacket>( new PlayerActionPacket(PlayerActionPacket::STOP_DESTROY_BLOCK, x, y, z, face) ) );
233 destroyBlock(x, y, z, face);
234 destroyProgress = 0;
235 destroyTicks = 0;
236 destroyDelay = 5;
237 }
238
239 minecraft->level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, (int)(destroyProgress * 10) - 1);
240 }
241 else
242 {
243 startDestroyBlock(x, y, z, face);
244 }
245
246}
247
248float MultiPlayerGameMode::getPickRange()
249{
250 if (localPlayerMode->isCreative())
251 {
252 return 5.0f;
253 }
254 return 4.5f;
255}
256
257void MultiPlayerGameMode::tick()
258{
259 ensureHasSentCarriedItem();
260 //minecraft->soundEngine->playMusicTick();
261}
262
263bool MultiPlayerGameMode::sameDestroyTarget(int x, int y, int z)
264{
265 shared_ptr<ItemInstance> selected = minecraft->player->getCarriedItem();
266 bool sameItems = destroyingItem == NULL && selected == NULL;
267 if (destroyingItem != NULL && selected != NULL)
268 {
269 sameItems =
270 selected->id == destroyingItem->id &&
271 ItemInstance::tagMatches(selected, destroyingItem) &&
272 (selected->isDamageableItem() || selected->getAuxValue() == destroyingItem->getAuxValue());
273 }
274 return x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock && sameItems;
275}
276
277void MultiPlayerGameMode::ensureHasSentCarriedItem()
278{
279 int newItem = minecraft->player->inventory->selected;
280 if (newItem != carriedItem)
281 {
282 carriedItem = newItem;
283 connection->send( shared_ptr<SetCarriedItemPacket>( new SetCarriedItemPacket(carriedItem) ) );
284 }
285}
286
287bool MultiPlayerGameMode::useItemOn(shared_ptr<Player> player, Level *level, shared_ptr<ItemInstance> item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly, bool *pbUsedItem)
288{
289 if( pbUsedItem ) *pbUsedItem = false; // Did we actually use the held item?
290
291 // 4J-PB - Adding a test only version to allow tooltips to be displayed
292 if(!bTestUseOnly)
293 {
294 ensureHasSentCarriedItem();
295 }
296 float clickX = (float) hit->x - x;
297 float clickY = (float) hit->y - y;
298 float clickZ = (float) hit->z - z;
299 bool didSomething = false;
300
301 if (!player->isSneaking() || player->getCarriedItem() == NULL)
302 {
303 int t = level->getTile(x, y, z);
304 if (t > 0 && player->isAllowedToUse(Tile::tiles[t]))
305 {
306 if(bTestUseOnly)
307 {
308 switch(t)
309 {
310 case Tile::jukebox_Id:
311 case Tile::bed_Id: // special case for a bed
312 if (Tile::tiles[t]->TestUse(level, x, y, z, player ))
313 {
314 return true;
315 }
316 else if (t==Tile::bed_Id) // 4J-JEV: You can still use items on record players (ie. set fire to them).
317 {
318 // bed is too far away, or something
319 return false;
320 }
321 break;
322 default:
323 if (Tile::tiles[t]->TestUse()) return true;
324 break;
325 }
326 }
327 else
328 {
329 if (Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ)) didSomething = true;
330 }
331 }
332 }
333
334 if (!didSomething && item != NULL && dynamic_cast<TileItem *>(item->getItem()))
335 {
336 TileItem *tile = dynamic_cast<TileItem *>(item->getItem());
337 if (!tile->mayPlace(level, x, y, z, face, player, item)) return false;
338 }
339
340 // 4J Stu - In Java we send the use packet before the above check for item being NULL
341 // so the following never gets executed but the packet still gets sent (for opening chests etc)
342 if(item != NULL)
343 {
344 if(!didSomething && player->isAllowedToUse(item))
345 {
346 if (localPlayerMode->isCreative())
347 {
348 int aux = item->getAuxValue();
349 int count = item->count;
350 didSomething = item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnly);
351 item->setAuxValue(aux);
352 item->count = count;
353 }
354 else
355 {
356 didSomething = item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnly);
357 }
358 if( didSomething )
359 {
360 if( pbUsedItem ) *pbUsedItem = true;
361 }
362 }
363 }
364 else
365 {
366 int t = level->getTile(x, y, z);
367 // 4J - Bit of a hack, however seems preferable to any larger changes which would have more chance of causing unwanted side effects.
368 // If we aren't going to be actually performing the use method locally, then call this method with its "soundOnly" parameter set to true.
369 // This is an addition from the java version, and as its name suggests, doesn't actually perform the use locally but just makes any sounds that
370 // are meant to be directly caused by this. If we don't do this, then the sounds never happen as the tile's use method is only called on the
371 // server, and that won't allow any sounds that are directly made, or broadcast back level events to us that would make the sound, since we are
372 // the source of the event.
373 if( ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) )
374 {
375 Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ, true);
376 }
377 }
378
379 // 4J Stu - Do the action before we send the packet, so that our predicted count is sent in the packet and the server
380 // doesn't think it has to update us
381 // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water.
382 if(!bTestUseOnly)
383 {
384 connection->send( shared_ptr<UseItemPacket>( new UseItemPacket(x, y, z, face, player->inventory->getSelected(), clickX, clickY, clickZ) ) );
385 }
386 return didSomething;
387}
388
389bool MultiPlayerGameMode::useItem(shared_ptr<Player> player, Level *level, shared_ptr<ItemInstance> item, bool bTestUseOnly)
390{
391 if(!player->isAllowedToUse(item)) return false;
392
393 // 4J-PB - Adding a test only version to allow tooltips to be displayed
394 if(!bTestUseOnly)
395 {
396 ensureHasSentCarriedItem();
397 }
398
399 // 4J Stu - Do the action before we send the packet, so that our predicted count is sent in the packet and the server
400 // doesn't think it has to update us, or can update us if we are wrong
401 // Fix for #13120 - Using a bucket of water or lava in the spawn area (centre of the map) causes the inventory to get out of sync
402 bool result = false;
403
404 // 4J-PB added for tooltips to test use only
405 if(bTestUseOnly)
406 {
407 result = item->TestUse(item, level, player);
408 }
409 else
410 {
411 int oldCount = item->count;
412 shared_ptr<ItemInstance> itemInstance = item->use(level, player);
413 if ((itemInstance != NULL && itemInstance != item) || (itemInstance != NULL && itemInstance->count != oldCount))
414 {
415 player->inventory->items[player->inventory->selected] = itemInstance;
416 if (itemInstance->count == 0)
417 {
418 player->inventory->items[player->inventory->selected] = nullptr;
419 }
420 result = true;
421 }
422 }
423
424 if(!bTestUseOnly)
425 {
426 connection->send( shared_ptr<UseItemPacket>( new UseItemPacket(-1, -1, -1, 255, player->inventory->getSelected(), 0, 0, 0) ) );
427 }
428 return result;
429}
430
431shared_ptr<MultiplayerLocalPlayer> MultiPlayerGameMode::createPlayer(Level *level)
432{
433 return shared_ptr<MultiplayerLocalPlayer>( new MultiplayerLocalPlayer(minecraft, level, minecraft->user, connection) );
434}
435
436void MultiPlayerGameMode::attack(shared_ptr<Player> player, shared_ptr<Entity> entity)
437{
438 ensureHasSentCarriedItem();
439 connection->send( shared_ptr<InteractPacket>( new InteractPacket(player->entityId, entity->entityId, InteractPacket::ATTACK) ) );
440 player->attack(entity);
441}
442
443bool MultiPlayerGameMode::interact(shared_ptr<Player> player, shared_ptr<Entity> entity)
444{
445 ensureHasSentCarriedItem();
446 connection->send(shared_ptr<InteractPacket>( new InteractPacket(player->entityId, entity->entityId, InteractPacket::INTERACT) ) );
447 return player->interact(entity);
448}
449
450shared_ptr<ItemInstance> MultiPlayerGameMode::handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, bool quickKeyHeld, shared_ptr<Player> player)
451{
452 short changeUid = player->containerMenu->backup(player->inventory);
453
454 shared_ptr<ItemInstance> clicked = player->containerMenu->clicked(slotNum, buttonNum, quickKeyHeld?AbstractContainerMenu::CLICK_QUICK_MOVE:AbstractContainerMenu::CLICK_PICKUP, player);
455 connection->send( shared_ptr<ContainerClickPacket>( new ContainerClickPacket(containerId, slotNum, buttonNum, quickKeyHeld, clicked, changeUid) ) );
456
457 return clicked;
458}
459
460void MultiPlayerGameMode::handleInventoryButtonClick(int containerId, int buttonId)
461{
462 connection->send(shared_ptr<ContainerButtonClickPacket>( new ContainerButtonClickPacket(containerId, buttonId) ));
463}
464
465void MultiPlayerGameMode::handleCreativeModeItemAdd(shared_ptr<ItemInstance> clicked, int slot)
466{
467 if (localPlayerMode->isCreative())
468 {
469 connection->send(shared_ptr<SetCreativeModeSlotPacket>( new SetCreativeModeSlotPacket(slot, clicked) ) );
470 }
471}
472
473void MultiPlayerGameMode::handleCreativeModeItemDrop(shared_ptr<ItemInstance> clicked)
474{
475 if (localPlayerMode->isCreative() && clicked != NULL)
476 {
477 connection->send(shared_ptr<SetCreativeModeSlotPacket>( new SetCreativeModeSlotPacket(-1, clicked) ) );
478 }
479}
480
481void MultiPlayerGameMode::releaseUsingItem(shared_ptr<Player> player)
482{
483 ensureHasSentCarriedItem();
484 connection->send(shared_ptr<PlayerActionPacket>( new PlayerActionPacket(PlayerActionPacket::RELEASE_USE_ITEM, 0, 0, 0, 255) ) );
485 player->releaseUsingItem();
486}
487
488bool MultiPlayerGameMode::hasExperience()
489{
490 return localPlayerMode->isSurvival();
491}
492
493bool MultiPlayerGameMode::hasMissTime()
494{
495 return !localPlayerMode->isCreative();
496}
497
498bool MultiPlayerGameMode::hasInfiniteItems()
499{
500 return localPlayerMode->isCreative();
501}
502
503bool MultiPlayerGameMode::hasFarPickRange()
504{
505 return localPlayerMode->isCreative();
506}
507
508// Returns true when the inventory is opened from the server-side. Currently
509// only happens when the player is riding a horse.
510bool MultiPlayerGameMode::isServerControlledInventory()
511{
512 return minecraft->player->isRiding() && minecraft->player->riding->instanceof(eTYPE_HORSE);
513}
514
515bool MultiPlayerGameMode::handleCraftItem(int recipe, shared_ptr<Player> player)
516{
517 short changeUid = player->containerMenu->backup(player->inventory);
518
519 connection->send( shared_ptr<CraftItemPacket>( new CraftItemPacket(recipe, changeUid) ) );
520
521 return true;
522}
523
524void MultiPlayerGameMode::handleDebugOptions(unsigned int uiVal, shared_ptr<Player> player)
525{
526 player->SetDebugOptions(uiVal);
527 connection->send( shared_ptr<DebugOptionsPacket>( new DebugOptionsPacket(uiVal) ) );
528}