aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/ServerPlayerGameMode.cpp
blob: 2e6bca35e00cc5024da464a088f114710efeda5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
#include "stdafx.h"
#include "ServerPlayerGameMode.h"
#include "ServerLevel.h"
#include "ServerPlayer.h"
#include "PlayerConnection.h"
#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\net.minecraft.network.packet.h"
#include "..\Minecraft.World\net.minecraft.world.level.h"
#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
#include "MultiPlayerLevel.h"
#include "LevelRenderer.h"

ServerPlayerGameMode::ServerPlayerGameMode(Level *level)
{
	// 4J - added initialisers
	isDestroyingBlock = false;
	destroyProgressStart = 0;
	xDestroyBlock = yDestroyBlock = zDestroyBlock = 0;
	gameTicks = 0;
	hasDelayedDestroy = false;
	delayedDestroyX = delayedDestroyY = delayedDestroyZ = 0;
	delayedTickStart = 0;
	lastSentState = -1;
	gameModeForPlayer = GameType::NOT_SET;

	this->level = level;

	// 4J Added
	m_gameRules = NULL;
}

ServerPlayerGameMode::~ServerPlayerGameMode()
{
	if(m_gameRules!=NULL) delete m_gameRules;
}

void ServerPlayerGameMode::setGameModeForPlayer(GameType *gameModeForPlayer)
{
	this->gameModeForPlayer = gameModeForPlayer;

	gameModeForPlayer->updatePlayerAbilities(&(player->abilities));
	player->onUpdateAbilities();

}

GameType *ServerPlayerGameMode::getGameModeForPlayer()
{
	return gameModeForPlayer;
}

bool ServerPlayerGameMode::isSurvival()
{
	return gameModeForPlayer->isSurvival();
}

bool ServerPlayerGameMode::isCreative()
{
	return gameModeForPlayer->isCreative();
}

void ServerPlayerGameMode::updateGameMode(GameType *gameType)
{
	if (gameModeForPlayer == GameType::NOT_SET)
	{
		gameModeForPlayer = gameType;
	}
	setGameModeForPlayer(gameModeForPlayer);
}

void ServerPlayerGameMode::tick()
{
	gameTicks++;

	if (hasDelayedDestroy)
	{
		int ticksSpentDestroying = gameTicks - delayedTickStart;
		int t = level->getTile(delayedDestroyX, delayedDestroyY, delayedDestroyZ);
		if (t == 0)
		{
			hasDelayedDestroy = false;
		}
		else
		{
			Tile *tile = Tile::tiles[t];
			float destroyProgress = tile->getDestroyProgress(player, player->level, delayedDestroyX, delayedDestroyY, delayedDestroyZ) * (ticksSpentDestroying + 1);
			int state = (int) (destroyProgress * 10);

			if (state != lastSentState)
			{
				level->destroyTileProgress(player->entityId, delayedDestroyX, delayedDestroyY, delayedDestroyZ, state);
				lastSentState = state;
			}
			if (destroyProgress >= 1)
			{
				hasDelayedDestroy = false;
				destroyBlock(delayedDestroyX, delayedDestroyY, delayedDestroyZ);
			}
		}
	}
	else if (isDestroyingBlock)
	{
		int t = level->getTile(xDestroyBlock, yDestroyBlock, zDestroyBlock);
		Tile *tile = Tile::tiles[t];

		if (tile == NULL)
		{
			level->destroyTileProgress(player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1);
			lastSentState = -1;
			isDestroyingBlock = false;
		}
		else
		{
			int ticksSpentDestroying = gameTicks - destroyProgressStart;
			float destroyProgress = tile->getDestroyProgress(player, player->level, xDestroyBlock, yDestroyBlock, zDestroyBlock) * (ticksSpentDestroying + 1);
			int state = (int) (destroyProgress * 10);

			if (state != lastSentState)
			{
				level->destroyTileProgress(player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, state);
				lastSentState = state;
			}
		}
	}
}

