game_server.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #define _GNU_SOURCE
  2. #include "game_server.h"
  3. Player *player_list = NULL;
  4. Client *client_list = NULL;
  5. short port = -1;
  6. char *board = NULL;
  7. int main(int argc, char* argv[]) {
  8. struct client *p;
  9. if (argc > 1)
  10. port = (short)(atoi(argv[1]));
  11. else
  12. port = PORT;
  13. //get listen file descriptor
  14. int listenfd = setup();
  15. //TODO install timer signal handler
  16. signal(SIGALRM,timer_handler);
  17. //start timer
  18. alarm(TIMER_TICK);
  19. //TODO: implement select()
  20. while (1) {
  21. fd_set fdlist;
  22. int maxfd = listenfd;
  23. FD_ZERO(&fdlist);
  24. FD_SET(listenfd, &fdlist);
  25. for(p = client_list; p; p = p->next){
  26. FD_SET(p->fd, &fdlist);
  27. if(p->fd > maxfd)
  28. maxfd = p->fd;
  29. }
  30. if(select(maxfd+1, &fdlist,NULL,NULL,NULL)<0){
  31. perror("select");
  32. }else{
  33. for(p=client_list; p; p=p->next)
  34. if(FD_ISSET(p->fd,&fdlist))
  35. break;
  36. if(p)
  37. receiveclient(p);
  38. if(FD_ISSET(listenfd, &fdlist))
  39. new_connection(listenfd);
  40. }
  41. }
  42. return 0;
  43. }
  44. /*
  45. * Sets up server socket: bind and listen
  46. * Returns a file descriptor for listening and accepting new connections
  47. */
  48. int setup (void) {
  49. int on = 1, status;
  50. struct sockaddr_in self;
  51. int listenfd;
  52. if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  53. perror("socket");
  54. exit(1);
  55. }
  56. status = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
  57. (const char *) &on, sizeof(on));
  58. if(status == -1) {
  59. perror("setsockopt -- REUSEADDR");
  60. }
  61. memset(&self, '\0', sizeof(self));
  62. self.sin_family = AF_INET;
  63. self.sin_addr.s_addr = INADDR_ANY;
  64. self.sin_port = htons(port);
  65. printf("Listening on %d\n", port);
  66. if (bind(listenfd, (struct sockaddr *)&self, sizeof(self)) == -1) {
  67. perror("bind"); // probably means port is in use
  68. exit(1);
  69. }
  70. if (listen(listenfd, 5) == -1) {
  71. perror("listen");
  72. exit(1);
  73. }
  74. return listenfd;
  75. }
  76. /* timer signal handler */
  77. void timer_handler(int sig) {
  78. //broadcast current game end to all connected clients
  79. Client *curr;
  80. for (curr = client_list; curr!=NULL; curr = curr->next) {
  81. sendclient(curr, "Game over\r\n");
  82. }
  83. //TODO - generate new game board
  84. board = boardGenerator();
  85. //reset the timer so we get called again in 120 seconds
  86. alarm(TIMER_TICK);
  87. }
  88. /* accepts new connection and adds a client-specific fd */
  89. void new_connection (int listenfd) {
  90. int fd;
  91. struct sockaddr_in r;
  92. socklen_t socklen = sizeof(r);
  93. if ((fd = accept(listenfd, (struct sockaddr *)&r, &socklen)) < 0) {
  94. perror("accept");
  95. return;
  96. }
  97. printf("connection from %s\n", inet_ntoa(r.sin_addr));
  98. add_client(fd, r.sin_addr);
  99. }
  100. /* creates a new client struct and
  101. TODO - adds it to the list of clients */
  102. void add_client(int fd, struct in_addr addr){
  103. struct client *p = malloc(sizeof(struct client));
  104. if (!p) {
  105. perror("malloc failure");
  106. exit(1);
  107. }
  108. printf("Adding client %s\n", inet_ntoa(addr));
  109. fflush(stdout);
  110. p->fd = fd;
  111. p->state = NAME; //needs yet to identify new client by name
  112. p->inbuf = 0;
  113. //TODO - add it to the list of clients
  114. p->next = client_list;
  115. client_list = p;
  116. sendclient(p, "What is your player name?\n");
  117. }
  118. void receiveclient(struct client *p) {
  119. char *after = p->buf + p->inbuf;
  120. int room = BUFFER_SIZE - p->inbuf;
  121. int nbytes;
  122. if ((nbytes = read(p->fd, after, room)) > 0) {
  123. p->inbuf += nbytes;
  124. int where = find_network_newline(p->buf, p->inbuf);
  125. if (where >= 0) {
  126. p->buf[where] = '\0';
  127. p->buf[where+1] = '\0';
  128. interpret_message(p);
  129. where+=2; // skip over \r\n
  130. p->inbuf -= where;
  131. memmove(p->buf, p->buf + where, p->inbuf);
  132. }
  133. room = sizeof(p->buf) - p->inbuf;
  134. after = p->buf + p->inbuf;
  135. } else {
  136. remove_client(p->fd);
  137. }
  138. }
  139. /*
  140. * Acts according to the client state
  141. */
  142. void interpret_message(Client *p) {
  143. char *buf;
  144. if (p->state == NAME) {
  145. strcpy(p->name, p->buf);
  146. switch (create_player(p->name, &player_list)) {
  147. case 1:
  148. sendclient(p, "Welcome back.\r\n");
  149. break;
  150. case 2:
  151. asprintf(&buf, "Username too long, truncated to %d chars.\r\n", (MAX_NAME-1));
  152. sendclient(p, buf);
  153. break;
  154. case 0:
  155. sendclient(p, "Welcome, new player\r\n");
  156. break;
  157. }
  158. printf("Added %s to the client list\n",p->name);
  159. p->state = COMMAND;
  160. sendclient(p, "Go ahead and enter player commands>\r\n");
  161. } else if (!strcmp(p->buf, "q")) {
  162. remove_client(p->fd);
  163. } else {
  164. parse_line(p);
  165. }
  166. }
  167. int parse_line(Client *p) {
  168. char * input = p->buf;
  169. // cmd_argv will held arguments to individual commands passed to sub-procedure
  170. char *cmd_argv[INPUT_ARG_MAX_NUM];
  171. int cmd_argc;
  172. // tokenize arguments
  173. char *next_token = strtok(input, DELIM);
  174. cmd_argc = 0;
  175. while (next_token != NULL) {
  176. cmd_argv[cmd_argc] = next_token;
  177. cmd_argc++;
  178. next_token = strtok(NULL, DELIM);
  179. }
  180. return (cmd_argc > 0 && do_command(p, cmd_argc, cmd_argv));
  181. }
  182. /*
  183. * Process player commands
  184. * Return: -1 for quit command
  185. * 0 otherwise
  186. */
  187. int do_command(struct client * p, int cmd_argc, char **cmd_argv) {
  188. if (cmd_argc <= 0) {
  189. return 0;
  190. } else if (strcmp(cmd_argv[0], "q") == 0 && cmd_argc == 1) {
  191. sendclient(p,"Thanks for playing! I hope to see you soon.\r\n");
  192. return -1;
  193. } else if (strcmp(cmd_argv[0], "all_players") == 0 && cmd_argc == 1) {
  194. //TODO produce list of all players and their stats in nice text format and sendclient
  195. Player *temp = player_list;
  196. while(temp!=NULL){
  197. char string [BUFFER_SIZE-10];
  198. sprintf(string, "Name:%s\tMax Score:%d\tTotal Games:%d\tTotal Score:%d\n", temp->name, temp->max_score, temp->total_games, temp->total_score);
  199. sendclient(p,string);
  200. temp = temp->next;
  201. }
  202. } else if (strcmp(cmd_argv[0], "top_3") == 0 && cmd_argc == 1) {
  203. //TODO produce list of top 3 players with their total score and sentclient as a text
  204. Player* curr = player_list;
  205. int all_scores[10] = {-1};
  206. int high_score_indices[3] = {-1};
  207. int counter = 0, curr_max = -1, curr_max_index, curr_print = 1;
  208. while(curr->next!=NULL){
  209. all_scores[counter] = curr->max_score;
  210. counter++;
  211. curr = curr->next;
  212. }
  213. all_scores[counter] = curr->max_score;
  214. counter++;
  215. for(int j = 0; j < counter; j++){
  216. for(int i =0;i<10;i++){
  217. if(all_scores[i] > curr_max){
  218. curr_max = all_scores[i];
  219. curr_max_index = i;
  220. }
  221. }
  222. high_score_indices[j] = curr_max_index;
  223. all_scores[curr_max_index] = -1;
  224. curr_max = -1;
  225. curr_max_index = -1;
  226. }
  227. curr = player_list;
  228. for(int k = 0; k < counter; k++){
  229. if(high_score_indices[k] == -1){
  230. break;
  231. }
  232. for(int h = 0; h < high_score_indices[k]; h++){
  233. curr = curr->next;
  234. }
  235. char str [1023];
  236. sprintf(str, "%d.%s\t%d\n", curr_print, curr->name, curr->max_score);
  237. sendclient(p, str);
  238. curr_print++;
  239. curr = player_list;
  240. }
  241. } else if (strcmp(cmd_argv[0], "add_score") == 0 && cmd_argc == 2) {
  242. char str [BUFFER_SIZE - 10];
  243. int score =atoi(cmd_argv[1]);
  244. Player *player = find_player(p->name, player_list);
  245. player->total_score+=score;
  246. if (score > player->max_score)
  247. player->max_score = score;
  248. sprintf (str, "added score %d for player %s\r\n", score, p->name);
  249. sendclient(p, str);
  250. } else if (strcmp(cmd_argv[0], "new_game") == 0 && cmd_argc == 1) {
  251. //TODO -- transmit current board to be presented as a 2D array of chars
  252. Player *temp = find_player(p->name, player_list);
  253. temp->total_games++;
  254. int counter = 0;
  255. board = boardGenerator();
  256. char new_board[4][8];
  257. for(int i = 0; i<4; i++){
  258. for(int j = 0; j < 8; j++){
  259. if(j==0||j==2||j==4||j==6){
  260. new_board[i][j] = board[counter];
  261. counter++;
  262. }else if(j==1||j==3||j==5){
  263. new_board[i][j] = ' ';
  264. }else{
  265. new_board[i][j] = '\n';
  266. }
  267. }
  268. }
  269. sendclient(p, new_board[0]);
  270. }else {
  271. sendclient(p, "Incorrect syntax\r\n");
  272. }
  273. return 0;
  274. }
  275. void sendclient(struct client *p, char *msg) {
  276. write(p->fd, msg, strlen(msg));
  277. }
  278. /*
  279. * Search the first inbuf characters of buf for a network newline.
  280. * Return the location of the '\r' if the network newline is found, or -1 otherwise.
  281. */
  282. int find_network_newline(char *buf, int inbuf) {
  283. int i;
  284. for (i = 0; i < inbuf - 1; i++)
  285. if ((buf[i] == '\r') && (buf[i + 1] == '\n'))
  286. return i;
  287. return -1;
  288. }
  289. void remove_client(int fd){
  290. struct client **p;
  291. for(p = &client_list; *p && (*p)->fd != fd; p = &(*p)->next)
  292. ;
  293. if(*p){
  294. struct client *t = (*p)->next;
  295. fprintf(stderr, "Removing client %s\n", (*p)->name);
  296. fflush(stdout);
  297. free(*p);
  298. if(close(fd) != 0){
  299. perror("closing fd on exiting client");
  300. exit(1);
  301. }
  302. *p = t;
  303. }else{
  304. fprintf(stderr, "Trying to remove fd %d, but I don't know about it\n", fd);
  305. fflush(stderr);
  306. }
  307. }