source: network-game/server/server.cpp@ 8554263

Last change on this file since 8554263 was 8554263, checked in by dportnoy <dmp1488@…>, 11 years ago

Restructuring and code cleanup

  • Property mode set to 100644
File size: 40.4 KB
Line 
1#include <cstdlib>
2#include <cstdio>
3#include <unistd.h>
4#include <string>
5#include <iostream>
6#include <sstream>
7#include <fstream>
8#include <cstring>
9#include <cmath>
10
11#include <vector>
12#include <map>
13
14#include <csignal>
15
16#include <sys/time.h>
17
18#include <sys/socket.h>
19#include <netdb.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22
23#include <crypt.h>
24
25/*
26#include <openssl/bio.h>
27#include <openssl/ssl.h>
28#include <openssl/err.h>
29*/
30
31#include "../common/Compiler.h"
32#include "../common/Common.h"
33#include "../common/MessageProcessor.h"
34#include "../common/WorldMap.h"
35#include "../common/Player.h"
36#include "../common/Projectile.h"
37#include "../common/Game.h"
38#include "../common/GameSummary.h"
39
40#include "DataAccess.h"
41
42using namespace std;
43
44bool done;
45
46// from used to be const. Removed that so I could take a reference
47// and use it to send messages
48void processMessage(const NETWORK_MSG &clientMsg, struct sockaddr_in &from, MessageProcessor &msgProcessor, map<unsigned int, Player*>& mapPlayers, map<string, Game*>& mapGames, WorldMap* gameMap, unsigned int& unusedPlayerId, NETWORK_MSG &serverMsg, int &scoreBlue, int &scoreRed);
49
50void broadcastMessage(MessageProcessor &msgProcessor, NETWORK_MSG &serverMsg, map<unsigned int, Player*>& players);
51void updateUnusedPlayerId(unsigned int& id, map<unsigned int, Player*>& mapPlayers);
52Player *findPlayerByName(map<unsigned int, Player*> &m, string name);
53Player *findPlayerByAddr(map<unsigned int, Player*> &m, const sockaddr_in &addr);
54void damagePlayer(Player *p, int damage);
55
56void addObjectToMap(WorldMap::ObjectType objectType, int x, int y, WorldMap* gameMap, map<unsigned int, Player*>& mapPlayers, MessageProcessor &msgProcessor);
57
58void quit(int sig) {
59 done = true;
60}
61
62int main(int argc, char *argv[])
63{
64 int sock, length;
65 struct sockaddr_in server;
66 struct sockaddr_in from; // info of client sending the message
67 NETWORK_MSG clientMsg, serverMsg;
68 MessageProcessor msgProcessor;
69 map<unsigned int, Player*> mapPlayers;
70 map<unsigned int, Projectile> mapProjectiles;
71 map<string, Game*> mapGames;
72 unsigned int unusedPlayerId = 1, unusedProjectileId = 1;
73 int scoreBlue, scoreRed;
74 ofstream outputLog;
75
76 done = false;
77
78 scoreBlue = 0;
79 scoreRed = 0;
80
81 signal(SIGINT, quit);
82
83 //SSL_load_error_strings();
84 //ERR_load_BIO_strings();
85 //OpenSSL_add_all_algorithms();
86
87 if (argc != 2)
88 {
89 cerr << "ERROR, expected server [domain] [port]" << endl;
90 exit(1);
91 }
92
93 outputLog.open("server.log", ios::app);
94 outputLog << "Started server on " << getCurrentDateTimeString() << endl;
95
96 WorldMap* gameMap = WorldMap::loadMapFromFile("../data/map.txt");
97
98 // add some items to the map. They will be sent out
99 // to players when they login
100 for (int y=0; y<gameMap->height; y++)
101 {
102 for (int x=0; x<gameMap->width; x++)
103 {
104 switch (gameMap->getStructure(x, y))
105 {
106 case WorldMap::STRUCTURE_BLUE_FLAG:
107 gameMap->addObject(WorldMap::OBJECT_BLUE_FLAG, x*25+12, y*25+12);
108 break;
109 case WorldMap::STRUCTURE_RED_FLAG:
110 gameMap->addObject(WorldMap::OBJECT_RED_FLAG, x*25+12, y*25+12);
111 break;
112 }
113 }
114 }
115
116 sock = socket(AF_INET, SOCK_DGRAM, 0);
117 if (sock < 0)
118 error("Opening socket");
119 length = sizeof(server);
120 bzero(&server,length);
121 server.sin_family=AF_INET;
122 server.sin_port=htons(atoi(argv[1]));
123 server.sin_addr.s_addr=INADDR_ANY;
124 if ( bind(sock, (struct sockaddr *)&server, length) < 0 )
125 error("binding");
126
127 set_nonblock(sock);
128
129 msgProcessor = MessageProcessor(sock, &outputLog);
130
131 timespec ts;
132 int timeLastUpdated = 0, curTime = 0, timeLastBroadcast = 0;
133 while (!done)
134 {
135 usleep(5000);
136
137 clock_gettime(CLOCK_REALTIME, &ts);
138 // make the number smaller so millis can fit in an int
139 ts.tv_sec -= 1368000000;
140 curTime = ts.tv_sec*1000 + ts.tv_nsec/1000000;
141
142 if (timeLastUpdated == 0 || (curTime-timeLastUpdated) >= 50)
143 {
144 timeLastUpdated = curTime;
145
146 msgProcessor.cleanAckedMessages();
147 msgProcessor.resendUnackedMessages();
148
149 map<unsigned int, Player*>::iterator it;
150
151 cout << "Updating player targets and respawning dead players" << endl;
152
153 // set targets for all chasing players (or make them attack if they're close enough)
154 // this should be moved into the games loop
155 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
156 {
157 Player* p = it->second;
158
159 // check if it's time to revive dead players
160 if (p->isDead)
161 {
162 cout << "Player is dead" << endl;
163
164 if (getCurrentMillis() - p->timeDied >= 10000)
165 {
166 p->isDead = false;
167
168 POSITION spawnPos;
169
170 switch (p->team)
171 {
172 case 0:// blue team
173 spawnPos = p->currentGame->getMap()->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
174 break;
175 case 1:// red team
176 spawnPos = p->currentGame->getMap()->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
177 break;
178 default:
179 // should never go here
180 cout << "Error: Invalid team" << endl;
181 break;
182 }
183
184 // spawn the player to the right of their flag location
185 spawnPos.x = (spawnPos.x+1) * 25 + 12;
186 spawnPos.y = spawnPos.y * 25 + 12;
187
188 p->pos = spawnPos.toFloat();
189 p->target = spawnPos;
190 p->health = p->maxHealth;
191
192 serverMsg.type = MSG_TYPE_PLAYER;
193 p->serialize(serverMsg.buffer);
194
195 broadcastMessage(msgProcessor, serverMsg, p->currentGame->getPlayers());
196 }
197
198 continue;
199 }
200
201 if (p->currentGame != NULL) {
202 map<unsigned int, Player*> playersInGame = p->currentGame->getPlayers();
203 if (p->updateTarget(playersInGame))
204 {
205 serverMsg.type = MSG_TYPE_PLAYER;
206 p->serialize(serverMsg.buffer);
207
208 broadcastMessage(msgProcessor, serverMsg, playersInGame);
209 }
210 }
211 }
212
213 cout << "Processing players in a game" << endl;
214
215 // process players currently in a game
216 FLOAT_POSITION oldPos;
217 map<string, Game*>::iterator itGames;
218 Game* game = NULL;
219 WorldMap* gameMap = NULL;
220 bool gameFinished;
221
222 for (itGames = mapGames.begin(); itGames != mapGames.end();) {
223 game = itGames->second;
224 gameMap = game->getMap();
225 map<unsigned int, Player*>& playersInGame = game->getPlayers();
226 gameFinished = false;
227
228 for (it = game->getPlayers().begin(); it != game->getPlayers().end(); it++)
229 {
230 Player* p = it->second;
231
232 cout << "moving player" << endl;
233 bool broadcastMove = false;
234
235 // xompute playersInGame here
236
237 // move player and perform associated tasks
238 oldPos = p->pos;
239 if (p->move(gameMap)) {
240
241 cout << "player moved" << endl;
242 if (game->processPlayerMovement(p, oldPos))
243 broadcastMove = true;
244 cout << "player move processed" << endl;
245
246 WorldMap::ObjectType flagType;
247 POSITION pos;
248 bool flagTurnedIn = false;
249 bool flagReturned = false;
250 bool ownFlagAtBase = false;
251
252 // need to figure out how to move this to a different file
253 // while still sending back flag type and position
254 switch(gameMap->getStructure(p->pos.x/25, p->pos.y/25))
255 {
256 case WorldMap::STRUCTURE_BLUE_FLAG:
257 {
258 if (p->team == 0 && p->hasRedFlag)
259 {
260 // check that your flag is at your base
261 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
262
263 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
264 vector<WorldMap::Object>::iterator itObjects;
265
266 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++)
267 {
268 if (itObjects->type == WorldMap::OBJECT_BLUE_FLAG)
269 {
270 if (itObjects->pos.x == pos.x*25+12 && itObjects->pos.y == pos.y*25+12)
271 {
272 ownFlagAtBase = true;
273 break;
274 }
275 }
276 }
277
278 if (ownFlagAtBase)
279 {
280 p->hasRedFlag = false;
281 flagType = WorldMap::OBJECT_RED_FLAG;
282 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
283 flagTurnedIn = true;
284 scoreBlue++;
285 }
286 }
287
288 break;
289 }
290 case WorldMap::STRUCTURE_RED_FLAG:
291 {
292 if (p->team == 1 && p->hasBlueFlag)
293 {
294 // check that your flag is at your base
295 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
296
297 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
298 vector<WorldMap::Object>::iterator itObjects;
299
300 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++)
301 {
302 if (itObjects->type == WorldMap::OBJECT_RED_FLAG)
303 {
304 if (itObjects->pos.x == pos.x*25+12 && itObjects->pos.y == pos.y*25+12)
305 {
306 ownFlagAtBase = true;
307 break;
308 }
309 }
310 }
311
312 if (ownFlagAtBase)
313 {
314 p->hasBlueFlag = false;
315 flagType = WorldMap::OBJECT_BLUE_FLAG;
316 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
317 flagTurnedIn = true;
318 scoreRed++;
319 }
320 }
321
322 break;
323 }
324 }
325
326 if (flagTurnedIn)
327 {
328 // send an OBJECT message to add the flag back to its spawn point
329 pos.x = pos.x*25+12;
330 pos.y = pos.y*25+12;
331 gameMap->addObject(flagType, pos.x, pos.y);
332
333 serverMsg.type = MSG_TYPE_OBJECT;
334 gameMap->getObjects()->back().serialize(serverMsg.buffer);
335 broadcastMessage(msgProcessor, serverMsg, playersInGame);
336
337 serverMsg.type = MSG_TYPE_SCORE;
338 memcpy(serverMsg.buffer, &scoreBlue, 4);
339 memcpy(serverMsg.buffer+4, &scoreRed, 4);
340 broadcastMessage(msgProcessor, serverMsg, playersInGame);
341
342 // check to see if the game should end
343 // move to its own method
344 if (scoreBlue == 3 || scoreRed == 3) {
345 gameFinished = true;
346
347 unsigned int winningTeam;
348 if (scoreBlue == 3)
349 winningTeam = 0;
350 else if (scoreRed == 3)
351 winningTeam = 1;
352
353 serverMsg.type = MSG_TYPE_FINISH_GAME;
354
355 // I should create an instance of the GameSummary object here and just serialize it into this message
356 memcpy(serverMsg.buffer, &winningTeam, 4);
357 memcpy(serverMsg.buffer+4, &scoreBlue, 4);
358 memcpy(serverMsg.buffer+8, &scoreRed, 4);
359 strcpy(serverMsg.buffer+12, game->getName().c_str());
360 broadcastMessage(msgProcessor, serverMsg, playersInGame);
361 }
362
363 // this means a PLAYER message will be sent
364 broadcastMove = true;
365 }
366
367 // go through all objects and check if the player is close to one and if its their flag
368 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
369 vector<WorldMap::Object>::iterator itObjects;
370 POSITION structPos;
371
372 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++)
373 {
374 POSITION pos = itObjects->pos;
375
376 if (posDistance(p->pos, pos.toFloat()) < 10)
377 {
378 if (p->team == 0 &&
379 itObjects->type == WorldMap::OBJECT_BLUE_FLAG)
380 {
381 structPos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
382 flagReturned = true;
383 break;
384 }
385 else if (p->team == 1 &&
386 itObjects->type == WorldMap::OBJECT_RED_FLAG)
387 {
388 structPos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
389 flagReturned = true;
390 break;
391 }
392 }
393 }
394
395 if (flagReturned)
396 {
397 itObjects->pos.x = structPos.x*25+12;
398 itObjects->pos.y = structPos.y*25+12;
399
400 serverMsg.type = MSG_TYPE_OBJECT;
401 itObjects->serialize(serverMsg.buffer);
402 broadcastMessage(msgProcessor, serverMsg, playersInGame);
403 }
404
405 if (broadcastMove)
406 {
407 serverMsg.type = MSG_TYPE_PLAYER;
408 p->serialize(serverMsg.buffer);
409 broadcastMessage(msgProcessor, serverMsg, playersInGame);
410 }
411 }
412
413 cout << "processing player attack" << endl;
414
415 // check if the player's attack animation is complete
416 if (p->isAttacking && p->timeAttackStarted+p->attackCooldown <= getCurrentMillis())
417 {
418 p->isAttacking = false;
419 cout << "Attack animation is complete" << endl;
420
421 //send everyone an ATTACK message
422 cout << "about to broadcast attack" << endl;
423
424 serverMsg.type = MSG_TYPE_ATTACK;
425 memcpy(serverMsg.buffer, &p->id, 4);
426 memcpy(serverMsg.buffer+4, &p->targetPlayer, 4);
427 broadcastMessage(msgProcessor, serverMsg, playersInGame);
428
429 if (p->attackType == Player::ATTACK_MELEE)
430 {
431 cout << "Melee attack" << endl;
432
433 Player* target = playersInGame[p->targetPlayer];
434 damagePlayer(target, p->damage);
435
436 if (target->isDead)
437 {
438 WorldMap::ObjectType flagType = WorldMap::OBJECT_NONE;
439 if (target->hasBlueFlag)
440 flagType = WorldMap::OBJECT_BLUE_FLAG;
441 else if (target->hasRedFlag)
442 flagType = WorldMap::OBJECT_RED_FLAG;
443
444 if (flagType != WorldMap::OBJECT_NONE) {
445 addObjectToMap(flagType, target->pos.x, target->pos.y, gameMap, playersInGame, msgProcessor);
446 }
447 }
448
449 serverMsg.type = MSG_TYPE_PLAYER;
450 target->serialize(serverMsg.buffer);
451 }
452 else if (p->attackType == Player::ATTACK_RANGED)
453 {
454 cout << "Ranged attack" << endl;
455
456 Projectile proj(p->pos.x, p->pos.y, p->targetPlayer, p->damage);
457 game->assignProjectileId(&proj);
458 game->addProjectile(proj);
459
460 int x = p->pos.x;
461 int y = p->pos.y;
462
463 serverMsg.type = MSG_TYPE_PROJECTILE;
464 memcpy(serverMsg.buffer, &proj.id, 4);
465 memcpy(serverMsg.buffer+4, &x, 4);
466 memcpy(serverMsg.buffer+8, &y, 4);
467 memcpy(serverMsg.buffer+12, &p->targetPlayer, 4);
468 }
469 else
470 cout << "Invalid attack type: " << p->attackType << endl;
471
472 broadcastMessage(msgProcessor, serverMsg, playersInGame);
473 }
474 }
475
476 if (gameFinished) {
477 // send a GAME_INFO message with 0 players to force clients to delete the game
478 int numPlayers = 0;
479 serverMsg.type = MSG_TYPE_GAME_INFO;
480 memcpy(serverMsg.buffer, &numPlayers, 4);
481 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
482
483 // erase game from server
484 mapGames.erase(itGames++);
485 delete game;
486 }else
487 itGames++;
488 }
489
490 cout << "Processing projectiles" << endl;
491
492 // move all projectiles
493 // see if this can be moved inside the game class
494 // this method can be moved when I add a MessageProcessor to the Game class
495 map<unsigned int, Projectile>::iterator itProj;
496 for (itGames = mapGames.begin(); itGames != mapGames.end(); itGames++) {
497 game = itGames->second;
498 for (itProj = game->getProjectiles().begin(); itProj != game->getProjectiles().end(); itProj++)
499 {
500 cout << "About to call projectile move" << endl;
501 if (itProj->second.move(game->getPlayers()))
502 {
503 // send a REMOVE_PROJECTILE message
504 cout << "send a REMOVE_PROJECTILE message" << endl;
505 serverMsg.type = MSG_TYPE_REMOVE_PROJECTILE;
506 memcpy(serverMsg.buffer, &itProj->second.id, 4);
507 game->removeProjectile(itProj->second.id);
508 broadcastMessage(msgProcessor, serverMsg, game->getPlayers());
509
510 Player* target = game->getPlayers()[itProj->second.target];
511 damagePlayer(target, itProj->second.damage);
512
513 if (target->isDead)
514 {
515 WorldMap::ObjectType flagType = WorldMap::OBJECT_NONE;
516 if (target->hasBlueFlag)
517 flagType = WorldMap::OBJECT_BLUE_FLAG;
518 else if (target->hasRedFlag)
519 flagType = WorldMap::OBJECT_RED_FLAG;
520
521 if (flagType != WorldMap::OBJECT_NONE)
522 addObjectToMap(flagType, target->pos.x, target->pos.y, game->getMap(), game->getPlayers(), msgProcessor);
523 }
524
525 // send a PLAYER message after dealing damage
526 serverMsg.type = MSG_TYPE_PLAYER;
527 target->serialize(serverMsg.buffer);
528 broadcastMessage(msgProcessor, serverMsg, game->getPlayers());
529 }
530 }
531 }
532 }
533
534 if (msgProcessor.receiveMessage(&clientMsg, &from) >= 0)
535 {
536 processMessage(clientMsg, from, msgProcessor, mapPlayers, mapGames, gameMap, unusedPlayerId, serverMsg, scoreBlue, scoreRed);
537
538 cout << "Finished processing the message" << endl;
539 }
540 }
541
542 outputLog << "Stopped server on " << getCurrentDateTimeString() << endl;
543 outputLog.close();
544
545 // delete all games
546 map<string, Game*>::iterator itGames;
547 for (itGames = mapGames.begin(); itGames != mapGames.end(); itGames++)
548 {
549 delete itGames->second;
550 }
551
552 map<unsigned int, Player*>::iterator itPlayers;
553 for (itPlayers = mapPlayers.begin(); itPlayers != mapPlayers.end(); itPlayers++)
554 {
555 delete itPlayers->second;
556 }
557
558 return 0;
559}
560
561void processMessage(const NETWORK_MSG &clientMsg, struct sockaddr_in &from, MessageProcessor &msgProcessor, map<unsigned int, Player*>& mapPlayers, map<string, Game*>& mapGames, WorldMap* gameMap, unsigned int& unusedPlayerId, NETWORK_MSG &serverMsg, int &scoreBlue, int &scoreRed)
562{
563 DataAccess da;
564
565 cout << "Inside processMessage" << endl;
566
567 cout << "Received message" << endl;
568 cout << "MSG: type: " << clientMsg.type << endl;
569 cout << "MSG contents: " << clientMsg.buffer << endl;
570
571 // Check that if an invalid message is sent, the client will correctly
572 // receive and display the response. Maybe make a special error msg type
573 switch(clientMsg.type)
574 {
575 case MSG_TYPE_REGISTER:
576 {
577 string username(clientMsg.buffer);
578 string password(strchr(clientMsg.buffer, '\0')+1);
579 Player::PlayerClass playerClass;
580
581 memcpy(&playerClass, clientMsg.buffer+username.length()+password.length()+2, 4);
582
583 cout << "username: " << username << endl;
584 cout << "password: " << password << endl;
585
586 bool validClass = false;
587
588 switch(playerClass) {
589 case Player::CLASS_WARRIOR:
590 case Player::CLASS_RANGER:
591 validClass = true;
592 break;
593 default:
594 validClass = false;
595 break;
596 }
597
598 serverMsg.type = MSG_TYPE_REGISTER;
599
600 if (validClass) {
601 int error = da.insertPlayer(username, password, playerClass);
602
603 if (error)
604 strcpy(serverMsg.buffer, "Registration failed. Please try again.");
605 else
606 strcpy(serverMsg.buffer, "Registration successful.");
607 }else
608 strcpy(serverMsg.buffer, "You didn't select a class");
609
610 msgProcessor.sendMessage(&serverMsg, &from);
611
612 break;
613 }
614 case MSG_TYPE_LOGIN:
615 {
616 cout << "Got login message" << endl;
617
618 string username(clientMsg.buffer);
619 string password(strchr(clientMsg.buffer, '\0')+1);
620
621 Player* p = da.getPlayer(username);
622
623 if (p == NULL || !da.verifyPassword(password, p->password))
624 {
625 strcpy(serverMsg.buffer, "Incorrect username or password");
626 if (p != NULL)
627 delete(p);
628 }
629 else if(findPlayerByName(mapPlayers, username) != NULL)
630 {
631 strcpy(serverMsg.buffer, "Player has already logged in.");
632 delete(p);
633 }
634 else
635 {
636 updateUnusedPlayerId(unusedPlayerId, mapPlayers);
637 p->id = unusedPlayerId;
638 cout << "new player id: " << p->id << endl;
639 p->setAddr(from);
640 p->currentGame = NULL;
641
642 // choose a random team (either 0 or 1)
643 p->team = rand() % 2;
644
645 serverMsg.type = MSG_TYPE_PLAYER;
646 // tell the new player about all the existing players
647 cout << "Sending other players to new player" << endl;
648
649 map<unsigned int, Player*>::iterator it;
650 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
651 {
652 it->second->serialize(serverMsg.buffer);
653
654 cout << "sending info about " << it->second->name << endl;
655 cout << "sending id " << it->second->id << endl;
656 msgProcessor.sendMessage(&serverMsg, &from);
657 }
658
659 // tell the new player about all map objects
660 // (currently just the flags)
661 serverMsg.type = MSG_TYPE_OBJECT;
662 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
663 vector<WorldMap::Object>::iterator itObjects;
664 cout << "sending items" << endl;
665 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
666 itObjects->serialize(serverMsg.buffer);
667 cout << "sending item id " << itObjects->id << endl;
668 msgProcessor.sendMessage(&serverMsg, &from);
669 }
670
671 // send info about existing games to new player
672 map<string, Game*>::iterator itGames;
673 Game* g;
674 int numPlayers;
675 serverMsg.type = MSG_TYPE_GAME_INFO;
676
677 for (itGames = mapGames.begin(); itGames != mapGames.end(); itGames++)
678 {
679 g = itGames->second;
680 numPlayers = g->getNumPlayers();
681 memcpy(serverMsg.buffer, &numPlayers, 4);
682 strcpy(serverMsg.buffer+4, g->getName().c_str());
683 msgProcessor.sendMessage(&serverMsg, &from);
684 }
685
686 // send the current score
687 serverMsg.type = MSG_TYPE_SCORE;
688 memcpy(serverMsg.buffer, &scoreBlue, 4);
689 memcpy(serverMsg.buffer+4, &scoreRed, 4);
690 msgProcessor.sendMessage(&serverMsg, &from);
691
692 serverMsg.type = MSG_TYPE_PLAYER;
693 p->serialize(serverMsg.buffer);
694 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
695
696 mapPlayers[unusedPlayerId] = p;
697 }
698
699 serverMsg.type = MSG_TYPE_LOGIN;
700 msgProcessor.sendMessage(&serverMsg, &from);
701
702 break;
703 }
704 case MSG_TYPE_LOGOUT:
705 {
706 string name(clientMsg.buffer);
707 cout << "Player logging out: " << name << endl;
708
709 Player *p = findPlayerByName(mapPlayers, name);
710
711 if (p == NULL)
712 {
713 strcpy(serverMsg.buffer+4, "That player is not logged in. This is either a bug, or you're trying to hack the server.");
714 cout << "Player not logged in" << endl;
715 }
716 else if ( p->addr.sin_addr.s_addr != from.sin_addr.s_addr ||
717 p->addr.sin_port != from.sin_port )
718 {
719 strcpy(serverMsg.buffer+4, "That player is logged in using a differemt connection. This is either a bug, or you're trying to hack the server.");
720 cout << "Player logged in using a different connection" << endl;
721 }
722 else
723 {
724 if (!p->isDead) {
725 WorldMap::ObjectType flagType = WorldMap::OBJECT_NONE;
726 if (p->hasBlueFlag)
727 flagType = WorldMap::OBJECT_BLUE_FLAG;
728 else if (p->hasRedFlag)
729 flagType = WorldMap::OBJECT_RED_FLAG;
730
731 if (flagType != WorldMap::OBJECT_NONE) {
732 addObjectToMap(flagType, p->pos.x, p->pos.y, gameMap, mapPlayers, msgProcessor);
733 }
734 }
735
736 // broadcast to all players before deleting p from the map
737 serverMsg.type = MSG_TYPE_LOGOUT;
738 memcpy(serverMsg.buffer, &p->id, 4);
739
740 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
741
742 if (p->id < unusedPlayerId)
743 unusedPlayerId = p->id;
744
745 mapPlayers.erase(p->id);
746 delete p;
747
748 strcpy(serverMsg.buffer+4, "You have successfully logged out.");
749 }
750
751 serverMsg.type = MSG_TYPE_LOGOUT;
752 msgProcessor.sendMessage(&serverMsg, &from);
753
754 break;
755 }
756 case MSG_TYPE_CHAT:
757 {
758 cout << "Got a chat message" << endl;
759
760 serverMsg.type = MSG_TYPE_CHAT;
761
762 Player *p = findPlayerByAddr(mapPlayers, from);
763
764 if (p == NULL)
765 {
766 strcpy(serverMsg.buffer, "No player is logged in using this connection. This is either a bug, or you're trying to hack the server.");
767 msgProcessor.sendMessage(&serverMsg, &from);
768 }
769 else
770 {
771 ostringstream oss;
772 oss << p->name << ": " << clientMsg.buffer;
773
774 strcpy(serverMsg.buffer, oss.str().c_str());
775 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
776 }
777
778 break;
779 }
780 case MSG_TYPE_PLAYER_MOVE:
781 {
782 cout << "PLAYER_MOVE" << endl;
783
784 int id, x, y;
785
786 memcpy(&id, clientMsg.buffer, 4);
787 memcpy(&x, clientMsg.buffer+4, 4);
788 memcpy(&y, clientMsg.buffer+8, 4);
789
790 cout << "x: " << x << endl;
791 cout << "y: " << y << endl;
792 cout << "id: " << id << endl;
793
794 Player* p = mapPlayers[id];
795 bool validMessage = false;
796
797 if ( p->addr.sin_addr.s_addr == from.sin_addr.s_addr &&
798 p->addr.sin_port == from.sin_port )
799 {
800 if (p->currentGame->startPlayerMovement(id, x, y)) {
801 serverMsg.type = MSG_TYPE_PLAYER_MOVE;
802
803 memcpy(serverMsg.buffer, &id, 4);
804 memcpy(serverMsg.buffer+4, &p->target.x, 4);
805 memcpy(serverMsg.buffer+8, &p->target.y, 4);
806
807 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
808
809 validMessage = true;
810 }
811 else
812 cout << "Bad terrain detected" << endl;
813 }
814 else
815 cout << "Player id (" << id << ") doesn't match sender" << endl;
816
817 if (!validMessage)
818 msgProcessor.sendMessage(&serverMsg, &from);
819
820 break;
821 }
822 case MSG_TYPE_PICKUP_FLAG:
823 {
824 // may want to check the id matches the sender, just like for PLAYER_NOVE
825 cout << "PICKUP_FLAG" << endl;
826
827 int id;
828
829 memcpy(&id, clientMsg.buffer, 4);
830 cout << "id: " << id << endl;
831
832 Player* p = mapPlayers[id];
833 int objectId = p->currentGame->processFlagPickupRequest(p);
834
835 if (objectId >= 0) {
836 map<unsigned int, Player*> players = p->currentGame->getPlayers();
837
838 serverMsg.type = MSG_TYPE_REMOVE_OBJECT;
839 memcpy(serverMsg.buffer, &objectId, 4);
840 broadcastMessage(msgProcessor, serverMsg, players);
841
842 serverMsg.type = MSG_TYPE_PLAYER;
843 p->serialize(serverMsg.buffer);
844 broadcastMessage(msgProcessor, serverMsg, players);
845 }
846
847 break;
848 }
849 case MSG_TYPE_DROP_FLAG:
850 {
851 // may want to check the id matches the sender, just like for PLAYER_NOVE
852 cout << "DROP_FLAG" << endl;
853
854 int id;
855
856 memcpy(&id, clientMsg.buffer, 4);
857 cout << "id: " << id << endl;
858
859 Player* p = mapPlayers[id];
860
861 WorldMap::ObjectType flagType = WorldMap::OBJECT_NONE;
862 if (p->hasBlueFlag)
863 flagType = WorldMap::OBJECT_BLUE_FLAG;
864 else if (p->hasRedFlag)
865 flagType = WorldMap::OBJECT_RED_FLAG;
866
867 map<unsigned int, Player*> players = p->currentGame->getPlayers();
868
869 addObjectToMap(flagType, p->pos.x, p->pos.y, p->currentGame->getMap(), players, msgProcessor);
870
871 p->hasBlueFlag = false;
872 p->hasRedFlag = false;
873
874 serverMsg.type = MSG_TYPE_PLAYER;
875 p->serialize(serverMsg.buffer);
876 broadcastMessage(msgProcessor, serverMsg, players);
877
878 break;
879 }
880 case MSG_TYPE_START_ATTACK:
881 {
882 cout << "Received a START_ATTACK message" << endl;
883
884 int id, targetId;
885
886 memcpy(&id, clientMsg.buffer, 4);
887 memcpy(&targetId, clientMsg.buffer+4, 4);
888
889 // need to make sure the target is in the sender's game
890
891 Player* p = mapPlayers[id];
892 p->targetPlayer = targetId;
893 p->isChasing = true;
894
895 map<unsigned int, Player*> players = p->currentGame->getPlayers();
896
897 serverMsg.type = MSG_TYPE_START_ATTACK;
898 memcpy(serverMsg.buffer, &id, 4);
899 memcpy(serverMsg.buffer+4, &targetId, 4);
900 broadcastMessage(msgProcessor, serverMsg, players);
901
902 break;
903 }
904 case MSG_TYPE_CREATE_GAME:
905 {
906 cout << "Received a CREATE_GAME message" << endl;
907
908 string gameName(clientMsg.buffer);
909 cout << "Game name: " << gameName << endl;
910
911 // check if this game already exists
912 if (mapGames.find(gameName) != mapGames.end()) {
913 cout << "Error: Game already exists" << endl;
914 serverMsg.type = MSG_TYPE_JOIN_GAME_FAILURE;
915 }else {
916 Game* g = new Game(gameName, "../data/map.txt");
917 mapGames[gameName] = g;
918
919 // add flag objects to the map
920 WorldMap* m = g->getMap();
921 for (int y=0; y<m->height; y++) {
922 for (int x=0; x<m->width; x++) {
923 switch (m->getStructure(x, y)) {
924 case WorldMap::STRUCTURE_BLUE_FLAG:
925 m->addObject(WorldMap::OBJECT_BLUE_FLAG, x*25+12, y*25+12);
926 break;
927 case WorldMap::STRUCTURE_RED_FLAG:
928 m->addObject(WorldMap::OBJECT_RED_FLAG, x*25+12, y*25+12);
929 break;
930 }
931 }
932 }
933
934 serverMsg.type = MSG_TYPE_JOIN_GAME_SUCCESS;
935 strcpy(serverMsg.buffer, gameName.c_str());
936 }
937
938 msgProcessor.sendMessage(&serverMsg, &from);
939
940 break;
941 }
942 case MSG_TYPE_JOIN_GAME:
943 {
944 cout << "Received a JOIN_GAME message" << endl;
945
946 string gameName(clientMsg.buffer);
947 cout << "Game name: " << gameName << endl;
948
949 // check if this game already exists
950 if (mapGames.find(gameName) == mapGames.end()) {
951 cout << "Error: Game does not exist" << endl;
952 serverMsg.type = MSG_TYPE_JOIN_GAME_FAILURE;
953 }else {
954 Game* g = mapGames[gameName];
955 map<unsigned int, Player*>& players = g->getPlayers();
956 Player* p = findPlayerByAddr(mapPlayers, from);
957
958 if (players.find(p->id) != players.end()) {
959 cout << "Player " << p->name << " trying to join a game he's already in" << endl;
960 serverMsg.type = MSG_TYPE_JOIN_GAME_FAILURE;
961 }else {
962 serverMsg.type = MSG_TYPE_JOIN_GAME_SUCCESS;
963 strcpy(serverMsg.buffer, gameName.c_str());
964 }
965 }
966
967 msgProcessor.sendMessage(&serverMsg, &from);
968
969 break;
970 }
971 case MSG_TYPE_LEAVE_GAME:
972 {
973 cout << "Received a LEAVE_GAME message" << endl;
974
975 Player* p = findPlayerByAddr(mapPlayers, from);
976 Game* g = p->currentGame;
977
978 if (g == NULL) {
979 cout << "Player " << p->name << " is trying to leave a game, but is not currently in a game." << endl;
980
981 /// should send a response back, maybe a new message type is needed
982 // not sure what to do here
983 }else {
984 cout << "Game name: " << g->getName() << endl;
985 p->currentGame = NULL;
986
987 serverMsg.type = MSG_TYPE_LEAVE_GAME;
988 memcpy(serverMsg.buffer, &p->id, 4);
989 strcpy(serverMsg.buffer+4, g->getName().c_str());
990 broadcastMessage(msgProcessor, serverMsg, g->getPlayers());
991
992 g->removePlayer(p->id);
993
994 int numPlayers = g->getNumPlayers();
995
996 serverMsg.type = MSG_TYPE_GAME_INFO;
997 memcpy(serverMsg.buffer, &numPlayers, 4);
998 strcpy(serverMsg.buffer+4, g->getName().c_str());
999 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
1000
1001 // if there are no more players in the game, remove it
1002 if (numPlayers == 0) {
1003 mapGames.erase(g->getName());
1004 delete g;
1005 }
1006 }
1007
1008 break;
1009 }
1010 case MSG_TYPE_JOIN_GAME_ACK:
1011 {
1012 cout << "Received a JOIN_GAME_ACK message" << endl;
1013
1014 string gameName(clientMsg.buffer);
1015 cout << "Game name: " << gameName << endl;
1016
1017 // check if this game already exists
1018 if (mapGames.find(gameName) == mapGames.end()) {
1019 serverMsg.type = MSG_TYPE_JOIN_GAME_FAILURE;
1020
1021 msgProcessor.sendMessage(&serverMsg, &from);
1022 }
1023
1024 Game* g = mapGames[gameName];
1025
1026 Player* p = findPlayerByAddr(mapPlayers, from);
1027 p->team = rand() % 2; // choose a random team (either 0 or 1)
1028 p->currentGame = g;
1029
1030 // tell the new player about all map objects
1031 // (currently just the flags)
1032
1033 serverMsg.type = MSG_TYPE_OBJECT;
1034 vector<WorldMap::Object>* vctObjects = g->getMap()->getObjects();
1035 vector<WorldMap::Object>::iterator itObjects;
1036 cout << "sending items" << endl;
1037 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
1038 itObjects->serialize(serverMsg.buffer);
1039 cout << "sending item id " << itObjects->id << endl;
1040 msgProcessor.sendMessage(&serverMsg, &from);
1041 }
1042
1043
1044 // send the current score
1045 serverMsg.type = MSG_TYPE_SCORE;
1046
1047 int game_blueScore = g->getBlueScore();
1048 int game_redScore = g->getRedScore();
1049 memcpy(serverMsg.buffer, &game_blueScore, 4);
1050 memcpy(serverMsg.buffer+4, &game_redScore, 4);
1051
1052 msgProcessor.sendMessage(&serverMsg, &from);
1053
1054 // send info to other players
1055 serverMsg.type = MSG_TYPE_PLAYER_JOIN_GAME;
1056 p->serialize(serverMsg.buffer);
1057 cout << "Should be broadcasting the message" << endl;
1058 broadcastMessage(msgProcessor, serverMsg, g->getPlayers());
1059
1060 g->addPlayer(p);
1061
1062
1063 // tell the new player about all the players in the game (including himself)
1064 cout << "Sending other players to new player" << endl;
1065 serverMsg.type = MSG_TYPE_PLAYER_JOIN_GAME;
1066
1067
1068 map<unsigned int, Player*>& allPlayers = g->getPlayers();
1069 map<unsigned int, Player*>::iterator it;
1070 for (it = allPlayers.begin(); it != allPlayers.end(); it++)
1071 {
1072 it->second->serialize(serverMsg.buffer);
1073
1074 cout << "sending info about " << it->second->name << endl;
1075 cout << "sending id " << it->second->id << endl;
1076 msgProcessor.sendMessage(&serverMsg, &from);
1077 }
1078
1079 int numPlayers = g->getNumPlayers();
1080
1081 serverMsg.type = MSG_TYPE_GAME_INFO;
1082 memcpy(serverMsg.buffer, &numPlayers, 4);
1083 strcpy(serverMsg.buffer+4, gameName.c_str());
1084 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
1085
1086 break;
1087 }
1088 default:
1089 {
1090 // probably want to log the error rather than sending a chat message,
1091 // especially since chat isn't currently visible on all screens
1092
1093 serverMsg.type = MSG_TYPE_CHAT;
1094 strcpy(serverMsg.buffer, "Server error occured. Report this please.");
1095
1096 break;
1097 }
1098 }
1099}
1100
1101void broadcastMessage(MessageProcessor &msgProcessor, NETWORK_MSG &serverMsg, map<unsigned int, Player*>& players) {
1102 map<unsigned int, Player*>::iterator it;
1103 for (it = players.begin(); it != players.end(); it++) {
1104 msgProcessor.sendMessage(&serverMsg, &(it->second->addr));
1105 }
1106}
1107
1108void updateUnusedPlayerId(unsigned int& id, map<unsigned int, Player*>& mapPlayers)
1109{
1110 while (mapPlayers.find(id) != mapPlayers.end())
1111 id++;
1112}
1113
1114Player *findPlayerByName(map<unsigned int, Player*> &m, string name)
1115{
1116 map<unsigned int, Player*>::iterator it;
1117
1118 for (it = m.begin(); it != m.end(); it++)
1119 {
1120 if ( it->second->name.compare(name) == 0 )
1121 return it->second;
1122 }
1123
1124 return NULL;
1125}
1126
1127Player *findPlayerByAddr(map<unsigned int, Player*> &m, const sockaddr_in &addr)
1128{
1129 map<unsigned int, Player*>::iterator it;
1130
1131 for (it = m.begin(); it != m.end(); it++)
1132 {
1133 if ( it->second->addr.sin_addr.s_addr == addr.sin_addr.s_addr &&
1134 it->second->addr.sin_port == addr.sin_port )
1135 return it->second;
1136 }
1137
1138 return NULL;
1139}
1140
1141void damagePlayer(Player *p, int damage) {
1142 p->health -= damage;
1143 if (p->health < 0)
1144 p->health = 0;
1145 if (p->health == 0) {
1146 cout << "Player died" << endl;
1147 p->isDead = true;
1148 p->timeDied = getCurrentMillis();
1149 }
1150}
1151
1152void addObjectToMap(WorldMap::ObjectType objectType, int x, int y, WorldMap* gameMap, map<unsigned int, Player*>& mapPlayers, MessageProcessor &msgProcessor) {
1153 NETWORK_MSG serverMsg;
1154
1155 gameMap->addObject(objectType, x, y);
1156
1157 serverMsg.type = MSG_TYPE_OBJECT;
1158 gameMap->getObjects()->back().serialize(serverMsg.buffer);
1159
1160 broadcastMessage(msgProcessor, serverMsg, mapPlayers);
1161}
Note: See TracBrowser for help on using the repository browser.