void ServerPlayerGameMode::startDestroyBlock(int x, int y, int z, int face)
{
	if(!player->isAllowedToMine()) return;

	if (gameModeForPlayer->isAdventureRestricted())
	{
		if (!player->mayDestroyBlockAt(x, y, z))
		{
			return;
		}
	}

	if (isCreative())
	{
		if(!level->extinguishFire(nullptr, x, y, z, face))
		{
			destroyBlock(x, y, z);
		}
		return;
	}
	level->extinguishFire(player, x, y, z, face);
	destroyProgressStart = gameTicks;
	float progress = 1.0f;
	int t = level->getTile(x, y, z);
	if (t > 0)
	{
		Tile::tiles[t]->attack(level, x, y, z, player);
		progress = Tile::tiles[t]->getDestroyProgress(player, player->level, x, y, z);
	}

	if (t > 0 && (progress >= 1 ) ) //|| (app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<<eDebugSetting_InstantDestroy) ) )))
	{
		destroyBlock(x, y, z);
	}
	else
	{
		isDestroyingBlock = true;
		xDestroyBlock = x;
		yDestroyBlock = y;
		zDestroyBlock = z;
		int state = (int) (progress * 10);
		level->destroyTileProgress(player->entityId, x, y, z, state);
		lastSentState = state;
	}
}

void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{
	if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
	{
		//         int ticksSpentDestroying = gameTicks - destroyProgressStart;

		int t = level->getTile(x, y, z);
		if (t != 0)
		{
			Tile *tile = Tile::tiles[t];

			// MGH -	removed checking for the destroy progress here, it has already been checked on the client before it sent the packet.
			//			fixes issues with this failing to destroy because of packets bunching up
			//             float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
			//             if (destroyProgress >= .7f || bIgnoreDestroyProgress)
			{
				isDestroyingBlock = false;
				level->destroyTileProgress(player->entityId, x, y, z, -1);
				destroyBlock(x, y, z);
			}
			// 			else if (!hasDelayedDestroy)
			// 			{
			// 				isDestroyingBlock = false;
			//                 hasDelayedDestroy = true;
			//                 delayedDestroyX = x;
			//                 delayedDestroyY = y;
			//                 delayedDestroyZ = z;
			//                 delayedTickStart = destroyProgressStart;
			//             }
		}
	}
}

void ServerPlayerGameMode::abortDestroyBlock(int x, int y, int z)
{
	isDestroyingBlock = false;
	level->destroyTileProgress(player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1);
}

bool ServerPlayerGameMode::superDestroyBlock(int x, int y, int z)
{
	Tile *oldTile = Tile::tiles[level->getTile(x, y, z)];
	int data = level->getData(x, y, z);

	if (oldTile != NULL)
	{
		oldTile->playerWillDestroy(level, x, y, z, data, player);
	}

	bool changed = level->removeTile(x, y, z);
	if (oldTile != NULL && changed)
	{
		oldTile->destroy(level, x, y, z, data);
	}
	return changed;
}

