| 1 /* |
|
| 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
| 3 * |
|
| 4 * Copyright 2016 Mike Becker. All rights reserved. |
|
| 5 * |
|
| 6 * Redistribution and use in source and binary forms, with or without |
|
| 7 * modification, are permitted provided that the following conditions are met: |
|
| 8 * |
|
| 9 * 1. Redistributions of source code must retain the above copyright |
|
| 10 * notice, this list of conditions and the following disclaimer. |
|
| 11 * |
|
| 12 * 2. Redistributions in binary form must reproduce the above copyright |
|
| 13 * notice, this list of conditions and the following disclaimer in the |
|
| 14 * documentation and/or other materials provided with the distribution. |
|
| 15 * |
|
| 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
| 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
| 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
| 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
| 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
| 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
| 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
| 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
| 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
| 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
| 26 * POSSIBILITY OF SUCH DAMAGE. |
|
| 27 * |
|
| 28 */ |
|
| 29 |
|
| 30 #include "network.h" |
|
| 31 #include "game.h" |
|
| 32 #include "chess/pgn.h" |
|
| 33 #include <ncurses.h> |
|
| 34 #include <errno.h> |
|
| 35 #include <string.h> |
|
| 36 #include <stdlib.h> |
|
| 37 #include <unistd.h> |
|
| 38 #include <signal.h> |
|
| 39 |
|
| 40 static int server_fd = -1; |
|
| 41 static void interrupt_listen(int sig) { |
|
| 42 if (server_fd > -1) { |
|
| 43 // this interrupts |
|
| 44 close(server_fd); |
|
| 45 } |
|
| 46 } |
|
| 47 |
|
| 48 static int server_open(Server *server, Settings *settings) { |
|
| 49 printw("\nListening for client...\n"); |
|
| 50 refresh(); |
|
| 51 if (settings->usedomainsocket |
|
| 52 ? net_create_sock(server, settings->serverhost) |
|
| 53 : net_create_tcp(server, settings->port)) { |
|
| 54 addstr("Server creation failed"); |
|
| 55 return 1; |
|
| 56 } |
|
| 57 |
|
| 58 // allow Ctrl+C to interrupt the listening process |
|
| 59 server_fd = server->fd; |
|
| 60 signal(SIGINT, interrupt_listen); |
|
| 61 |
|
| 62 if (net_listen(server)) { |
|
| 63 addstr("Listening for client failed or interrupted"); |
|
| 64 return 1; |
|
| 65 } |
|
| 66 |
|
| 67 // restore default action |
|
| 68 signal(SIGINT, SIG_DFL); |
|
| 69 |
|
| 70 return 0; |
|
| 71 } |
|
| 72 |
|
| 73 static int server_handshake(Client *client) { |
|
| 74 net_send_code(client->fd, NETCODE_VERSION); |
|
| 75 if (net_recieve_code(client->fd) != NETCODE_VERSION) { |
|
| 76 addstr("Client uses an incompatible software version."); |
|
| 77 return 1; |
|
| 78 } |
|
| 79 |
|
| 80 addstr("Client connected - transmitting gameinfo..."); |
|
| 81 refresh(); |
|
| 82 |
|
| 83 return 0; |
|
| 84 } |
|
| 85 |
|
| 86 int server_run(Settings *settings) { |
|
| 87 Server server; |
|
| 88 |
|
| 89 dump_gameinfo(&(settings->gameinfo)); |
|
| 90 GameState gamestate; |
|
| 91 gamestate_init(&gamestate); |
|
| 92 if (settings->continuepgn) { |
|
| 93 /* preload PGN data before handshake */ |
|
| 94 FILE *pgnfile = fopen(settings->continuepgn, "r"); |
|
| 95 if (pgnfile) { |
|
| 96 int result = read_pgn(pgnfile, &gamestate, |
|
| 97 &(settings->gameinfo)); |
|
| 98 long position = ftell(pgnfile); |
|
| 99 fclose(pgnfile); |
|
| 100 if (result) { |
|
| 101 printw("Invalid PGN file content at position %ld:\n%s\n", |
|
| 102 position, pgn_error_str(result)); |
|
| 103 return 1; |
|
| 104 } |
|
| 105 if (!is_game_running(&gamestate)) { |
|
| 106 addstr("Game has ended. Use -s to analyze it locally.\n"); |
|
| 107 return 1; |
|
| 108 } |
|
| 109 addch('\n'); |
|
| 110 dump_moveinfo(&gamestate); |
|
| 111 addch('\n'); |
|
| 112 } else { |
|
| 113 printw("Can't read PGN file (%s)\n", strerror(errno)); |
|
| 114 return 1; |
|
| 115 } |
|
| 116 } |
|
| 117 |
|
| 118 if (server_open(&server, settings)) { |
|
| 119 net_destroy(&server); |
|
| 120 return 1; |
|
| 121 } |
|
| 122 |
|
| 123 if (server_handshake(server.client)) { |
|
| 124 net_destroy(&server); |
|
| 125 return 1; |
|
| 126 } |
|
| 127 |
|
| 128 int fd = server.client->fd; |
|
| 129 if (settings->continuepgn) { |
|
| 130 /* Continue game, send PGN data */ |
|
| 131 uint16_t mc = gamestate.movecount; |
|
| 132 size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move); |
|
| 133 char *pgndata = malloc(pgndata_size); |
|
| 134 memcpy(pgndata, &(settings->gameinfo), sizeof(GameInfo)); |
|
| 135 unsigned offset = sizeof(GameInfo); |
|
| 136 memcpy(pgndata+offset, &mc, sizeof(mc)); |
|
| 137 offset += sizeof(mc); |
|
| 138 memcpy(pgndata+offset, gamestate.moves, mc*sizeof(Move)); |
|
| 139 net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size); |
|
| 140 free(pgndata); |
|
| 141 } else { |
|
| 142 /* Start new game */ |
|
| 143 net_send_data(fd, NETCODE_GAMEINFO, |
|
| 144 &(settings->gameinfo), sizeof(GameInfo)); |
|
| 145 } |
|
| 146 addstr("\rClient connected - awaiting challenge acceptance..."); |
|
| 147 refresh(); |
|
| 148 int code = net_recieve_code(fd); |
|
| 149 int exitcode = 0; |
|
| 150 if (code == NETCODE_ACCEPT) { |
|
| 151 addstr("\rClient connected - challenge accepted."); |
|
| 152 clrtoeol(); |
|
| 153 game_play(settings, &gamestate, fd); |
|
| 154 net_destroy(&server); |
|
| 155 game_review(settings, &gamestate); |
|
| 156 } else if (code == NETCODE_DECLINE) { |
|
| 157 addstr("\rClient connected - challenge declined."); |
|
| 158 clrtoeol(); |
|
| 159 net_destroy(&server); |
|
| 160 } else if (code == NETCODE_CONNLOST) { |
|
| 161 addstr("\rClient connected - but gave no response."); |
|
| 162 clrtoeol(); |
|
| 163 net_destroy(&server); |
|
| 164 } else { |
|
| 165 addstr("\rInvalid client response"); |
|
| 166 clrtoeol(); |
|
| 167 |
|
| 168 net_destroy(&server); |
|
| 169 exitcode = 1; |
|
| 170 } |
|
| 171 gamestate_cleanup(&gamestate); |
|
| 172 return exitcode; |
|
| 173 } |
|