source: network-game/client/Client/main.cpp@ 5c84d54

Last change on this file since 5c84d54 was 4926168, checked in by dportnoy <dmp1488@…>, 12 years ago

The client sends a PICKUP_FLAG message when S is pressed

  • Property mode set to 100644
File size: 19.4 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 <sys/types.h>
16#include <cstdio>
17#include <cstdlib>
18#include <string>
19#include <iostream>
20#include <sstream>
21
22#include <map>
23
24#include <map>
25
26#include <allegro5/allegro.h>
27#include <allegro5/allegro_font.h>
28#include <allegro5/allegro_ttf.h>
29#include <allegro5/allegro_primitives.h>
30
31#include "../../common/Message.h"
32#include "../../common/Common.h"
33#include "../../common/WorldMap.h"
34#include "../../common/Player.h"
35
36#include "Window.h"
37#include "Textbox.h"
38#include "Button.h"
39#include "chat.h"
40
41#ifdef WINDOWS
42 #pragma comment(lib, "ws2_32.lib")
43#endif
44
45using namespace std;
46
47void initWinSock();
48void shutdownWinSock();
49void processMessage(NETWORK_MSG &msg, int &state, chat &chatConsole, WorldMap *gameMap, map<unsigned int, Player>& mapPlayers, unsigned int& curPlayerId);
50void drawMap(WorldMap* gameMap);
51void drawPlayers(map<unsigned int, Player>& mapPlayers, unsigned int curPlayerId);
52POSITION screenToMap(POSITION pos);
53POSITION mapToScreen(POSITION pos);
54
55// callbacks
56void registerAccount();
57void login();
58void logout();
59void quit();
60void sendChatMessage();
61
62void error(const char *);
63
64const float FPS = 60;
65const int SCREEN_W = 640;
66const int SCREEN_H = 480;
67
68enum STATE {
69 STATE_START,
70 STATE_LOGIN // this means you're already logged in
71};
72
73int state;
74
75bool doexit;
76
77Window* wndLogin;
78Window* wndMain;
79Window* wndCurrent;
80
81Textbox* txtUsername;
82Textbox* txtPassword;
83Textbox* txtChat;
84
85int sock;
86struct sockaddr_in server, from;
87struct hostent *hp;
88NETWORK_MSG msgTo, msgFrom;
89string username;
90chat chatConsole;
91
92int main(int argc, char **argv)
93{
94 ALLEGRO_DISPLAY *display = NULL;
95 ALLEGRO_EVENT_QUEUE *event_queue = NULL;
96 ALLEGRO_TIMER *timer = NULL;
97 bool key[4] = { false, false, false, false };
98 bool redraw = true;
99 doexit = false;
100 map<unsigned int, Player> mapPlayers;
101 unsigned int curPlayerId = -1;
102
103 state = STATE_START;
104
105 if(!al_init()) {
106 fprintf(stderr, "failed to initialize allegro!\n");
107 return -1;
108 }
109
110 if (al_init_primitives_addon())
111 cout << "Primitives initialized" << endl;
112 else
113 cout << "Primitives not initialized" << endl;
114
115 al_init_font_addon();
116 al_init_ttf_addon();
117
118 #if defined WINDOWS
119 ALLEGRO_FONT *font = al_load_ttf_font("../pirulen.ttf", 12, 0);
120 #elif defined LINUX
121 ALLEGRO_FONT *font = al_load_ttf_font("pirulen.ttf", 12, 0);
122 #endif
123
124 if (!font) {
125 fprintf(stderr, "Could not load 'pirulen.ttf'.\n");
126 getchar();
127 return -1;
128 }
129
130 if(!al_install_keyboard()) {
131 fprintf(stderr, "failed to initialize the keyboard!\n");
132 return -1;
133 }
134
135 if(!al_install_mouse()) {
136 fprintf(stderr, "failed to initialize the mouse!\n");
137 return -1;
138 }
139
140 timer = al_create_timer(1.0 / FPS);
141 if(!timer) {
142 fprintf(stderr, "failed to create timer!\n");
143 return -1;
144 }
145
146 display = al_create_display(SCREEN_W, SCREEN_H);
147 if(!display) {
148 fprintf(stderr, "failed to create display!\n");
149 al_destroy_timer(timer);
150 return -1;
151 }
152
153 WorldMap* gameMap = WorldMap::loadMapFromFile("../../data/map.txt");
154
155 wndLogin = new Window(0, 0, SCREEN_W, SCREEN_H);
156 wndLogin->addComponent(new Textbox(104, 40, 100, 20, font));
157 wndLogin->addComponent(new Textbox(104, 70, 100, 20, font));
158 wndLogin->addComponent(new Button(22, 100, 90, 20, font, "Register", registerAccount));
159 wndLogin->addComponent(new Button(122, 100, 60, 20, font, "Login", login));
160 wndLogin->addComponent(new Button(540, 10, 80, 20, font, "Quit", quit));
161
162 txtUsername = (Textbox*)wndLogin->getComponent(0);
163 txtPassword = (Textbox*)wndLogin->getComponent(1);
164
165 wndMain = new Window(0, 0, SCREEN_W, SCREEN_H);
166 wndMain->addComponent(new Textbox(95, 40, 525, 20, font));
167 wndMain->addComponent(new Button(95, 70, 160, 20, font, "Send Message", sendChatMessage));
168 wndMain->addComponent(new Button(540, 10, 80, 20, font, "Logout", logout));
169
170 txtChat = (Textbox*)wndMain->getComponent(0);
171
172 wndCurrent = wndLogin;
173
174 event_queue = al_create_event_queue();
175 if(!event_queue) {
176 fprintf(stderr, "failed to create event_queue!\n");
177 al_destroy_display(display);
178 al_destroy_timer(timer);
179 return -1;
180 }
181
182 al_set_target_bitmap(al_get_backbuffer(display));
183
184 al_register_event_source(event_queue, al_get_display_event_source(display));
185 al_register_event_source(event_queue, al_get_timer_event_source(timer));
186 al_register_event_source(event_queue, al_get_keyboard_event_source());
187 al_register_event_source(event_queue, al_get_mouse_event_source());
188
189 al_clear_to_color(al_map_rgb(0,0,0));
190
191 al_flip_display();
192
193 if (argc != 3) {
194 cout << "Usage: server port" << endl;
195 exit(1);
196 }
197
198 initWinSock();
199
200 sock = socket(AF_INET, SOCK_DGRAM, 0);
201 if (sock < 0)
202 error("socket");
203
204 set_nonblock(sock);
205
206 server.sin_family = AF_INET;
207 hp = gethostbyname(argv[1]);
208 if (hp==0)
209 error("Unknown host");
210
211 memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
212 server.sin_port = htons(atoi(argv[2]));
213
214 al_start_timer(timer);
215
216 while(!doexit)
217 {
218 ALLEGRO_EVENT ev;
219
220 al_wait_for_event(event_queue, &ev);
221
222 if(wndCurrent->handleEvent(ev)) {
223 // do nothing
224 }
225 else if(ev.type == ALLEGRO_EVENT_TIMER) {
226 redraw = true; // seems like we should just call a draw function here instead
227 }
228 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
229 doexit = true;
230 }
231 else if(ev.type == ALLEGRO_EVENT_KEY_DOWN) {
232 }
233 else if(ev.type == ALLEGRO_EVENT_KEY_UP) {
234 switch(ev.keyboard.keycode) {
235 case ALLEGRO_KEY_ESCAPE:
236 doexit = true;
237 break;
238 case ALLEGRO_KEY_S: // pickup an item next to you
239 if (state == STATE_LOGIN) {
240 msgTo.type = MSG_TYPE_PICKUP_FLAG;
241 memcpy(msgTo.buffer, &curPlayerId, 4);
242 sendMessage(&msgTo, sock, &server);
243 }
244 break;
245 case ALLEGRO_KEY_D: // drop the current item
246 if (state == STATE_LOGIN) {
247 // find the current player in the player list
248 map<unsigned int, Player>::iterator it;
249 Player* p = NULL;
250 for(it = mapPlayers.begin(); it != mapPlayers.end(); it++)
251 {
252 &it->second;
253 if (it->second.id == curPlayerId)
254 p = &it->second;
255 }
256
257 if (p != NULL) {
258 int flagType = WorldMap::OBJECT_NONE;
259
260 if (p->hasBlueFlag)
261 flagType = WorldMap::OBJECT_BLUE_FLAG;
262 else if (p->hasRedFlag)
263 flagType = WorldMap::OBJECT_RED_FLAG;
264
265 if (flagType != WorldMap::OBJECT_NONE) {
266 msgTo.type = MSG_TYPE_DROP_FLAG;
267 memcpy(msgTo.buffer, &curPlayerId, 4);
268 sendMessage(&msgTo, sock, &server);
269 }
270 }
271 }
272 break;
273 }
274 }
275 else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
276 if(wndCurrent == wndMain) {
277 msgTo.type = MSG_TYPE_PLAYER_MOVE;
278
279 POSITION pos;
280 pos.x = ev.mouse.x;
281 pos.y = ev.mouse.y;
282 pos = screenToMap(pos);
283
284 if (pos.x != -1)
285 {
286 memcpy(msgTo.buffer, &curPlayerId, 4);
287 memcpy(msgTo.buffer+4, &pos.x, 4);
288 memcpy(msgTo.buffer+8, &pos.y, 4);
289
290 sendMessage(&msgTo, sock, &server);
291 }
292 else
293 cout << "Invalid point: User did not click on the map" << endl;
294 }
295 }
296
297 if (receiveMessage(&msgFrom, sock, &from) >= 0)
298 processMessage(msgFrom, state, chatConsole, gameMap, mapPlayers, curPlayerId);
299
300 if (redraw)
301 {
302 redraw = false;
303
304 wndCurrent->draw(display);
305
306 chatConsole.draw(font, al_map_rgb(255,255,255));
307
308 if(wndCurrent == wndLogin) {
309 al_draw_text(font, al_map_rgb(0, 255, 0), 4, 43, ALLEGRO_ALIGN_LEFT, "Username:");
310 al_draw_text(font, al_map_rgb(0, 255, 0), 1, 73, ALLEGRO_ALIGN_LEFT, "Password:");
311 }
312 else if(wndCurrent == wndMain) {
313 al_draw_text(font, al_map_rgb(0, 255, 0), 4, 43, ALLEGRO_ALIGN_LEFT, "Message:");
314
315 // update player positions
316 map<unsigned int, Player>::iterator it;
317 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
318 {
319 it->second.move(gameMap); // ignore return value
320 }
321
322 drawMap(gameMap);
323 drawPlayers(mapPlayers, curPlayerId);
324 }
325
326 al_flip_display();
327 }
328 }
329
330 #if defined WINDOWS
331 closesocket(sock);
332 #elif defined LINUX
333 close(sock);
334 #endif
335
336 shutdownWinSock();
337
338 delete wndLogin;
339 delete wndMain;
340
341 delete gameMap;
342
343 al_destroy_event_queue(event_queue);
344 al_destroy_display(display);
345 al_destroy_timer(timer);
346
347 return 0;
348}
349
350// need to make a function like this that works on windows
351void error(const char *msg)
352{
353 perror(msg);
354 shutdownWinSock();
355 exit(1);
356}
357
358void initWinSock()
359{
360#if defined WINDOWS
361 WORD wVersionRequested;
362 WSADATA wsaData;
363 int wsaerr;
364
365 wVersionRequested = MAKEWORD(2, 2);
366 wsaerr = WSAStartup(wVersionRequested, &wsaData);
367
368 if (wsaerr != 0) {
369 cout << "The Winsock dll not found." << endl;
370 exit(1);
371 }else
372 cout << "The Winsock dll was found." << endl;
373#endif
374}
375
376void shutdownWinSock()
377{
378#if defined WINDOWS
379 WSACleanup();
380#endif
381}
382
383POSITION screenToMap(POSITION pos)
384{
385 pos.x = pos.x-300;
386 pos.y = pos.y-100;
387
388 if (pos.x < 0 || pos.y < 0)
389 {
390 pos.x = -1;
391 pos.y = -1;
392 }
393
394 return pos;
395}
396
397POSITION mapToScreen(POSITION pos)
398{
399 pos.x = pos.x+300;
400 pos.y = pos.y+100;
401
402 return pos;
403}
404
405POSITION mapToScreen(FLOAT_POSITION pos)
406{
407 POSITION p;
408 p.x = pos.x+300;
409 p.y = pos.y+100;
410
411 return p;
412}
413
414void processMessage(NETWORK_MSG &msg, int &state, chat &chatConsole, WorldMap *gameMap, map<unsigned int, Player>& mapPlayers, unsigned int& curPlayerId)
415{
416 string response = string(msg.buffer);
417
418 cout << "Processing message" << endl;
419
420 switch(state)
421 {
422 case STATE_START:
423 {
424 cout << "In STATE_START" << endl;
425
426 switch(msg.type)
427 {
428 case MSG_TYPE_REGISTER:
429 {
430 break;
431 }
432 case MSG_TYPE_LOGIN:
433 {
434 if (response.compare("Player has already logged in.") == 0)
435 {
436 username.clear();
437 cout << "User login failed" << endl;
438 }
439 else if (response.compare("Incorrect username or password") == 0)
440 {
441 username.clear();
442 cout << "User login failed" << endl;
443 }
444 else
445 {
446 state = STATE_LOGIN;
447 wndCurrent = wndMain;
448
449 Player p("", "");
450 p.deserialize(msg.buffer);
451 mapPlayers[p.id] = p;
452 curPlayerId = p.id;
453
454 cout << "Got a valid login response with the player" << endl;
455 cout << "Player id: " << curPlayerId << endl;
456 cout << "map size: " << mapPlayers.size() << endl;
457 }
458
459 break;
460 }
461 case MSG_TYPE_PLAYER: // kind of hacky to put this here
462 {
463 cout << "Got MSG_TYPE_PLAYER message in STATE_START" << endl;
464
465 Player p("", "");
466 p.deserialize(msg.buffer);
467 p.timeLastUpdated = getCurrentMillis();
468 mapPlayers[p.id] = p;
469
470 cout << "new player id: " << p.id << endl;
471 cout << "map size: " << mapPlayers.size() << endl;
472
473 break;
474 }
475 case MSG_TYPE_OBJECT:
476 {
477 cout << "Received OBJECT message in STATE_START." << endl;
478
479 WorldMap::Object o(0, WorldMap::OBJECT_NONE, 0, 0);
480 o.deserialize(msg.buffer);
481 cout << "object id: " << o.id << endl;
482 gameMap->updateObject(o.id, o.type, o.pos.x, o.pos.y);
483
484 break;
485 }
486 }
487
488 break;
489 }
490 case STATE_LOGIN:
491 {
492 switch(msg.type)
493 {
494 case MSG_TYPE_REGISTER:
495 {
496 break;
497 }
498 case MSG_TYPE_LOGIN:
499 {
500 cout << "Got a login message" << endl;
501
502 chatConsole.addLine(response);
503 cout << "Added new line" << endl;
504
505 break;
506 }
507 case MSG_TYPE_LOGOUT:
508 {
509 cout << "Got a logout message" << endl;
510
511 if (response.compare("You have successfully logged out.") == 0)
512 {
513 cout << "Logged out" << endl;
514 state = STATE_START;
515 wndCurrent = wndLogin;
516 }
517
518 break;
519 }
520 case MSG_TYPE_PLAYER:
521 {
522 cout << "Got MSG_TYPE_PLAYER message in STATE_LOGIN" << endl;
523
524 Player p("", "");
525 p.deserialize(msg.buffer);
526 p.timeLastUpdated = getCurrentMillis();
527 mapPlayers[p.id] = p;
528
529 break;
530 }
531 case MSG_TYPE_PLAYER_MOVE:
532 {
533 cout << "Got a player move message" << endl;
534
535 unsigned int id;
536 int x, y;
537
538 memcpy(&id, msg.buffer, 4);
539 memcpy(&x, msg.buffer+4, 4);
540 memcpy(&y, msg.buffer+8, 4);
541
542 mapPlayers[id].target.x = x;
543 mapPlayers[id].target.y = y;
544
545 break;
546 }
547 case MSG_TYPE_CHAT:
548 {
549 chatConsole.addLine(response);
550
551 break;
552 }
553 case MSG_TYPE_OBJECT:
554 {
555 cout << "Received object message in STATE_LOGIN." << endl;
556
557 WorldMap::Object o(0, WorldMap::OBJECT_NONE, 0, 0);
558 o.deserialize(msg.buffer);
559 gameMap->updateObject(o.id, o.type, o.pos.x, o.pos.y);
560
561 break;
562 }
563 case MSG_TYPE_REMOVE_OBJECT:
564 {
565 cout << "Received REMOVE_OBJECT message!" << endl;
566
567 int id;
568 memcpy(&id, msg.buffer, 4);
569
570 cout << "Removing object with id " << id << endl;
571
572 if (!gameMap->removeObject(id))
573 cout << "Did not remove the object" << endl;
574
575 break;
576 }
577 default:
578 {
579 cout << "Received an unexpected message type: " << msg.type << endl;
580 }
581 }
582
583 break;
584 }
585 default:
586 {
587 cout << "The state has an invalid value: " << state << endl;
588
589 break;
590 }
591 }
592}
593
594// this should probably be in the WorldMap class
595void drawMap(WorldMap* gameMap)
596{
597 POSITION mapPos;
598 mapPos.x = 0;
599 mapPos.y = 0;
600 mapPos = mapToScreen(mapPos);
601
602 for (int x=0; x<12; x++)
603 {
604 for (int y=0; y<12; y++)
605 {
606 WorldMap::TerrainType el = gameMap->getElement(x, y);
607 WorldMap::StructureType structure = gameMap->getStructure(x, y);
608
609 if (el == WorldMap::TERRAIN_GRASS)
610 al_draw_filled_rectangle(x*25+mapPos.x, y*25+mapPos.y, x*25+25+mapPos.x, y*25+25+mapPos.y, al_map_rgb(0, 255, 0));
611 else if (el == WorldMap::TERRAIN_OCEAN)
612 al_draw_filled_rectangle(x*25+mapPos.x, y*25+mapPos.y, x*25+25+mapPos.x, y*25+25+mapPos.y, al_map_rgb(0, 0, 255));
613 else if (el == WorldMap::TERRAIN_ROCK)
614 al_draw_filled_rectangle(x*25+mapPos.x, y*25+mapPos.y, x*25+25+mapPos.x, y*25+25+mapPos.y, al_map_rgb(100, 100, 0));
615
616 if (structure == WorldMap::STRUCTURE_BLUE_FLAG) {
617 al_draw_circle(x*25+12+mapPos.x, y*25+12+mapPos.y, 12, al_map_rgb(0, 0, 0), 3);
618 //al_draw_filled_rectangle(x*25+5+mapPos.x, y*25+5+mapPos.y, x*25+20+mapPos.x, y*25+20+mapPos.y, al_map_rgb(0, 0, 255));
619 }else if (structure == WorldMap::STRUCTURE_RED_FLAG) {
620 al_draw_circle(x*25+12+mapPos.x, y*25+12+mapPos.y, 12, al_map_rgb(0, 0, 0), 3);
621 //al_draw_filled_rectangle(x*25+5+mapPos.x, y*25+5+mapPos.y, x*25+20+mapPos.x, y*25+20+mapPos.y, al_map_rgb(255, 0, 0));
622 }
623 }
624 }
625
626 for (int x=0; x<12; x++)
627 {
628 for (int y=0; y<12; y++)
629 {
630 vector<WorldMap::Object> vctObjects = gameMap->getObjects(x, y);
631
632 vector<WorldMap::Object>::iterator it;
633 for(it = vctObjects.begin(); it != vctObjects.end(); it++) {
634 switch(it->type) {
635 case WorldMap::OBJECT_BLUE_FLAG:
636 al_draw_filled_rectangle(it->pos.x-8+mapPos.x, it->pos.y-8+mapPos.y, it->pos.x+8+mapPos.x, it->pos.y+8+mapPos.y, al_map_rgb(0, 0, 255));
637 break;
638 case WorldMap::OBJECT_RED_FLAG:
639 al_draw_filled_rectangle(it->pos.x-8+mapPos.x, it->pos.y-8+mapPos.y, it->pos.x+8+mapPos.x, it->pos.y+8+mapPos.y, al_map_rgb(255, 0, 0));
640 break;
641 }
642 }
643 }
644 }
645}
646
647void drawPlayers(map<unsigned int, Player>& mapPlayers, unsigned int curPlayerId)
648{
649 map<unsigned int, Player>::iterator it;
650
651 Player* p;
652 POSITION pos;
653
654 for(it = mapPlayers.begin(); it != mapPlayers.end(); it++)
655 {
656 p = &it->second;
657 pos = mapToScreen(p->pos);
658
659 if (p->id == curPlayerId)
660 al_draw_filled_circle(pos.x, pos.y, 14, al_map_rgb(0, 0, 0));
661
662 if (p->team == 0)
663 al_draw_filled_circle(pos.x, pos.y, 12, al_map_rgb(0, 0, 255));
664 else if (p->team == 1)
665 al_draw_filled_circle(pos.x, pos.y, 12, al_map_rgb(255, 0, 0));
666
667 if (p->hasBlueFlag)
668 al_draw_filled_rectangle(pos.x+4, pos.y-18, pos.x+18, pos.y-4, al_map_rgb(0, 0, 255));
669 else if (p->hasRedFlag)
670 al_draw_filled_rectangle(pos.x+4, pos.y-18, pos.x+18, pos.y-4, al_map_rgb(255, 0, 0));
671 }
672}
673
674void registerAccount()
675{
676 string username = txtUsername->getStr();
677 string password = txtPassword->getStr();
678
679 txtUsername->clear();
680 txtPassword->clear();
681
682 msgTo.type = MSG_TYPE_REGISTER;
683
684 strcpy(msgTo.buffer, username.c_str());
685 strcpy(msgTo.buffer+username.size()+1, password.c_str());
686
687 sendMessage(&msgTo, sock, &server);
688}
689
690void login()
691{
692 string strUsername = txtUsername->getStr();
693 string strPassword = txtPassword->getStr();
694 username = strUsername;
695
696 txtUsername->clear();
697 txtPassword->clear();
698
699 msgTo.type = MSG_TYPE_LOGIN;
700
701 strcpy(msgTo.buffer, strUsername.c_str());
702 strcpy(msgTo.buffer+username.size()+1, strPassword.c_str());
703
704 sendMessage(&msgTo, sock, &server);
705}
706
707void logout()
708{
709 txtChat->clear();
710
711 msgTo.type = MSG_TYPE_LOGOUT;
712
713 strcpy(msgTo.buffer, username.c_str());
714
715 sendMessage(&msgTo, sock, &server);
716}
717
718void quit()
719{
720 doexit = true;
721}
722
723void sendChatMessage()
724{
725 string msg = txtChat->getStr();
726
727 txtChat->clear();
728
729 msgTo.type = MSG_TYPE_CHAT;
730
731 strcpy(msgTo.buffer, msg.c_str());
732
733 sendMessage(&msgTo, sock, &server);
734}
Note: See TracBrowser for help on using the repository browser.