source: network-game/server/server.cpp@ 446dc65

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

A player can only turn the enemy flag in if their own flag is at their base. They can also return their own flag to base by walking over it, if it's on the ground.

  • Property mode set to 100644
File size: 23.3 KB
Line 
1#include <cstdlib>
2#include <cstdio>
3#include <unistd.h>
4#include <string>
5#include <iostream>
6#include <sstream>
7#include <cstring>
8#include <cmath>
9
10#include <vector>
11#include <map>
12
13#include <sys/time.h>
14
15#include <sys/socket.h>
16#include <netdb.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19
20#include <crypt.h>
21
22/*
23#include <openssl/bio.h>
24#include <openssl/ssl.h>
25#include <openssl/err.h>
26*/
27
28#include "../common/Compiler.h"
29#include "../common/Common.h"
30#include "../common/Message.h"
31#include "../common/WorldMap.h"
32#include "../common/Player.h"
33
34#include "DataAccess.h"
35
36using namespace std;
37
38// from used to be const. Removed that so I could take a reference
39// and use it to send messages
40bool processMessage(const NETWORK_MSG &clientMsg, struct sockaddr_in &from, map<unsigned int, Player>& mapPlayers, WorldMap* gameMap, unsigned int& unusedId, NETWORK_MSG &serverMsg, int sock, int &scoreBlue, int &scoreRed);
41
42void updateUnusedId(unsigned int& id, map<unsigned int, Player>& mapPlayers);
43
44// this should probably go somewhere in the common folder
45void error(const char *msg)
46{
47 perror(msg);
48 exit(0);
49}
50
51Player *findPlayerByName(map<unsigned int, Player> &m, string name)
52{
53 map<unsigned int, Player>::iterator it;
54
55 for (it = m.begin(); it != m.end(); it++)
56 {
57 if ( it->second.name.compare(name) == 0 )
58 return &(it->second);
59 }
60
61 return NULL;
62}
63
64Player *findPlayerByAddr(map<unsigned int, Player> &m, const sockaddr_in &addr)
65{
66 map<unsigned int, Player>::iterator it;
67
68 for (it = m.begin(); it != m.end(); it++)
69 {
70 if ( it->second.addr.sin_addr.s_addr == addr.sin_addr.s_addr &&
71 it->second.addr.sin_port == addr.sin_port )
72 return &(it->second);
73 }
74
75 return NULL;
76}
77
78int main(int argc, char *argv[])
79{
80 int sock, length, n;
81 struct sockaddr_in server;
82 struct sockaddr_in from; // info of client sending the message
83 NETWORK_MSG clientMsg, serverMsg;
84 map<unsigned int, Player> mapPlayers;
85 unsigned int unusedId = 1;
86 int scoreBlue, scoreRed;
87
88 scoreBlue = 0;
89 scoreRed = 0;
90
91 //SSL_load_error_strings();
92 //ERR_load_BIO_strings();
93 //OpenSSL_add_all_algorithms();
94
95 if (argc < 2) {
96 cerr << "ERROR, no port provided" << endl;
97 exit(1);
98 }
99
100 WorldMap* gameMap = WorldMap::loadMapFromFile("../data/map.txt");
101
102 // add some items to the map. They will be sent out
103 // to players when they login
104 for (int y=0; y<gameMap->height; y++) {
105 for (int x=0; x<gameMap->width; x++) {
106 switch (gameMap->getStructure(x, y)) {
107 case WorldMap::STRUCTURE_BLUE_FLAG:
108 gameMap->addObject(WorldMap::OBJECT_BLUE_FLAG, x*25+12, y*25+12);
109 break;
110 case WorldMap::STRUCTURE_RED_FLAG:
111 gameMap->addObject(WorldMap::OBJECT_RED_FLAG, x*25+12, y*25+12);
112 break;
113 }
114 }
115 }
116
117 sock = socket(AF_INET, SOCK_DGRAM, 0);
118 if (sock < 0) 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 bool broadcastResponse;
130 timespec ts;
131 int timeLastUpdated = 0, curTime = 0, timeLastBroadcast = 0;
132 while (true) {
133
134 usleep(5000);
135
136 clock_gettime(CLOCK_REALTIME, &ts);
137 // make the number smaller so millis can fit in an int
138 ts.tv_sec -= 1368000000;
139 curTime = ts.tv_sec*1000 + ts.tv_nsec/1000000;
140
141 if (timeLastUpdated == 0 || (curTime-timeLastUpdated) >= 50) {
142 timeLastUpdated = curTime;
143
144 // maybe put this in a separate method
145 map<unsigned int, Player>::iterator it;
146 FLOAT_POSITION oldPos;
147 bool broadcastMove = false;
148 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++) {
149 oldPos = it->second.pos;
150 if (it->second.move(gameMap)) {
151
152 // check if the move needs to be canceled
153 switch(gameMap->getElement(it->second.pos.x/25, it->second.pos.y/25)) {
154 case WorldMap::TERRAIN_NONE:
155 case WorldMap::TERRAIN_OCEAN:
156 case WorldMap::TERRAIN_ROCK:
157 {
158 it->second.pos = oldPos;
159 it->second.target.x = it->second.pos.x;
160 it->second.target.y = it->second.pos.y;
161 broadcastMove = true;
162 break;
163 }
164 default:
165 // if there are no obstacles, do nothing
166 break;
167 }
168
169 WorldMap::ObjectType flagType;
170 POSITION pos;
171 bool flagTurnedIn = false;
172 bool flagReturned = false;
173 bool ownFlagAtBase = false;
174
175 switch(gameMap->getStructure(it->second.pos.x/25, it->second.pos.y/25)) {
176 case WorldMap::STRUCTURE_BLUE_FLAG:
177 {
178 if (it->second.team == 0 && it->second.hasRedFlag)
179 {
180 // check that your flag is at your base
181 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
182
183 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
184 vector<WorldMap::Object>::iterator itObjects;
185
186 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
187 if (itObjects->type == WorldMap::OBJECT_BLUE_FLAG) {
188 if (itObjects->pos.x == pos.x*25+12 && itObjects->pos.y == pos.y*25+12) {
189 ownFlagAtBase = true;
190 break;
191 }
192 }
193 }
194
195 if (ownFlagAtBase) {
196 it->second.hasRedFlag = false;
197 flagType = WorldMap::OBJECT_RED_FLAG;
198 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
199 flagTurnedIn = true;
200 scoreBlue++;
201 }
202 }
203
204 break;
205 }
206 case WorldMap::STRUCTURE_RED_FLAG:
207 {
208 if (it->second.team == 1 && it->second.hasBlueFlag)
209 {
210 // check that your flag is at your base
211 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
212
213 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
214 vector<WorldMap::Object>::iterator itObjects;
215
216 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
217 if (itObjects->type == WorldMap::OBJECT_RED_FLAG) {
218 if (itObjects->pos.x == pos.x*25+12 && itObjects->pos.y == pos.y*25+12) {
219 ownFlagAtBase = true;
220 break;
221 }
222 }
223 }
224
225 if (ownFlagAtBase) {
226 it->second.hasBlueFlag = false;
227 flagType = WorldMap::OBJECT_BLUE_FLAG;
228 pos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
229 flagTurnedIn = true;
230 scoreRed++;
231 }
232 }
233
234 break;
235 }
236 }
237
238 if (flagTurnedIn) {
239 // send an OBJECT message to add the flag back to its spawn point
240 pos.x = pos.x*25+12;
241 pos.y = pos.y*25+12;
242 gameMap->addObject(flagType, pos.x, pos.y);
243
244 serverMsg.type = MSG_TYPE_OBJECT;
245 gameMap->getObjects()->back().serialize(serverMsg.buffer);
246
247 map<unsigned int, Player>::iterator it2;
248 for (it2 = mapPlayers.begin(); it2 != mapPlayers.end(); it2++)
249 {
250 if ( sendMessage(&serverMsg, sock, &(it2->second.addr)) < 0 )
251 error("sendMessage");
252 }
253
254 serverMsg.type = MSG_TYPE_SCORE;
255 memcpy(serverMsg.buffer, &scoreBlue, 4);
256 memcpy(serverMsg.buffer+4, &scoreRed, 4);
257
258 for (it2 = mapPlayers.begin(); it2 != mapPlayers.end(); it2++)
259 {
260 if ( sendMessage(&serverMsg, sock, &(it2->second.addr)) < 0 )
261 error("sendMessage");
262 }
263
264 // this means a PLAYER message will be sent
265 broadcastMove = true;
266 }
267
268 // go through all objects and check if the player is close to one and if its their flag
269 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
270 vector<WorldMap::Object>::iterator itObjects;
271 POSITION structPos;
272
273 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
274 POSITION pos = itObjects->pos;
275
276 if (posDistance(it->second.pos, pos.toFloat()) < 10) {
277 if (it->second.team == 0 &&
278 itObjects->type == WorldMap::OBJECT_BLUE_FLAG) {
279 structPos = gameMap->getStructureLocation(WorldMap::STRUCTURE_BLUE_FLAG);
280 flagReturned = true;
281 break;
282 } else if (it->second.team == 1 &&
283 itObjects->type == WorldMap::OBJECT_RED_FLAG) {
284 structPos = gameMap->getStructureLocation(WorldMap::STRUCTURE_RED_FLAG);
285 flagReturned = true;
286 break;
287 }
288 }
289 }
290
291 if (flagReturned) {
292 itObjects->pos.x = structPos.x*25+12;
293 itObjects->pos.y = structPos.y*25+12;
294
295 serverMsg.type = MSG_TYPE_OBJECT;
296 itObjects->serialize(serverMsg.buffer);
297
298 map<unsigned int, Player>::iterator it2;
299 for (it2 = mapPlayers.begin(); it2 != mapPlayers.end(); it2++)
300 {
301 if ( sendMessage(&serverMsg, sock, &(it2->second.addr)) < 0 )
302 error("sendMessage");
303 }
304 }
305
306 if (broadcastMove) {
307 serverMsg.type = MSG_TYPE_PLAYER;
308 it->second.serialize(serverMsg.buffer);
309
310 cout << "about to broadcast move" << endl;
311 map<unsigned int, Player>::iterator it2;
312 for (it2 = mapPlayers.begin(); it2 != mapPlayers.end(); it2++)
313 {
314 if ( sendMessage(&serverMsg, sock, &(it2->second.addr)) < 0 )
315 error("sendMessage");
316 }
317 }
318 }
319 }
320 }
321
322 n = receiveMessage(&clientMsg, sock, &from);
323
324 if (n >= 0) {
325 broadcastResponse = processMessage(clientMsg, from, mapPlayers, gameMap, unusedId, serverMsg, sock, scoreBlue, scoreRed);
326
327 // probably replace this with a function that prints based on the
328 // message type
329 cout << "msg: " << serverMsg.buffer << endl;
330 if (broadcastResponse)
331 {
332 cout << "Should be broadcasting the message" << endl;
333
334 map<unsigned int, Player>::iterator it;
335 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
336 {
337 cout << "Sent message back to " << it->second.name << endl;
338 if ( sendMessage(&serverMsg, sock, &(it->second.addr)) < 0 )
339 error("sendMessage");
340 }
341 }
342 else
343 {
344 cout << "Should be sending back the message" << endl;
345
346 if ( sendMessage(&serverMsg, sock, &from) < 0 )
347 error("sendMessage");
348 }
349 }
350 }
351
352 return 0;
353}
354
355bool processMessage(const NETWORK_MSG& clientMsg, struct sockaddr_in& from, map<unsigned int, Player>& mapPlayers, WorldMap* gameMap, unsigned int& unusedId, NETWORK_MSG& serverMsg, int sock, int &scoreBlue, int &scoreRed)
356{
357 DataAccess da;
358
359 cout << "Received message" << endl;
360 cout << "MSG: type: " << clientMsg.type << endl;
361 cout << "MSG contents: " << clientMsg.buffer << endl;
362
363 // maybe we should make a message class and have this be a member
364 bool broadcastResponse = false;
365
366 // Check that if an invalid message is sent, the client will correctly
367 // receive and display the response. Maybe make a special error msg type
368 switch(clientMsg.type)
369 {
370 case MSG_TYPE_REGISTER:
371 {
372 string username(clientMsg.buffer);
373 string password(strchr(clientMsg.buffer, '\0')+1);
374
375 cout << "username: " << username << endl;
376 cout << "password: " << password << endl;
377
378 int error = da.insertPlayer(username, password);
379
380 if (!error)
381 strcpy(serverMsg.buffer, "Registration successful.");
382 else
383 strcpy(serverMsg.buffer, "Registration failed. Please try again.");
384
385 serverMsg.type = MSG_TYPE_REGISTER;
386
387 break;
388 }
389 case MSG_TYPE_LOGIN:
390 {
391 cout << "Got login message" << endl;
392
393 serverMsg.type = MSG_TYPE_LOGIN;
394
395 string username(clientMsg.buffer);
396 string password(strchr(clientMsg.buffer, '\0')+1);
397
398 Player* p = da.getPlayer(username);
399
400 if (p == NULL || !da.verifyPassword(password, p->password))
401 {
402 strcpy(serverMsg.buffer, "Incorrect username or password");
403 }
404 else if(findPlayerByName(mapPlayers, username) != NULL)
405 {
406 strcpy(serverMsg.buffer, "Player has already logged in.");
407 }
408 else
409 {
410 serverMsg.type = MSG_TYPE_PLAYER;
411
412 updateUnusedId(unusedId, mapPlayers);
413 p->id = unusedId;
414 cout << "new player id: " << p->id << endl;
415 p->setAddr(from);
416
417 // choose a random team (either 0 or 1)
418 p->team = rand() % 2;
419
420 // tell the new player about all the existing players
421 cout << "Sending other players to new player" << endl;
422
423 map<unsigned int, Player>::iterator it;
424 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
425 {
426 it->second.serialize(serverMsg.buffer);
427
428 cout << "sending info about " << it->second.name << endl;
429 cout << "sending id " << it->second.id << endl;
430 if ( sendMessage(&serverMsg, sock, &from) < 0 )
431 error("sendMessage");
432 }
433
434 // tell the new player about all map objects
435 // (currently just the flags)
436 serverMsg.type = MSG_TYPE_OBJECT;
437 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
438 vector<WorldMap::Object>::iterator itObjects;
439 cout << "sending items" << endl;
440 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end(); itObjects++) {
441 itObjects->serialize(serverMsg.buffer);
442 cout << "sending item id " << itObjects->id << endl;
443 if ( sendMessage(&serverMsg, sock, &from) < 0 )
444 error("sendMessage");
445 }
446
447 // send the current score
448 serverMsg.type = MSG_TYPE_SCORE;
449 memcpy(serverMsg.buffer, &scoreBlue, 4);
450 memcpy(serverMsg.buffer+4, &scoreRed, 4);
451 if ( sendMessage(&serverMsg, sock, &from) < 0 )
452 error("sendMessage");
453
454 serverMsg.type = MSG_TYPE_PLAYER;
455 p->serialize(serverMsg.buffer);
456 cout << "Should be broadcasting the message" << endl;
457
458 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
459 {
460 cout << "Sent message back to " << it->second.name << endl;
461 if ( sendMessage(&serverMsg, sock, &(it->second.addr)) < 0 )
462 error("sendMessage");
463 }
464
465 serverMsg.type = MSG_TYPE_LOGIN;
466 mapPlayers[unusedId] = *p;
467 }
468
469 delete(p);
470
471 break;
472 }
473 case MSG_TYPE_LOGOUT:
474 {
475 string name(clientMsg.buffer);
476 cout << "Player logging out: " << name << endl;
477
478 Player *p = findPlayerByName(mapPlayers, name);
479
480 if (p == NULL)
481 {
482 strcpy(serverMsg.buffer, "That player is not logged in. This is either a bug, or you're trying to hack the server.");
483 cout << "Player not logged in" << endl;
484 }
485 else if ( p->addr.sin_addr.s_addr != from.sin_addr.s_addr ||
486 p->addr.sin_port != from.sin_port )
487 {
488 strcpy(serverMsg.buffer, "That player is logged in using a differemt connection. This is either a bug, or you're trying to hack the server.");
489 cout << "Player logged in using a different connection" << endl;
490 }
491 else
492 {
493 if (p->id < unusedId)
494 unusedId = p->id;
495 mapPlayers.erase(p->id);
496 strcpy(serverMsg.buffer, "You have successfully logged out.");
497 }
498
499 serverMsg.type = MSG_TYPE_LOGOUT;
500
501 break;
502 }
503 case MSG_TYPE_CHAT:
504 {
505 cout << "Got a chat message" << endl;
506
507 Player *p = findPlayerByAddr(mapPlayers, from);
508
509 if (p == NULL)
510 {
511 strcpy(serverMsg.buffer, "No player is logged in using this connection. This is either a bug, or you're trying to hack the server.");
512 }
513 else
514 {
515 broadcastResponse = true;
516
517 ostringstream oss;
518 oss << p->name << ": " << clientMsg.buffer;
519
520 strcpy(serverMsg.buffer, oss.str().c_str());
521 }
522
523 serverMsg.type = MSG_TYPE_CHAT;
524
525 break;
526 }
527 case MSG_TYPE_PLAYER_MOVE:
528 {
529 cout << "PLAYER_MOVE" << endl;
530
531 int id, x, y;
532
533 memcpy(&id, clientMsg.buffer, 4);
534 memcpy(&x, clientMsg.buffer+4, 4);
535 memcpy(&y, clientMsg.buffer+8, 4);
536
537 cout << "x: " << x << endl;
538 cout << "y: " << y << endl;
539 cout << "id: " << id << endl;
540
541 if ( mapPlayers[id].addr.sin_addr.s_addr == from.sin_addr.s_addr &&
542 mapPlayers[id].addr.sin_port == from.sin_port )
543 {
544 // we need to make sure the player can move here
545 if (0 <= x && x < 300 && 0 <= y && y < 300 &&
546 gameMap->getElement(x/25, y/25) == WorldMap::TERRAIN_GRASS)
547 {
548 cout << "valid terrain" << endl;
549
550 mapPlayers[id].target.x = x;
551 mapPlayers[id].target.y = y;
552
553 serverMsg.type = MSG_TYPE_PLAYER_MOVE;
554
555 memcpy(serverMsg.buffer, &id, 4);
556 memcpy(serverMsg.buffer+4, &mapPlayers[id].target.x, 4);
557 memcpy(serverMsg.buffer+8, &mapPlayers[id].target.y, 4);
558
559 broadcastResponse = true;
560 }
561 else
562 cout << "Bad terrain detected" << endl;
563 }
564 else // nned to send back a message indicating failure
565 cout << "Player id (" << id << ") doesn't match sender" << endl;
566
567 break;
568 }
569 case MSG_TYPE_PICKUP_FLAG:
570 {
571 // may want to check the id matches the sender, just like for PLAYER_NOVE
572 cout << "PICKUP_FLAG" << endl;
573
574 int id;
575
576 memcpy(&id, clientMsg.buffer, 4);
577 cout << "id: " << id << endl;
578
579 vector<WorldMap::Object>* vctObjects = gameMap->getObjects();
580 vector<WorldMap::Object>::iterator itObjects;
581
582 for (itObjects = vctObjects->begin(); itObjects != vctObjects->end();) {
583 POSITION pos = itObjects->pos;
584 bool gotFlag = false;
585
586 if (posDistance(mapPlayers[id].pos, pos.toFloat()) < 10) {
587 switch (itObjects->type) {
588 case WorldMap::OBJECT_BLUE_FLAG:
589 if (mapPlayers[id].team == 1) {
590 gotFlag = true;
591 mapPlayers[id].hasBlueFlag = true;
592 broadcastResponse = true;
593 }
594 break;
595 case WorldMap::OBJECT_RED_FLAG:
596 if (mapPlayers[id].team == 0) {
597 gotFlag = true;
598 mapPlayers[id].hasRedFlag = true;
599 broadcastResponse = true;
600 }
601 break;
602 }
603
604 if (gotFlag) {
605 serverMsg.type = MSG_TYPE_REMOVE_OBJECT;
606 memcpy(serverMsg.buffer, &itObjects->id, 4);
607
608 map<unsigned int, Player>::iterator it;
609 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
610 {
611 if ( sendMessage(&serverMsg, sock, &(it->second.addr)) < 0 )
612 error("sendMessage");
613 }
614
615 // remove the object from the server-side map
616 cout << "size before: " << gameMap->getObjects()->size() << endl;
617 itObjects = vctObjects->erase(itObjects);
618 cout << "size after: " << gameMap->getObjects()->size() << endl;
619 }
620 }
621
622 if (!gotFlag)
623 itObjects++;
624 }
625
626 serverMsg.type = MSG_TYPE_PLAYER;
627 mapPlayers[id].serialize(serverMsg.buffer);
628
629 break;
630 }
631 case MSG_TYPE_DROP_FLAG:
632 {
633 // may want to check the id matches the sender, just like for PLAYER_NOVE
634 cout << "DROP_FLAG" << endl;
635
636 int id;
637
638 memcpy(&id, clientMsg.buffer, 4);
639 cout << "id: " << id << endl;
640
641 WorldMap::ObjectType flagType = WorldMap::OBJECT_NONE;
642 if (mapPlayers[id].hasBlueFlag)
643 flagType = WorldMap::OBJECT_BLUE_FLAG;
644 else if (mapPlayers[id].hasRedFlag)
645 flagType = WorldMap::OBJECT_RED_FLAG;
646
647 gameMap->addObject(flagType, mapPlayers[id].pos.x, mapPlayers[id].pos.y);
648
649 // need to send the OBJECT message too
650 serverMsg.type = MSG_TYPE_OBJECT;
651 gameMap->getObjects()->back().serialize(serverMsg.buffer);
652
653 map<unsigned int, Player>::iterator it;
654 for (it = mapPlayers.begin(); it != mapPlayers.end(); it++)
655 {
656 if ( sendMessage(&serverMsg, sock, &(it->second.addr)) < 0 )
657 error("sendMessage");
658 }
659
660 mapPlayers[id].hasBlueFlag = false;
661 mapPlayers[id].hasRedFlag = false;
662
663 serverMsg.type = MSG_TYPE_PLAYER;
664 mapPlayers[id].serialize(serverMsg.buffer);
665
666 map<unsigned int, Player>::iterator it2;
667 broadcastResponse = true;
668
669 break;
670 }
671 default:
672 {
673 strcpy(serverMsg.buffer, "Server error occured. Report this please.");
674
675 serverMsg.type = MSG_TYPE_CHAT;
676
677 break;
678 }
679 }
680
681 return broadcastResponse;
682}
683
684void updateUnusedId(unsigned int& id, map<unsigned int, Player>& mapPlayers)
685{
686 while (mapPlayers.find(id) != mapPlayers.end())
687 id++;
688}
Note: See TracBrowser for help on using the repository browser.