bool ServerPlayerGameMode::destroyBlock(int x, int y, int z)
{
	if (gameModeForPlayer->isAdventureRestricted())
	{
		if (!player->mayDestroyBlockAt(x, y, z))
		{
			return false;
		}
	}

	if (gameModeForPlayer->isCreative())
	{
		if (player->getCarriedItem() != NULL && dynamic_cast<WeaponItem *>(player->getCarriedItem()->getItem()) != NULL)
		{
			return false;
		}
	}

	int t = level->getTile(x, y, z);
	int data = level->getData(x, y, z);

	level->levelEvent(player, LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, t + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT));

	// 4J - In creative mode, the point where we need to tell the renderer that we are about to destroy a tile via destroyingTileAt is quite complicated.
	// If the player being told is remote, then we always want the client to do it as it does the final update. If the player being told is local,
	// then we need to update the renderer Here if we are sharing data between host & client as this is the final point where the original data is still intact.
	// If the player being told is local, and we aren't sharing data between host & client, then we can just treat it as if it is a remote player and
	// it can update the renderer.
	bool clientToUpdateRenderer = false;
	if( isCreative() )
	{
		clientToUpdateRenderer = true;
		if( dynamic_pointer_cast<ServerPlayer>(player)->connection->isLocal() )
		{
			// Establish whether we are sharing this chunk between client & server
			MultiPlayerLevel *clientLevel = Minecraft::GetInstance()->getLevel(level->dimension->id);
			if( clientLevel )
			{
				LevelChunk *lc = clientLevel->getChunkAt( x, z );
#ifdef SHARING_ENABLED
				if( lc->sharingTilesAndData )
				{
					// We are sharing - this is the last point we can tell the renderer
					Minecraft::GetInstance()->levelRenderer->destroyedTileManager->destroyingTileAt( clientLevel, x, y, z );

					// Don't need to ask the client to do this too
					clientToUpdateRenderer = false;
				}
#endif
			}
		}
	}

	bool changed = superDestroyBlock(x, y, z);

	if (isCreative())
	{
		shared_ptr<TileUpdatePacket> tup = shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) );
		// 4J - a bit of a hack here, but if we want to tell the client that it needs to inform the renderer of a block being destroyed, then send a block 255 instead of a 0. This is handled in ClientConnection::handleTileUpdate
		if( tup->block == 0 )
		{
			if( clientToUpdateRenderer ) tup->block = 255;
		}
		player->connection->send( tup );
	}
	else 
	{
		shared_ptr<ItemInstance> item = player->getSelectedItem();
		bool canDestroy = player->canDestroy(Tile::tiles[t]);
		if (item != NULL)
		{
			item->mineBlock(level, t, x, y, z, player);
			if (item->count == 0)
			{
				player->removeSelectedItem();
			}
		}
		if (changed && canDestroy)
		{
			Tile::tiles[t]->playerDestroy(level, player, x, y, z, data);
		}
	}
	return changed;

}

bool ServerPlayerGameMode::useItem(shared_ptr<Player> player, Level *level, shared_ptr<ItemInstance> item, bool bTestUseOnly)
{
	if(!player->isAllowedToUse(item)) return false;

	int oldCount = item->count;
	int oldAux = item->getAuxValue();
	shared_ptr<ItemInstance> itemInstance = item->use(level, player);
	if (itemInstance != item || (itemInstance != NULL && (itemInstance->count != oldCount || itemInstance->getUseDuration() > 0 || itemInstance->getAuxValue() != oldAux)))
	{
		player->inventory->items[player->inventory->selected] = itemInstance;
		if (isCreative())
		{
			itemInstance->count = oldCount;
			if (itemInstance->isDamageableItem()) itemInstance->setAuxValue(oldAux);
		}
		if (itemInstance->count == 0)
		{
			player->inventory->items[player->inventory->selected] = nullptr;
		}
		if (!player->isUsingItem())
		{
			dynamic_pointer_cast<ServerPlayer>(player)->refreshContainer(player->inventoryMenu);
		}
		return true;
	}
	return false;

}

bool ServerPlayerGameMode::useItemOn(shared_ptr<Player> player, Level *level, shared_ptr<ItemInstance> item, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly, bool *pbUsedItem)
{
	// 4J-PB - Adding a test only version to allow tooltips to be displayed
	int t = level->getTile(x, y, z);
	if (!player->isSneaking() || player->getCarriedItem() == NULL)
	{
		if (t > 0 && player->isAllowedToUse(Tile::tiles[t]))
		{
			if(bTestUseOnOnly)
			{
				if (Tile::tiles[t]->TestUse()) return true;
			}
			else 
			{
				if (Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ))
				{
					if(m_gameRules != NULL) m_gameRules->onUseTile(t,x,y,z);
					return true;
				}
			}
		}
	}

	if (item == NULL || !player->isAllowedToUse(item)) return false;
	if (isCreative())
	{
		int aux = item->getAuxValue();
		int count = item->count;
		bool success = item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ);
		item->setAuxValue(aux);
		item->count = count;
		return success;
	}
	else
	{
		return item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnOnly);
	}
}

void ServerPlayerGameMode::setLevel(ServerLevel *newLevel)
{
	level = newLevel;
}

// 4J Added
void ServerPlayerGameMode::setGameRules(GameRulesInstance *rules)
{
	if(m_gameRules != NULL) delete m_gameRules;
	m_gameRules = rules;
}