source: network-game/client/Client/main.cpp@ 883bb5d

Last change on this file since 883bb5d was 883bb5d, checked in by Dmitry Portnoy <dmp1488@…>, 11 years ago

Extra timer events removed from queue to prevent them from piling up and reducing the frame rate

  • Property mode set to 100644
File size: 39.9 KB
Line 
1#include "../../common/Compiler.h"
2
3#if defined WINDOWS
4 #include <winsock2.h>
5 #include <ws2tcpip.h>
6#elif defined LINUX
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <netdb.h>
12 #include <cstring>
13#endif
14
15#include <cstdio>
16#include <cstdlib>
17//#include <cmath>
18#include <sys/types.h>
19#include <string>
20#include <iostream>
21#include <sstream>
22#include <fstream>
23#include <map>
24#include <vector>
25#include <stdexcept>
26
27#include <allegro5/allegro.h>
28#include <allegro5/allegro_font.h>
29#include <allegro5/allegro_ttf.h>
30#include <allegro5/allegro_primitives.h>
31
32#include "../../common/Common.h"
33#include "../../common/MessageContainer.h"
34#include "../../common/MessageProcessor.h"
35#include "../../common/WorldMap.h"
36#include "../../common/Player.h"
37#include "../../common/Projectile.h"
38#include "../../common/Game.h"
39#include "../../common/GameSummary.h"
40
41#include "Window.h"
42#include "TextLabel.h"
43#include "Button.h"
44#include "Textbox.h"
45#include "RadioButtonList.h"
46
47#include "GameRender.h"
48
49#include "chat.h"
50
51#ifdef WINDOWS
52 #pragma comment(lib, "ws2_32.lib")
53#endif
54
55using namespace std;
56
57void initWinSock();
58void shutdownWinSock();
59void createGui(ALLEGRO_FONT* font);
60
61void processMessage(NETWORK_MSG &msg, int &state, chat &chatConsole, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames, unsigned int& curPlayerId);
62void handleMsgPlayer(NETWORK_MSG &msg, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames);
63void handleMsgGameInfo(NETWORK_MSG &msg, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames);
64
65int getRefreshRate(int width, int height);
66void drawMessageStatus(ALLEGRO_FONT* font);
67
68// Callback declarations
69void goToLoginScreen();
70void goToRegisterScreen();
71void registerAccount();
72void login();
73void logout();
74void quit();
75void sendChatMessage();
76void toggleDebugging();
77void joinGame();
78void createGame();
79void leaveGame();
80void closeGameSummary();
81
82const float FPS = 60;
83const int SCREEN_W = 1024;
84const int SCREEN_H = 768;
85
86enum STATE {
87 STATE_START,
88 STATE_LOBBY,
89 STATE_GAME
90};
91
92int state;
93
94bool doexit;
95
96vector<GuiComponent*> vctComponents;
97
98Window* wndLogin;
99Window* wndRegister;
100Window* wndLobby;
101Window* wndLobbyDebug;
102Window* wndGame;
103Window* wndGameSummary;
104Window* wndCurrent;
105
106// wndLogin
107Textbox* txtUsername;
108Textbox* txtPassword;
109TextLabel* lblLoginStatus;
110
111// wndRegister
112Textbox* txtUsernameRegister;
113Textbox* txtPasswordRegister;
114RadioButtonList* rblClasses;
115TextLabel* lblRegisterStatus;
116
117// wndLobby
118Textbox* txtJoinGame;
119Textbox* txtCreateGame;
120Textbox* txtChat;
121
122int sock;
123struct sockaddr_in server, from;
124struct hostent *hp;
125NETWORK_MSG msgTo, msgFrom;
126string username;
127chat chatConsole, debugConsole;
128bool debugging;
129Game* game;
130GameSummary* gameSummary;
131
132MessageProcessor msgProcessor;
133
134int main(int argc, char **argv)
135{
136 ALLEGRO_DISPLAY *display = NULL;
137 ALLEGRO_EVENT_QUEUE *event_queue = NULL;
138 ALLEGRO_TIMER *timer = NULL;
139 map<unsigned int, Player*> mapPlayers;
140 map<string, int> mapGames;
141 unsigned int curPlayerId = -1;
142 ofstream outputLog;
143
144 doexit = false;
145 debugging = false;
146 bool redraw = true;
147 bool fullscreen = false;
148 game = NULL;
149 gameSummary = NULL;
150
151 state = STATE_START;
152
153 if(!al_init()) {
154 fprintf(stderr, "failed to initialize allegro!\n");
155 return -1;
156 }
157
158 outputLog.open("client.log", ios::app);
159 outputLog << "Started client on " << getCurrentDateTimeString() << endl;
160
161 if (al_init_primitives_addon())
162 cout << "Primitives initialized" << endl;
163 else
164 cout << "Primitives not initialized" << endl;
165
166 al_init_font_addon();
167 al_init_ttf_addon();
168
169 ALLEGRO_FONT* font;
170 #if defined WINDOWS
171 font = al_load_ttf_font("../pirulen.ttf", 12, 0);
172 #elif defined LINUX
173 font = al_load_ttf_font("pirulen.ttf", 12, 0);
174 #endif
175
176 if (!font) {
177 fprintf(stderr, "Could not load 'pirulen.ttf'.\n");
178 getchar();
179 return -1;
180 }
181
182 if(!al_install_keyboard()) {
183 fprintf(stderr, "failed to initialize the keyboard!\n");
184 return -1;
185 }
186
187 if(!al_install_mouse()) {
188 fprintf(stderr, "failed to initialize the mouse!\n");
189 return -1;
190 }
191
192 timer = al_create_timer(1.0 / FPS);
193 if(!timer) {
194 fprintf(stderr, "failed to create timer!\n");
195 return -1;
196 }
197
198 int refreshRate = getRefreshRate(SCREEN_W, SCREEN_H);
199 // if the computer doesn't support this resolution, just use windowed mode
200 if (refreshRate > 0 && fullscreen) {
201 al_set_new_display_flags(ALLEGRO_FULLSCREEN);
202 al_set_new_display_refresh_rate(refreshRate);
203 }
204 display = al_create_display(SCREEN_W, SCREEN_H);
205 if(!display) {
206 fprintf(stderr, "failed to create display!\n");
207 al_destroy_timer(timer);
208 return -1;
209 }
210
211 debugConsole.addLine("Debug console:");
212 debugConsole.addLine("");
213
214 createGui(font);
215
216 goToLoginScreen();
217
218 event_queue = al_create_event_queue();
219 if(!event_queue) {
220 fprintf(stderr, "failed to create event_queue!\n");
221 al_destroy_display(display);
222 al_destroy_timer(timer);
223 return -1;
224 }
225
226 al_set_target_bitmap(al_get_backbuffer(display));
227
228 al_register_event_source(event_queue, al_get_display_event_source(display));
229 al_register_event_source(event_queue, al_get_timer_event_source(timer));
230 al_register_event_source(event_queue, al_get_keyboard_event_source());
231 al_register_event_source(event_queue, al_get_mouse_event_source());
232
233 al_clear_to_color(al_map_rgb(0,0,0));
234
235 al_flip_display();
236
237 if (argc != 3) {
238 cout << "Usage: server port" << endl;
239 exit(1);
240 }
241
242 initWinSock();
243
244 sock = socket(AF_INET, SOCK_DGRAM, 0);
245 if (sock < 0)
246 error("socket");
247
248 set_nonblock(sock);
249
250 server.sin_family = AF_INET;
251 hp = gethostbyname(argv[1]);
252 if (hp == 0)
253 error("Unknown host");
254
255 memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
256 server.sin_port = htons(atoi(argv[2]));
257
258 msgProcessor = MessageProcessor(sock, &outputLog);
259
260 al_start_timer(timer);
261
262 while (!doexit)
263 {
264 ALLEGRO_EVENT ev;
265
266 al_wait_for_event(event_queue, &ev);
267
268 if(wndCurrent->handleEvent(ev)) {
269 // do nothing
270 }
271 else if(ev.type == ALLEGRO_EVENT_TIMER) {
272 redraw = true;
273
274 // remove any other timer events in the queue
275 while (al_peek_next_event(event_queue, &ev) && ev.type == ALLEGRO_EVENT_TIMER) {
276 al_get_next_event(event_queue, &ev);
277 }
278 }
279 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
280 doexit = true;
281 }
282 else if(ev.type == ALLEGRO_EVENT_KEY_DOWN) {
283 }
284 else if(ev.type == ALLEGRO_EVENT_KEY_UP) {
285 switch(ev.keyboard.keycode) {
286 case ALLEGRO_KEY_ESCAPE:
287 doexit = true;
288 break;
289 case ALLEGRO_KEY_S: // pickup an item next to you
290 if (state == STATE_GAME) {
291 msgTo.type = MSG_TYPE_PICKUP_FLAG;
292 memcpy(msgTo.buffer, &curPlayerId, 4);
293 msgProcessor.sendMessage(&msgTo, &server);
294 }
295 break;
296 case ALLEGRO_KEY_D: // drop the current item
297 if (state == STATE_GAME) {
298 try {
299 Player* p = mapPlayers.at(curPlayerId);
300 int flagType = OBJECT_NONE;
301
302 if (p->hasBlueFlag)
303 flagType = OBJECT_BLUE_FLAG;
304 else if (p->hasRedFlag)
305 flagType = OBJECT_RED_FLAG;
306
307 if (flagType != OBJECT_NONE) {
308 msgTo.type = MSG_TYPE_DROP_FLAG;
309 memcpy(msgTo.buffer, &curPlayerId, 4);
310 msgProcessor.sendMessage(&msgTo, &server);
311 }
312 } catch (const out_of_range& ex) {}
313 }
314 break;
315 }
316 }
317 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
318 if(wndCurrent == wndGame) {
319 if (ev.mouse.button == 1) { // left click
320 msgTo.type = MSG_TYPE_PLAYER_MOVE;
321
322 POSITION pos;
323 pos.x = ev.mouse.x;
324 pos.y = ev.mouse.y;
325 pos = screenToMap(pos);
326
327 if (pos.x != -1)
328 {
329 memcpy(msgTo.buffer, &curPlayerId, 4);
330 memcpy(msgTo.buffer+4, &pos.x, 4);
331 memcpy(msgTo.buffer+8, &pos.y, 4);
332
333 msgProcessor.sendMessage(&msgTo, &server);
334 }
335 else
336 cout << "Invalid point: User did not click on the map" << endl;
337 }else if (ev.mouse.button == 2) { // right click
338 cout << "Detected a right-click" << endl;
339 map<unsigned int, Player*>::iterator it;
340
341 Player* curPlayer = mapPlayers[curPlayerId];;
342
343 cout << "Got current player" << endl;
344 cout << "current game: " << game << endl;
345
346 map<unsigned int, Player*> playersInGame = game->getPlayers();
347 Player* target;
348
349 for(it = playersInGame.begin(); it != playersInGame.end(); it++)
350 {
351 target = it->second;
352 cout << "set target" << endl;
353 if (target->team != curPlayer->team)
354 {
355 cout << "Found valid target" << endl;
356
357 POSITION cursorPos;
358 cursorPos.x = ev.mouse.x;
359 cursorPos.y = ev.mouse.y;
360 cursorPos = screenToMap(cursorPos);
361
362 float distance =posDistance(cursorPos.toFloat(), target->pos);
363
364 if (distance < 25) {
365 unsigned int targetId = target->getId();
366
367 msgTo.type = MSG_TYPE_ATTACK;
368 memcpy(msgTo.buffer, &curPlayerId, 4);
369 memcpy(msgTo.buffer+4, &targetId, 4);
370
371 msgProcessor.sendMessage(&msgTo, &server);
372 }
373 }
374 }
375 }
376 }
377 }
378
379 if (msgProcessor.receiveMessage(&msgFrom, &from) >= 0)
380 processMessage(msgFrom, state, chatConsole, mapPlayers, mapGames, curPlayerId);
381
382 if (redraw)
383 {
384 redraw = false;
385
386 msgProcessor.resendUnackedMessages();
387
388 if (debugging && wndCurrent == wndLobby)
389 wndLobbyDebug->draw(display);
390 else
391 wndCurrent->draw(display);
392
393 if (wndCurrent == wndLobby) {
394 if (!debugging)
395 chatConsole.draw(font, al_map_rgb(255,255,255));
396
397 al_draw_text(font, al_map_rgb(0, 255, 0), SCREEN_W*1/2-100, 120, ALLEGRO_ALIGN_LEFT, "Current Games");
398
399 map<string, int>::iterator it;
400 int i=0;
401 ostringstream oss;
402 for (it = mapGames.begin(); it != mapGames.end(); it++) {
403 oss << it->first << " (" << it->second << " players)" << endl;
404 al_draw_text(font, al_map_rgb(0, 255, 0), SCREEN_W*1/2-100, 135+i*15, ALLEGRO_ALIGN_LEFT, oss.str().c_str());
405 oss.clear();
406 oss.str("");
407 i++;
408 }
409
410 al_draw_text(font, al_map_rgb(0, 255, 0), SCREEN_W*3/4-100, 120, ALLEGRO_ALIGN_LEFT, "Online Players");
411
412 map<unsigned int, Player*>::iterator itPlayers;
413 i=0;
414 for (itPlayers = mapPlayers.begin(); itPlayers != mapPlayers.end(); itPlayers++) {
415 oss << itPlayers->second->name << endl;
416 al_draw_text(font, al_map_rgb(0, 255, 0), SCREEN_W*3/4-100, 135+i*15, ALLEGRO_ALIGN_LEFT, oss.str().c_str());
417 oss.clear();
418 oss.str("");
419 i++;
420 }
421 }
422 else if (wndCurrent == wndGame)
423 {
424 al_draw_text(font, al_map_rgb(0, 255, 0), 4, 4, ALLEGRO_ALIGN_LEFT, "Players");
425
426 map<unsigned int, Player*>& gamePlayers = game->getPlayers();
427 map<unsigned int, Player*>::iterator it;
428
429 if (!debugging) {
430 int playerCount = 0;
431 for (it = gamePlayers.begin(); it != gamePlayers.end(); it++)
432 {
433 al_draw_text(font, al_map_rgb(0, 255, 0), 4, 19+(playerCount+1)*15, ALLEGRO_ALIGN_LEFT, it->second->name.c_str());
434 playerCount++;
435 }
436 }
437
438 ostringstream ossScoreBlue, ossScoreRed;
439
440 ossScoreBlue << "Blue: " << game->getBlueScore() << endl;
441 ossScoreRed << "Red: " << game->getRedScore() << endl;
442
443 al_draw_text(font, al_map_rgb(0, 255, 0), 330, 80, ALLEGRO_ALIGN_LEFT, ossScoreBlue.str().c_str());
444 al_draw_text(font, al_map_rgb(0, 255, 0), 515, 80, ALLEGRO_ALIGN_LEFT, ossScoreRed.str().c_str());
445
446 // update players
447 for (it = game->getPlayers().begin(); it != game->getPlayers().end(); it++)
448 {
449 it->second->updateTarget(game->getPlayers());
450 }
451
452 for (it = game->getPlayers().begin(); it != game->getPlayers().end(); it++)
453 {
454 it->second->move(game->getMap()); // ignore return value
455 }
456
457 // update projectile positions
458 map<unsigned int, Projectile>::iterator it2;
459 for (it2 = game->getProjectiles().begin(); it2 != game->getProjectiles().end(); it2++)
460 {
461 it2->second.move(game->getPlayers());
462 }
463
464 GameRender::drawMap(game->getMap());
465 GameRender::drawPlayers(game->getPlayers(), font, curPlayerId);
466 GameRender::drawProjectiles(game->getProjectiles(), game->getPlayers());
467 }
468 else if (wndCurrent == wndGameSummary)
469 {
470 ostringstream ossBlueScore, ossRedScore;
471
472 ossBlueScore << "Blue Score: " << gameSummary->getBlueScore();
473 ossRedScore << "Red Score: " << gameSummary->getRedScore();
474
475 string strWinner;
476
477 if (gameSummary->getWinner() == 0)
478 strWinner = "Blue Team Wins";
479 else if (gameSummary->getWinner() == 1)
480 strWinner = "Red Team Wins";
481 else
482 strWinner = "winner set to wrong value";
483
484 al_draw_text(font, al_map_rgb(0, 255, 0), 512, 40, ALLEGRO_ALIGN_CENTRE, gameSummary->getName().c_str());
485 al_draw_text(font, al_map_rgb(0, 255, 0), 330, 80, ALLEGRO_ALIGN_LEFT, ossBlueScore.str().c_str());
486 al_draw_text(font, al_map_rgb(0, 255, 0), 515, 80, ALLEGRO_ALIGN_LEFT, ossRedScore.str().c_str());
487 al_draw_text(font, al_map_rgb(0, 255, 0), 512, 120, ALLEGRO_ALIGN_CENTRE, strWinner.c_str());
488 }
489
490 if (debugging) {
491 drawMessageStatus(font);
492 }
493
494 al_flip_display();
495 }
496 }
497
498 #if defined WINDOWS
499 closesocket(sock);
500 #elif defined LINUX
501 close(sock);
502 #endif
503
504 shutdownWinSock();
505
506 // delete all components
507 for (unsigned int x=0; x<vctComponents.size(); x++)
508 delete vctComponents[x];
509
510 delete wndLogin;
511 delete wndRegister;
512 delete wndLobby;
513 delete wndLobbyDebug;
514 delete wndGame;
515 delete wndGameSummary;
516
517 // game should be deleted when the player leaves a gamw
518 if (game != NULL)
519 delete game;
520
521 if (gameSummary != NULL)
522 delete gameSummary;
523
524 map<unsigned int, Player*>::iterator it;
525
526 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++) {
527 delete it->second;
528 }
529
530 al_destroy_event_queue(event_queue);
531 al_destroy_display(display);
532 al_destroy_timer(timer);
533
534 outputLog << "Stopped client on " << getCurrentDateTimeString() << endl;
535 outputLog.close();
536
537 return 0;
538}
539
540void initWinSock()
541{
542#if defined WINDOWS
543 WORD wVersionRequested;
544 WSADATA wsaData;
545 int wsaerr;
546
547 wVersionRequested = MAKEWORD(2, 2);
548 wsaerr = WSAStartup(wVersionRequested, &wsaData);
549
550 if (wsaerr != 0) {
551 cout << "The Winsock dll not found." << endl;
552 exit(1);
553 }else
554 cout << "The Winsock dll was found." << endl;
555#endif
556}
557
558void shutdownWinSock()
559{
560#if defined WINDOWS
561 WSACleanup();
562#endif
563}
564
565void createGui(ALLEGRO_FONT* font) {
566 // wndLogin
567
568 wndLogin = new Window(0, 0, SCREEN_W, SCREEN_H);
569 vctComponents.push_back(wndLogin->addComponent(new Textbox(516, 40, 100, 20, font)));
570 vctComponents.push_back(wndLogin->addComponent(new Textbox(516, 70, 100, 20, font)));
571 vctComponents.push_back(wndLogin->addComponent(new TextLabel(410, 40, 100, 20, font, "Username:", ALLEGRO_ALIGN_RIGHT)));
572 vctComponents.push_back(wndLogin->addComponent(new TextLabel(410, 70, 100, 20, font, "Password:", ALLEGRO_ALIGN_RIGHT)));
573 vctComponents.push_back(wndLogin->addComponent(new TextLabel((SCREEN_W-600)/2, 100, 600, 20, font, "", ALLEGRO_ALIGN_CENTRE)));
574 vctComponents.push_back(wndLogin->addComponent(new Button(SCREEN_W/2-100, 130, 90, 20, font, "Register", goToRegisterScreen)));
575 vctComponents.push_back(wndLogin->addComponent(new Button(SCREEN_W/2+10, 130, 90, 20, font, "Login", login)));
576 vctComponents.push_back(wndLogin->addComponent(new Button(920, 10, 80, 20, font, "Quit", quit)));
577 vctComponents.push_back(wndLogin->addComponent(new Button(20, 10, 160, 20, font, "Toggle Debugging", toggleDebugging)));
578
579 txtUsername = (Textbox*)wndLogin->getComponent(0);
580 txtPassword = (Textbox*)wndLogin->getComponent(1);
581 lblLoginStatus = (TextLabel*)wndLogin->getComponent(4);
582
583 cout << "Created login screen" << endl;
584
585
586 // wndRegister
587
588 wndRegister = new Window(0, 0, SCREEN_W, SCREEN_H);
589 vctComponents.push_back(wndRegister->addComponent(new Textbox(516, 40, 100, 20, font)));
590 vctComponents.push_back(wndRegister->addComponent(new Textbox(516, 70, 100, 20, font)));
591 vctComponents.push_back(wndRegister->addComponent(new TextLabel(410, 40, 100, 20, font, "Username:", ALLEGRO_ALIGN_RIGHT)));
592 vctComponents.push_back(wndRegister->addComponent(new TextLabel(410, 70, 100, 20, font, "Password:", ALLEGRO_ALIGN_RIGHT)));
593 vctComponents.push_back(wndRegister->addComponent(new RadioButtonList(432, 100, "Pick a class", font)));
594 vctComponents.push_back(wndRegister->addComponent(new TextLabel((SCREEN_W-600)/2, 190, 600, 20, font, "", ALLEGRO_ALIGN_CENTRE)));
595 vctComponents.push_back(wndRegister->addComponent(new Button(SCREEN_W/2-100, 220, 90, 20, font, "Back", goToLoginScreen)));
596 vctComponents.push_back(wndRegister->addComponent(new Button(SCREEN_W/2+10, 220, 90, 20, font, "Submit", registerAccount)));
597 vctComponents.push_back(wndRegister->addComponent(new Button(920, 10, 80, 20, font, "Quit", quit)));
598 vctComponents.push_back(wndRegister->addComponent(new Button(20, 10, 160, 20, font, "Toggle Debugging", toggleDebugging)));
599
600 txtUsernameRegister = (Textbox*)wndRegister->getComponent(0);
601 txtPasswordRegister = (Textbox*)wndRegister->getComponent(1);
602
603 rblClasses = (RadioButtonList*)wndRegister->getComponent(4);
604 rblClasses->addRadioButton("Warrior");
605 rblClasses->addRadioButton("Ranger");
606
607 lblRegisterStatus = (TextLabel*)wndRegister->getComponent(5);
608
609 cout << "Created register screen" << endl;
610
611
612 // wndLobby
613
614 txtJoinGame = new Textbox(SCREEN_W*1/2+15+4, 40, 100, 20, font);
615 vctComponents.push_back(txtJoinGame);
616
617 txtCreateGame = new Textbox(SCREEN_W*3/4+4, 40, 100, 20, font);
618 vctComponents.push_back(txtCreateGame);
619
620 wndLobby = new Window(0, 0, SCREEN_W, SCREEN_H);
621 vctComponents.push_back(wndLobby->addComponent(new Button(920, 10, 80, 20, font, "Logout", logout)));
622 vctComponents.push_back(wndLobby->addComponent(new TextLabel(SCREEN_W*1/2+15-112, 40, 110, 20, font, "Game Name:", ALLEGRO_ALIGN_RIGHT)));
623 wndLobby->addComponent(txtJoinGame);
624 vctComponents.push_back(wndLobby->addComponent(new Button(SCREEN_W*1/2+15-100, 80, 200, 20, font, "Join Existing Game", joinGame)));
625 vctComponents.push_back(wndLobby->addComponent(new TextLabel(SCREEN_W*3/4-112, 40, 110, 20, font, "Game Name:", ALLEGRO_ALIGN_RIGHT)));
626 wndLobby->addComponent(txtCreateGame);
627 vctComponents.push_back(wndLobby->addComponent(new Button(SCREEN_W*3/4-100, 80, 200, 20, font, "Create New Game", createGame)));
628 vctComponents.push_back(wndLobby->addComponent(new Textbox(95, 40, 300, 20, font)));
629 vctComponents.push_back(wndLobby->addComponent(new Button(95, 70, 60, 20, font, "Send", sendChatMessage)));
630 vctComponents.push_back(wndLobby->addComponent(new Button(20, 10, 160, 20, font, "Toggle Debugging", toggleDebugging)));
631
632 txtChat = (Textbox*)wndLobby->getComponent(7);
633
634 cout << "Created lobby screen" << endl;
635
636
637 // wndLobbyDebug
638
639 wndLobbyDebug = new Window(0, 0, SCREEN_W, SCREEN_H);
640 vctComponents.push_back(wndLobbyDebug->addComponent(new Button(920, 10, 80, 20, font, "Logout", logout)));
641 vctComponents.push_back(wndLobbyDebug->addComponent(new TextLabel(SCREEN_W*1/2+15-112, 40, 110, 20, font, "Game Name:", ALLEGRO_ALIGN_RIGHT)));
642 wndLobbyDebug->addComponent(txtJoinGame);
643 vctComponents.push_back(wndLobbyDebug->addComponent(new Button(SCREEN_W*1/2+15-100, 80, 200, 20, font, "Join Existing Game", joinGame)));
644 vctComponents.push_back(wndLobbyDebug->addComponent(new TextLabel(SCREEN_W*3/4-112, 40, 110, 20, font, "Game Name:", ALLEGRO_ALIGN_RIGHT)));
645 wndLobbyDebug->addComponent(txtCreateGame);
646 vctComponents.push_back(wndLobbyDebug->addComponent(new Button(SCREEN_W*3/4-100, 80, 200, 20, font, "Create New Game", createGame)));
647 vctComponents.push_back(wndLobbyDebug->addComponent(new Button(20, 10, 160, 20, font, "Toggle Debugging", toggleDebugging)));
648
649 cout << "Created debug lobby screen" << endl;
650
651
652 // wndGame
653
654 wndGame = new Window(0, 0, SCREEN_W, SCREEN_H);
655 vctComponents.push_back(wndGame->addComponent(new Button(880, 10, 120, 20, font, "Leave Game", leaveGame)));
656
657 cout << "Created new game screen" << endl;
658
659 wndGameSummary = new Window(0, 0, SCREEN_W, SCREEN_H);
660 vctComponents.push_back(wndGameSummary->addComponent(new Button(840, 730, 160, 20, font, "Back to Lobby", closeGameSummary)));
661
662 cout << "Created game summary screen" << endl;
663}
664
665void processMessage(NETWORK_MSG &msg, int &state, chat &chatConsole, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames, unsigned int& curPlayerId)
666{
667 cout << "Total players in map: " << mapPlayers.size() << endl;
668
669 // this is outdated since most messages now don't contain just a text string
670 string response = string(msg.buffer);
671
672 switch(state)
673 {
674 case STATE_START:
675 {
676 cout << "In STATE_START" << endl;
677
678 switch(msg.type)
679 {
680 case MSG_TYPE_REGISTER:
681 {
682 lblRegisterStatus->setText(response);
683 break;
684 }
685 default:
686 {
687 cout << "(STATE_REGISTER) Received invalid message of type " << msg.type << endl;
688 break;
689 }
690 }
691
692 break;
693 }
694 case STATE_LOBBY:
695 {
696 cout << "In STATE_LOBBY" << endl;
697 switch(msg.type)
698 {
699 case MSG_TYPE_LOGIN:
700 {
701 if (response.compare("Player has already logged in.") == 0)
702 {
703 goToLoginScreen();
704 state = STATE_START;
705
706 lblLoginStatus->setText(response);
707 }
708 else if (response.compare("Incorrect username or password") == 0)
709 {
710 goToLoginScreen();
711 state = STATE_START;
712
713 lblLoginStatus->setText(response);
714 }
715 else
716 {
717 wndCurrent = wndLobby;
718
719 // this message should only be sent when a player first logs in so they know their id
720
721 Player* p = new Player("", "");
722 p->deserialize(msg.buffer);
723
724 if (mapPlayers.find(p->getId()) != mapPlayers.end())
725 delete mapPlayers[p->getId()];
726 mapPlayers[p->getId()] = p;
727 curPlayerId = p->getId();
728
729 cout << "Got a valid login response with the player" << endl;
730 cout << "Player id: " << curPlayerId << endl;
731 cout << "Player health: " << p->health << endl;
732 cout << "player map size: " << mapPlayers.size() << endl;
733 }
734
735 break;
736 }
737 case MSG_TYPE_LOGOUT:
738 {
739 cout << "Got a logout message" << endl;
740
741 unsigned int playerId;
742
743 // Check if it's about you or another player
744 memcpy(&playerId, msg.buffer, 4);
745 response = string(msg.buffer+4);
746
747 if (playerId == curPlayerId)
748 {
749 cout << "Got logout message for self" << endl;
750
751 if (response.compare("You have successfully logged out.") == 0)
752 {
753 cout << "Logged out" << endl;
754 state = STATE_START;
755 goToLoginScreen();
756 }
757
758 // if there was an error logging out, nothing happens
759 }
760 else
761 {
762 delete mapPlayers[playerId];
763 mapPlayers.erase(playerId);
764 }
765
766 break;
767 }
768 case MSG_TYPE_CHAT:
769 {
770 chatConsole.addLine(response);
771
772 break;
773 }
774 case MSG_TYPE_JOIN_GAME_SUCCESS:
775 {
776 cout << "Received a JOIN_GAME_SUCCESS message" << endl;
777
778 string gameName(msg.buffer);
779
780 #if defined WINDOWS
781 game = new Game(gameName, "../../data/map.txt", &msgProcessor);
782 #elif defined LINUX
783 game = new Game(gameName, "../data/map.txt", &msgProcessor);
784 #endif
785
786 cout << "Game name: " << gameName << endl;
787
788 state = STATE_GAME;
789 wndCurrent = wndGame;
790
791 msgTo.type = MSG_TYPE_JOIN_GAME_ACK;
792 strcpy(msgTo.buffer, gameName.c_str());
793
794 msgProcessor.sendMessage(&msgTo, &server);
795
796 break;
797 }
798 case MSG_TYPE_JOIN_GAME_FAILURE:
799 {
800 cout << "Received a JOIN_GAME_FAILURE message" << endl;
801
802 break;
803 }
804 case MSG_TYPE_PLAYER:
805 {
806 handleMsgPlayer(msg, mapPlayers, mapGames);
807
808 break;
809 }
810 case MSG_TYPE_GAME_INFO:
811 {
812 handleMsgGameInfo(msg, mapPlayers, mapGames);
813
814 break;
815 }
816 default:
817 {
818 cout << "(STATE_LOBBY) Received invlaid message of type " << msg.type << endl;
819
820 break;
821 }
822 }
823
824 break;
825 }
826 case STATE_GAME:
827 {
828 cout << "(STATE_GAME) ";
829 switch(msg.type)
830 {
831 case MSG_TYPE_SCORE:
832 {
833 cout << "Received SCORE message!" << endl;
834
835 int blueScore;
836 memcpy(&blueScore, msg.buffer, 4);
837 cout << "blue score: " << blueScore << endl;
838 game->setBlueScore(blueScore);
839
840 int redScore;
841 memcpy(&redScore, msg.buffer+4, 4);
842 cout << "red score: " << redScore << endl;
843 game->setRedScore(redScore);
844
845 cout << "Processed SCORE message!" << endl;
846
847 break;
848 }
849 case MSG_TYPE_FINISH_GAME:
850 {
851 cout << "Got a finish game message" << endl;
852 cout << "Should switch to STATE_LOBBY and show the final score" << endl;
853
854 unsigned int winner, blueScore, redScore;
855 memcpy(&winner, msg.buffer, 4);
856 memcpy(&blueScore, msg.buffer+4, 4);
857 memcpy(&redScore, msg.buffer+8, 4);
858
859 string gameName(msg.buffer+12);
860
861 gameSummary = new GameSummary(gameName, winner, blueScore, redScore);
862
863 delete game;
864 game = NULL;
865 state = STATE_LOBBY;
866 wndCurrent = wndGameSummary;
867
868 break;
869 }
870 case MSG_TYPE_LOGOUT:
871 {
872 cout << "Got a logout message" << endl;
873
874 unsigned int playerId;
875
876 // Check if it's about you or another player
877 memcpy(&playerId, msg.buffer, 4);
878 response = string(msg.buffer+4);
879
880 if (playerId == curPlayerId)
881 cout << "Received MSG_TYPE_LOGOUT for self in STATE_GAME. This shouldn't happen." << endl;
882 else {
883 delete mapPlayers[playerId];
884 mapPlayers.erase(playerId);
885 }
886
887 break;
888 }
889 case MSG_TYPE_PLAYER_JOIN_GAME:
890 {
891 cout << "Received MSG_TYPE_PLAYER_JOIN_GAME" << endl;
892
893 Player p("", "");
894 p.deserialize(msg.buffer);
895 cout << "Deserialized player" << endl;
896 p.timeLastUpdated = getCurrentMillis();
897 p.isChasing = false;
898 if (p.health <= 0)
899 p.isDead = true;
900 else
901 p.isDead = false;
902
903 if (mapPlayers.find(p.getId()) != mapPlayers.end())
904 *(mapPlayers[p.getId()]) = p;
905 else
906 mapPlayers[p.getId()] = new Player(p);
907
908 game->addPlayer(mapPlayers[p.getId()]);
909
910 break;
911 }
912 case MSG_TYPE_LEAVE_GAME:
913 {
914 cout << "Received a LEAVE_GAME message" << endl;
915
916 string gameName(msg.buffer+4);
917 unsigned int playerId;
918
919 memcpy(&playerId, msg.buffer, 4);
920
921 game->removePlayer(playerId);
922
923 break;
924 }
925 case MSG_TYPE_PLAYER_MOVE:
926 {
927 cout << "Received PLAYER_MOVE message" << endl;
928
929 unsigned int id;
930 int x, y;
931
932 memcpy(&id, msg.buffer, 4);
933 memcpy(&x, msg.buffer+4, 4);
934 memcpy(&y, msg.buffer+8, 4);
935
936 cout << "id: " << id << endl;
937
938 mapPlayers[id]->target.x = x;
939 mapPlayers[id]->target.y = y;
940
941 mapPlayers[id]->isChasing = false;
942 mapPlayers[id]->setTargetPlayer(0);
943
944 break;
945 }
946 case MSG_TYPE_OBJECT:
947 {
948 cout << "Received object message in STATE_GAME" << endl;
949
950 WorldMap::Object o(0, OBJECT_NONE, 0, 0);
951 o.deserialize(msg.buffer);
952 cout << "object id: " << o.id << endl;
953 game->getMap()->updateObject(o.id, o.type, o.pos.x, o.pos.y);
954
955 break;
956 }
957 case MSG_TYPE_REMOVE_OBJECT:
958 {
959 cout << "Received REMOVE_OBJECT message!" << endl;
960
961 int id;
962 memcpy(&id, msg.buffer, 4);
963
964 cout << "Removing object with id " << id << endl;
965
966 if (!game->getMap()->removeObject(id))
967 cout << "Did not remove the object" << endl;
968
969 break;
970 }
971 case MSG_TYPE_ATTACK:
972 {
973 cout << "Received START_ATTACK message" << endl;
974
975 unsigned int id, targetId;
976 memcpy(&id, msg.buffer, 4);
977 memcpy(&targetId, msg.buffer+4, 4);
978
979 cout << "source id: " << id << endl;
980 cout << "target id: " << targetId << endl;
981
982 // need to check the target exists in the current game
983 Player* source = game->getPlayers()[id];
984 source->setTargetPlayer(targetId);
985 source->isChasing = true;
986
987 break;
988 }
989 case MSG_TYPE_PROJECTILE:
990 {
991 cout << "Received a PROJECTILE message" << endl;
992
993 unsigned int projId, x, y, targetId;
994
995 memcpy(&projId, msg.buffer, 4);
996 memcpy(&x, msg.buffer+4, 4);
997 memcpy(&y, msg.buffer+8, 4);
998 memcpy(&targetId, msg.buffer+12, 4);
999
1000 cout << "projId: " << projId << endl;
1001 cout << "x: " << x << endl;
1002 cout << "y: " << y << endl;
1003 cout << "Target: " << targetId << endl;
1004
1005 Projectile proj(x, y, targetId, 0);
1006 proj.setId(projId);
1007
1008 game->addProjectile(proj);
1009
1010 break;
1011 }
1012 case MSG_TYPE_REMOVE_PROJECTILE:
1013 {
1014 cout << "Received a REMOVE_PROJECTILE message" << endl;
1015
1016 unsigned int id;
1017 memcpy(&id, msg.buffer, 4);
1018
1019 game->removeProjectile(id);
1020
1021 break;
1022 }
1023 case MSG_TYPE_PLAYER:
1024 {
1025 handleMsgPlayer(msg, mapPlayers, mapGames);
1026
1027 break;
1028 }
1029 case MSG_TYPE_GAME_INFO:
1030 {
1031 handleMsgGameInfo(msg, mapPlayers, mapGames);
1032
1033 break;
1034 }
1035 default:
1036 {
1037 cout << "Received invalid message of type " << msg.type << endl;
1038
1039 break;
1040 }
1041 }
1042
1043 break;
1044 }
1045 default:
1046 {
1047 cout << "The state has an invalid value: " << state << endl;
1048
1049 break;
1050 }
1051 }
1052}
1053
1054int getRefreshRate(int width, int height)
1055{
1056 int numRefreshRates = al_get_num_display_modes();
1057 ALLEGRO_DISPLAY_MODE displayMode;
1058
1059 for(int i=0; i<numRefreshRates; i++) {
1060 al_get_display_mode(i, &displayMode);
1061
1062 if (displayMode.width == width && displayMode.height == height)
1063 return displayMode.refresh_rate;
1064 }
1065
1066 return 0;
1067}
1068
1069void drawMessageStatus(ALLEGRO_FONT* font)
1070{
1071 int clientMsgOffset = 5;
1072 int serverMsgOffset = 950;
1073
1074 al_draw_text(font, al_map_rgb(0, 255, 255), 0+clientMsgOffset, 43, ALLEGRO_ALIGN_LEFT, "ID");
1075 al_draw_text(font, al_map_rgb(0, 255, 255), 20+clientMsgOffset, 43, ALLEGRO_ALIGN_LEFT, "Type");
1076 al_draw_text(font, al_map_rgb(0, 255, 255), 240+clientMsgOffset, 43, ALLEGRO_ALIGN_LEFT, "Acked?");
1077
1078 //al_draw_text(font, al_map_rgb(0, 255, 255), serverMsgOffset, 43, ALLEGRO_ALIGN_LEFT, "ID");
1079
1080 map<unsigned int, map<unsigned long, MessageContainer> >& sentMessages = msgProcessor.getSentMessages();
1081 int id, type;
1082 bool acked;
1083 ostringstream ossId, ossAcked;
1084
1085 map<unsigned int, map<unsigned long, MessageContainer> >::iterator it;
1086
1087 int msgCount = 0;
1088 for (it = sentMessages.begin(); it != sentMessages.end(); it++) {
1089 map<unsigned long, MessageContainer> playerMessage = it->second;
1090 map<unsigned long, MessageContainer>::iterator it2;
1091 for (it2 = playerMessage.begin(); it2 != playerMessage.end(); it2++) {
1092
1093 id = it->first;
1094 ossId.str("");;
1095 ossId << id;
1096
1097 type = it2->second.getMessage()->type;
1098 string typeStr = MessageContainer::getMsgTypeString(type);
1099
1100 acked = it2->second.getAcked();
1101 ossAcked.str("");;
1102 ossAcked << boolalpha << acked;
1103
1104 al_draw_text(font, al_map_rgb(0, 255, 0), clientMsgOffset, 60+15*msgCount, ALLEGRO_ALIGN_LEFT, ossId.str().c_str());
1105 al_draw_text(font, al_map_rgb(0, 255, 0), 20+clientMsgOffset, 60+15*msgCount, ALLEGRO_ALIGN_LEFT, typeStr.c_str());
1106 al_draw_text(font, al_map_rgb(0, 255, 0), 240+clientMsgOffset, 60+15*msgCount, ALLEGRO_ALIGN_LEFT, ossAcked.str().c_str());
1107
1108 msgCount++;
1109 }
1110 }
1111
1112 if (msgProcessor.getAckedMessages().size() > 0) {
1113 map<unsigned int, unsigned long long> ackedMessages = msgProcessor.getAckedMessages()[0];
1114 map<unsigned int, unsigned long long>::iterator it3;
1115
1116 msgCount = 0;
1117 for (it3 = ackedMessages.begin(); it3 != ackedMessages.end(); it3++) {
1118 ossId.str("");;
1119 ossId << it3->first;
1120
1121 al_draw_text(font, al_map_rgb(255, 0, 0), 25+serverMsgOffset, 60+15*msgCount, ALLEGRO_ALIGN_LEFT, ossId.str().c_str());
1122
1123 msgCount++;
1124 }
1125 }
1126}
1127
1128// message handling functions
1129
1130void handleMsgPlayer(NETWORK_MSG &msg, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames) {
1131 cout << "Received MSG_TYPE_PLAYER" << endl;
1132
1133 Player p("", "");
1134 p.deserialize(msg.buffer);
1135 p.timeLastUpdated = getCurrentMillis();
1136 p.isChasing = false;
1137 if (p.health <= 0)
1138 p.isDead = true;
1139 else
1140 p.isDead = false;
1141
1142 if (mapPlayers.find(p.getId()) != mapPlayers.end())
1143 *(mapPlayers[p.getId()]) = p;
1144 else
1145 mapPlayers[p.getId()] = new Player(p);
1146}
1147
1148void handleMsgGameInfo(NETWORK_MSG &msg, map<unsigned int, Player*>& mapPlayers, map<string, int>& mapGames) {
1149 cout << "Received a GAME_INFO message" << endl;
1150
1151 string gameName(msg.buffer+4);
1152 int numPlayers;
1153
1154 memcpy(&numPlayers, msg.buffer, 4);
1155
1156 cout << "Received game info for " << gameName << " (num players: " << numPlayers << ")" << endl;
1157
1158 if (numPlayers > 0)
1159 mapGames[gameName] = numPlayers;
1160 else
1161 mapGames.erase(gameName);
1162}
1163
1164// Callback definitions
1165
1166void goToRegisterScreen()
1167{
1168 txtUsernameRegister->clear();
1169 txtPasswordRegister->clear();
1170 lblRegisterStatus->setText("");
1171 rblClasses->setSelectedButton(-1);
1172
1173 wndCurrent = wndRegister;
1174}
1175
1176void goToLoginScreen()
1177{
1178 txtUsername->clear();
1179 txtPassword->clear();
1180 lblLoginStatus->setText("");
1181
1182 wndCurrent = wndLogin;
1183}
1184
1185// maybe need a goToGameScreen function as well and add state changes to these functions as well
1186
1187void registerAccount()
1188{
1189 string username = txtUsernameRegister->getStr();
1190 string password = txtPasswordRegister->getStr();
1191
1192 txtUsernameRegister->clear();
1193 txtPasswordRegister->clear();
1194 // maybe clear rblClasses as well (add a method to RadioButtonList to enable this)
1195
1196 Player::PlayerClass playerClass;
1197
1198 switch (rblClasses->getSelectedButton()) {
1199 case 0:
1200 playerClass = Player::CLASS_WARRIOR;
1201 break;
1202 case 1:
1203 playerClass = Player::CLASS_RANGER;
1204 break;
1205 default:
1206 cout << "Invalid class selection" << endl;
1207 playerClass = Player::CLASS_NONE;
1208 break;
1209 }
1210
1211 msgTo.type = MSG_TYPE_REGISTER;
1212
1213 strcpy(msgTo.buffer, username.c_str());
1214 strcpy(msgTo.buffer+username.size()+1, password.c_str());
1215 memcpy(msgTo.buffer+username.size()+password.size()+2, &playerClass, 4);
1216
1217 msgProcessor.sendMessage(&msgTo, &server);
1218}
1219
1220void login()
1221{
1222 string strUsername = txtUsername->getStr();
1223 string strPassword = txtPassword->getStr();
1224 username = strUsername;
1225
1226 txtUsername->clear();
1227 txtPassword->clear();
1228
1229 msgTo.type = MSG_TYPE_LOGIN;
1230
1231 strcpy(msgTo.buffer, strUsername.c_str());
1232 strcpy(msgTo.buffer+username.size()+1, strPassword.c_str());
1233
1234 msgProcessor.sendMessage(&msgTo, &server);
1235
1236 state = STATE_LOBBY;
1237}
1238
1239void logout()
1240{
1241 switch(state) {
1242 case STATE_LOBBY:
1243 txtJoinGame->clear();
1244 txtCreateGame->clear();
1245 break;
1246 default:
1247 cout << "Logout called from invalid state: " << state << endl;
1248 break;
1249 }
1250
1251 msgTo.type = MSG_TYPE_LOGOUT;
1252
1253 strcpy(msgTo.buffer, username.c_str());
1254
1255 msgProcessor.sendMessage(&msgTo, &server);
1256}
1257
1258void quit()
1259{
1260 doexit = true;
1261}
1262
1263void sendChatMessage()
1264{
1265 string msg = txtChat->getStr();
1266 txtChat->clear();
1267
1268 msgTo.type = MSG_TYPE_CHAT;
1269 strcpy(msgTo.buffer, msg.c_str());
1270
1271 msgProcessor.sendMessage(&msgTo, &server);
1272}
1273
1274void toggleDebugging()
1275{
1276 debugging = !debugging;
1277}
1278
1279void joinGame()
1280{
1281 cout << "Joining game" << endl;
1282
1283 string msg = txtJoinGame->getStr();
1284 txtJoinGame->clear();
1285
1286 msgTo.type = MSG_TYPE_JOIN_GAME;
1287 strcpy(msgTo.buffer, msg.c_str());
1288
1289 msgProcessor.sendMessage(&msgTo, &server);
1290}
1291
1292void createGame()
1293{
1294 cout << "Creating game" << endl;
1295
1296 string msg = txtCreateGame->getStr();
1297 txtCreateGame->clear();
1298
1299 cout << "Sending message: " << msg.c_str() << endl;
1300
1301 msgTo.type = MSG_TYPE_CREATE_GAME;
1302 strcpy(msgTo.buffer, msg.c_str());
1303
1304 msgProcessor.sendMessage(&msgTo, &server);
1305}
1306
1307void leaveGame()
1308{
1309 cout << "Leaving game" << endl;
1310
1311 delete game;
1312 game = NULL;
1313
1314 state = STATE_LOBBY;
1315 wndCurrent = wndLobby;
1316
1317 msgTo.type = MSG_TYPE_LEAVE_GAME;
1318
1319 msgProcessor.sendMessage(&msgTo, &server);
1320}
1321
1322void closeGameSummary()
1323{
1324 delete gameSummary;
1325 gameSummary = NULL;
1326 wndCurrent = wndLobby;
1327 cout << "Processed button actions" << endl;
1328}
Note: See TracBrowser for help on using the repository browser.