1 
2 /**
3  *
4  * @file clientgame.cpp
5  *
6  * Part of the OpenJazz project
7  *
8  * @par History:
9  * - 23rd August 2005: Created level.c and menu.c
10  * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp
11  * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp
12  * - 18th July 2009: Created clientgame.cpp from parts of game.cpp
13  *
14  * @par Licence:
15  * Copyright (c) 2005-2017 Alister Thomson
16  *
17  * OpenJazz is distributed under the terms of
18  * the GNU General Public License, version 2.0
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25 
26 
27 #include "game.h"
28 #include "gamemode.h"
29 
30 #include "io/controls.h"
31 #include "io/file.h"
32 #include "io/gfx/font.h"
33 #include "io/gfx/video.h"
34 #include "io/network.h"
35 #include "player/player.h"
36 #include "loop.h"
37 #include "setup.h"
38 #include "util.h"
39 
40 #include <string.h>
41 
42 
43 /**
44  * Create game client
45  *
46  * @param address Address of the server to which to connect
47  */
ClientGame(char * address)48 ClientGame::ClientGame (char* address) {
49 
50 	unsigned char buffer[BUFFER_LENGTH];
51 	unsigned int timeout;
52 	int count, ret;
53 	GameModeType modeType;
54 
55 	sock = net->join(address);
56 
57 	if (sock < 0) throw sock; // Tee hee hee hee hee.
58 
59 
60 	// Receive initialisation message
61 
62 	count = 0;
63 	timeout = globalTicks + T_SCHECK + T_TIMEOUT;
64 
65 	// Wait for whole message to arrive
66 	while (count < MTL_G_PROPS) {
67 
68 		if (loop(NORMAL_LOOP) == E_QUIT) {
69 
70 			net->close(sock);
71 
72 			throw E_QUIT;
73 
74 		}
75 
76 		if (controls.release(C_ESCAPE)) {
77 
78 			net->close(sock);
79 
80 			throw E_RETURN;
81 
82 		}
83 
84 		SDL_Delay(T_MENU_FRAME);
85 
86 		video.clearScreen(0);
87 		fontmn2->showString("WAITING FOR REPLY", canvasW >> 2, (canvasH >> 1) - 16);
88 
89 		ret = net->recv(sock, buffer + count, MTL_G_PROPS - count);
90 
91 		if (ret > 0) count += ret;
92 
93 		if (globalTicks > timeout) {
94 
95 			net->close(sock);
96 
97 			throw E_TIMEOUT;
98 
99 		}
100 
101 	}
102 
103 	// Make sure message is valid
104 	if (buffer[1] != MT_G_PROPS) {
105 
106 		net->close(sock);
107 
108 		throw E_DATA;
109 
110 	} else if (buffer[2] != 1) {
111 
112 		net->close(sock);
113 
114 		throw E_VERSION;
115 
116 	}
117 
118 	printf("Connected to server (version %d).\n", buffer[2]);
119 
120 	// Copy game parameters
121 	modeType = GameModeType(buffer[3]);
122 	difficulty = buffer[4];
123 	maxPlayers = buffer[5];
124 	nPlayers = buffer[6];
125 	clientID = buffer[7];
126 
127 	printf("Game mode %d, difficulty %d, %d of %d players.\n", modeType, difficulty, nPlayers, maxPlayers);
128 
129 	if (nPlayers > maxPlayers) {
130 
131 		net->close(sock);
132 
133 		throw E_DATA;
134 
135 	}
136 
137 
138 	mode = createMode(modeType);
139 
140 	if (!mode) {
141 
142 		net->close(sock);
143 
144 		throw E_DATA;
145 
146 	}
147 
148 
149 	// Create players
150 	nPlayers = 0;
151 	players = new Player[maxPlayers];
152 
153 
154 	// Download the level from the server
155 
156 	levelFile = createString(LEVEL_FILE);
157 	file = NULL;
158 
159 	ret = setLevel(NULL);
160 
161 	if (ret < 0) {
162 
163 		net->close(sock);
164 
165 		if (file) delete file;
166 
167 		delete mode;
168 
169 		throw ret;
170 
171 	}
172 
173 	// Add a new player to the game
174 
175 	buffer[0] = MTL_G_PJOIN + strlen(setup.characterName);
176 	buffer[1] = MT_G_PJOIN;
177 	buffer[2] = clientID;
178 	buffer[3] = 0; // Player's number, assigned by the server
179 	buffer[4] = 0; // Player's team, assigned by the server
180 	memcpy(buffer + 5, setup.characterCols, 4);
181 	memcpy(buffer + 9, setup.characterName, strlen(setup.characterName) + 1);
182 	send(buffer);
183 
184 
185 	// Wait for acknowledgement
186 
187 	localPlayer = NULL;
188 
189 	while (!localPlayer) {
190 
191 		if (loop(NORMAL_LOOP) == E_QUIT) {
192 
193 			net->close(sock);
194 
195 			if (file) delete file;
196 
197 			delete mode;
198 
199 			throw E_QUIT;
200 
201 		}
202 
203 		if (controls.release(C_ESCAPE)) {
204 
205 			net->close(sock);
206 
207 			if (file) delete file;
208 
209 			delete mode;
210 
211 			throw E_RETURN;
212 
213 		}
214 
215 		video.clearScreen(0);
216 		fontmn2->showString("JOINING GAME", canvasW >> 2, (canvasH >> 1) - 16);
217 
218 		ret = step(0);
219 
220 		if (ret < 0) {
221 
222 			net->close(sock);
223 
224 			if (file) delete file;
225 
226 			delete mode;
227 
228 			throw ret;
229 
230 		}
231 
232 	}
233 
234 	return;
235 
236 }
237 
238 
239 /**
240  * Disconnect and destroy client
241  */
~ClientGame()242 ClientGame::~ClientGame () {
243 
244 	net->close(sock);
245 
246 	if (file) delete file;
247 
248 	delete mode;
249 
250 	return;
251 
252 }
253 
254 
255 /**
256  * Set the next level and receive level data from server
257  *
258  * @param fileName The file name of the next level
259  *
260  * @return Error code
261  */
setLevel(char * fileName)262 int ClientGame::setLevel (char* fileName) {
263 
264 	(void)fileName;
265 
266 	int ret;
267 
268 	video.setPalette(menuPalette);
269 
270 	// Wait for level data to start arriving
271 	while (!file && levelFile) {
272 
273 		if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
274 
275 		if (controls.release(C_ESCAPE)) return E_RETURN;
276 
277 		SDL_Delay(T_MENU_FRAME);
278 
279 		video.clearScreen(0);
280 		fontmn2->showString("WAITING FOR SERVER", canvasW >> 2, (canvasH >> 1) - 16);
281 
282 		ret = step(0);
283 
284 		if (ret < 0) return ret;
285 
286 	}
287 
288 	// Wait for level data to finish arriving
289 	while (file && levelFile) {
290 
291 		if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT;
292 
293 		if (controls.release(C_ESCAPE)) return E_RETURN;
294 
295 		SDL_Delay(T_MENU_FRAME);
296 
297 		video.clearScreen(0);
298 		fontmn2->showString("downloaded", canvasW >> 2, (canvasH >> 1) - 16);
299 		fontmn2->showNumber(file->tell(), (canvasW >> 2) + 56, canvasH >> 1);
300 		fontmn2->showString("bytes", (canvasW >> 2) + 64, canvasH >> 1);
301 
302 		ret = step(0);
303 
304 		if (ret < 0) return ret;
305 
306 	}
307 
308 	return E_NONE;
309 
310 }
311 
312 
313 /**
314  * Send data to server
315  *
316  * @param buffer Data to send. First byte indicates length.
317  */
send(unsigned char * buffer)318 void ClientGame::send (unsigned char* buffer) {
319 
320 	net->send(sock, buffer);
321 
322 	return;
323 
324 }
325 
326 
327 /**
328  * Game iteration
329  *
330  * @param ticks Current time
331  *
332  * @return Error code
333  */
step(unsigned int ticks)334 int ClientGame::step (unsigned int ticks) {
335 
336 	unsigned char sendBuffer[BUFFER_LENGTH];
337 	int length, count;
338 	bool firstMessage;
339 
340 	// Receive data from server
341 
342 	if (received == 0) {
343 
344 		// Not currently receiving a message
345 		// See if there is a new message to receive
346 
347 		length = net->recv(sock, recvBuffer, 1);
348 
349 		if (length > 0) received++;
350 
351 	}
352 
353 	if (received > 0) {
354 
355 		// Currently receiving a message
356 		// See if there is any more data
357 
358 		length = net->recv(sock, recvBuffer + received,
359 			recvBuffer[0] - received);
360 
361 		if (length > 0) received += length;
362 
363 
364 		// See if the whole message has arrived
365 
366 		if (received >= recvBuffer[0]) {
367 
368 			switch (recvBuffer[1] & MCMASK) {
369 
370 				case MC_GAME:
371 
372 					if (recvBuffer[1] == MT_G_LEVEL) {
373 
374 						if (!file) {
375 
376 							// Not already storing level data, so open the file
377 
378 							try {
379 
380 								file = new File(levelFile, true);
381 
382 							} catch (int e) {
383 
384 								return e;
385 
386 							}
387 
388 							firstMessage = true;
389 
390 						} else firstMessage = false;
391 
392 						file->seek((recvBuffer[2] << 8) + recvBuffer[3], true);
393 
394 						for (count = 4; count < recvBuffer[0]; count++)
395 							file->storeChar(recvBuffer[count]);
396 
397 						// If a zero-length block has been sent, it is the last
398 						if (recvBuffer[0] == MTL_G_LEVEL) {
399 
400 							if (firstMessage) {
401 
402 								// If the last message was also the first,
403 								// then the run of levels has ended
404 
405 								delete[] levelFile;
406 								levelFile = NULL;
407 
408 							}
409 
410 							delete file;
411 							file = NULL;
412 
413 						}
414 
415 						break;
416 
417 					}
418 
419 					if ((recvBuffer[1] == MT_G_PJOIN) &&
420 						(recvBuffer[3] < maxPlayers)) {
421 
422 						printf("Player %d joined the game.\n", recvBuffer[3]);
423 
424 						// Add the new player, and any that have been missed
425 
426 						for (count = nPlayers; count <= recvBuffer[3]; count++) {
427 
428 							players[count].init(this, (char *)recvBuffer + 9,
429 								recvBuffer + 5, recvBuffer[4]);
430 							addLevelPlayer(players + count);
431 
432 							printf("Player %d joined team %d.\n", count, recvBuffer[4]);
433 
434 						}
435 
436 						nPlayers = count;
437 
438 						if (recvBuffer[2] == clientID)
439 							localPlayer = players + recvBuffer[3];
440 
441 					}
442 
443 					if ((recvBuffer[1] == MT_G_PQUIT) &&
444 						(recvBuffer[2] < nPlayers)) {
445 
446 						printf("Player %d left the game.\n", recvBuffer[2]);
447 
448 						// Remove the player
449 
450 						players[recvBuffer[2]].deinit();
451 
452 						// If necessary, move more recent players
453 						for (count = recvBuffer[2]; count < nPlayers; count++)
454 							memcpy(players + count, players + count + 1,
455 								sizeof(Player));
456 
457 						// Clear duplicate pointers
458 						memset(players + nPlayers, 0, sizeof(Player));
459 
460 					}
461 
462 					if (recvBuffer[1] == MT_G_CHECK) {
463 
464 						checkX = recvBuffer[2];
465 						checkY = recvBuffer[3];
466 
467 						if (recvBuffer[0] > 4) {
468 
469 							checkX += recvBuffer[4] << 8;
470 							checkY += recvBuffer[5] << 8;
471 
472 						}
473 
474 					}
475 
476 					if (recvBuffer[1] == MT_G_SCORE) {
477 
478 						for (count = 0; count < nPlayers; count++) {
479 
480 							if (players[count].getTeam() == recvBuffer[2])
481 								players[count].teamScore++;
482 
483 						}
484 
485 					}
486 
487 					if (recvBuffer[1] == MT_G_LTYPE) {
488 
489 						levelType = (LevelType)recvBuffer[2];
490 
491 					}
492 
493 					break;
494 
495 				case MC_LEVEL:
496 
497 					if (baseLevel) baseLevel->receive(recvBuffer);
498 
499 					break;
500 
501 				case MC_PLAYER:
502 
503 					if (recvBuffer[2] < maxPlayers)
504 						players[recvBuffer[2]].receive(recvBuffer);
505 
506 					break;
507 
508 			}
509 
510 			received = 0;
511 
512 		}
513 
514 	}
515 
516 	if (ticks >= checkTime) {
517 
518 		// Check for disconnection
519 
520 		if (!(net->isConnected(sock))) {
521 
522 			if (file) delete file;
523 			file = NULL;
524 
525 			return E_N_DISCONNECT;
526 
527 		}
528 
529 		checkTime = ticks + T_CCHECK;
530 
531 	}
532 
533 	if (localPlayer && (ticks >= sendTime)) {
534 
535 		// Update server
536 
537 		sendBuffer[0] = MTL_P_TEMP;
538 		sendBuffer[1] = MT_P_TEMP;
539 		sendBuffer[2] = 0;
540 		localPlayer->send(sendBuffer);
541 		send(sendBuffer);
542 
543 		sendTime = ticks + T_CSEND;
544 
545 	}
546 
547 	return E_NONE;
548 
549 }
550 
551 
552 /**
553  * Ask server to award team a point
554  *
555  * @param team Team to receive point
556  */
score(unsigned char team)557 void ClientGame::score (unsigned char team) {
558 
559 	unsigned char buffer[MTL_G_SCORE];
560 
561 	// Inform server
562 	buffer[0] = MTL_G_SCORE;
563 	buffer[1] = MT_G_SCORE;
564 	buffer[2] = team;
565 	send(buffer);
566 
567 	return;
568 
569 }
570 
571 
572 /**
573  * Ask server to approve new checkpoint
574  *
575  * @param gridX X-coordinate (in tiles) of the checkpoint
576  * @param gridY Y-coordinate (in tiles) of the checkpoint
577  */
setCheckpoint(int gridX,int gridY)578 void ClientGame::setCheckpoint (int gridX, int gridY) {
579 
580 	unsigned char buffer[MTL_G_CHECK];
581 
582 	buffer[0] = MTL_G_CHECK;
583 	buffer[1] = MT_G_CHECK;
584 	buffer[2] = gridX & 0xFF;
585 	buffer[3] = gridY & 0xFF;
586 	buffer[4] = (gridX >> 8) & 0xFF;
587 	buffer[5] = (gridY >> 8) & 0xFF;
588 	send(buffer);
589 
590 	return;
591 
592 }
593 
594 
595