- // network.cpp 
-   
- #include "common.h" 
-   
-   
- // prototypes of local functions 
- static void StartThread_ListenToServer (void *thread_parms); 
- static void EvaluateServerReply (player_t *player); 
-   
-   
- bool PlayerNetwork_Init (player_t *player) 
- { 
-    // this function initializes the network layer and connects to the chess server 
-   
-    WSADATA wsaData; 
-    struct hostent *hostinfo; 
-    struct sockaddr_in service; 
-    unsigned long nonblocking_mode; 
-    char ascii_hostname[256]; 
-   
-    onlineplayers = NULL; // ensure the online players array is empty 
-    onlineplayer_count = 0; // and reset the online players count 
-    onlineplayers_updated = false; 
-   
-    soughtgames = NULL; // ensure the sought games array is empty 
-    soughtgame_count = 0; // and reset the sought games count 
-    soughtgames_updated = false; 
-    lastsought_time = 0; 
-   
-    chatterchannels = NULL; // ensure the chatter channels array is empty 
-    chatterchannel_count = 0; // and reset the chatter channels count 
-    chatterchannels_updated = false; 
-    selected_chatterchannel = NULL; 
-   
-    PlayerCards_Init (); // initialize the player cards array 
-    Challenges_Init (); // initialize the challenges array 
-    Interlocutors_Init (); // initialize the interlocutors array 
-   
-    server_motd[0] = 0; // reset the server MOTD 
-   
-    player->is_connected = false; // remember we aren't connected yet 
-    player->is_logged_in = false; // and not logged in either 
-    player->is_in_game = false; // we aren't in game either 
-    player->game_number = 0; 
-    player->remaining_seconds = 0; 
-    player->our_socket = INVALID_SOCKET; // and that we have no socket yet 
-    player->name[0] = 0; // player name undefined 
-   
-    // initialize WinSock 
-    if (WSAStartup (MAKEWORD (2, 2), &wsaData) != 0) 
-    { 
-       messagebox.hWndParent = hMainWnd; 
-       wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-       wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed")); 
-       messagebox.flags = MB_ICONWARNING | MB_OK; 
-       DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-       PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-       return (false); 
-    } 
-   
-    // get the chess server's IP address from the host name 
-    ConvertTo7BitASCII (ascii_hostname, sizeof (ascii_hostname), options.network.server_address); 
-    hostinfo = gethostbyname (ascii_hostname); 
-    if (hostinfo == NULL) 
-    { 
-       messagebox.hWndParent = hMainWnd; 
-       wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-       wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed")); 
-       messagebox.flags = MB_ICONWARNING | MB_OK; 
-       DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-       PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-       return (false); 
-    } 
-   
-    // fill in the sockaddr server structure with the server hostinfo data 
-    service.sin_family = AF_INET; 
-    service.sin_addr.s_addr = inet_addr (inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0])); 
-    service.sin_port = htons (options.network.server_port); 
-   
-    // create our socket 
-    if ((player->our_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) 
-    { 
-       messagebox.hWndParent = hMainWnd; 
-       wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-       wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed")); 
-       messagebox.flags = MB_ICONWARNING | MB_OK; 
-       DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-       PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-       return (false); 
-    } 
-   
-    // connect the game to the chess server using that socket 
-    if (connect (player->our_socket, (struct sockaddr *) &service, sizeof (service)) == -1) 
-    { 
-       messagebox.hWndParent = hMainWnd; 
-       wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-       wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed")); 
-       messagebox.flags = MB_ICONWARNING | MB_OK; 
-       DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-       PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-       return (false); 
-    } 
-   
-    // set it to be non-blocking (but only AFTER the connection is made) 
-    nonblocking_mode = 1; 
-    if (ioctlsocket (player->our_socket, FIONBIO, &nonblocking_mode) == SOCKET_ERROR) 
-    { 
-       messagebox.hWndParent = hMainWnd; 
-       wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-       wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed")); 
-       messagebox.flags = MB_ICONWARNING | MB_OK; 
-       DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-       PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-       return (false); 
-    } 
-   
-    // initialize the debug log file 
-    Debug_Init (L"Chess server output.txt"); 
-   
-    player->is_connected = true; // remember we are connected 
-    return (true); // finished, we have a valid, connected socket 
- } 
-   
-   
- void PlayerNetwork_Shutdown (player_t *player) 
- { 
-    // this function shutdowns the network layer 
-   
-    int chatterchannel_index; 
-   
-    // shutdown the socket if it is open. This will disconnect us from the server. 
-    if (player->our_socket != INVALID_SOCKET) 
-       closesocket (player->our_socket); // close the network socket 
-    player->our_socket = INVALID_SOCKET; 
-   
-    player->is_connected = false; // remember we are no longer connected 
-    player->is_logged_in = false; // and not logged in either 
-    player->is_in_game = false; // we aren't in game either 
-    player->game_number = 0; 
-    player->remaining_seconds = 0; 
-    player->name[0] = 0; // player name undefined 
-   
-    server_motd[0] = 0; // reset the server MOTD 
-   
-    // cycle through all chatter channels we know... 
-    for (chatterchannel_index = 0; chatterchannel_index < chatterchannel_count; chatterchannel_index++) 
-    { 
-       SAFE_free ((void **) &chatterchannels[chatterchannel_index].members); // free their member list 
-       chatterchannels[chatterchannel_index].member_count = 0; // reset their members count 
-    } 
-    SAFE_free ((void **) &chatterchannels); // free the chatter channels array 
-    chatterchannel_count = 0; // and reset the chatter channels count 
-    chatterchannels_updated = false; 
-    selected_chatterchannel = NULL; 
-   
-    SAFE_free ((void **) &soughtgames); // free the sought games array 
-    soughtgame_count = 0; // and reset the sought games count 
-    soughtgames_updated = false; 
-   
-    SAFE_free ((void **) &onlineplayers); // free the online players array 
-    onlineplayer_count = 0; // and reset the online players count 
-    onlineplayers_updated = false; 
-   
-    Interlocutors_Shutdown (); // shutdown the interlocutors 
-    Challenges_Shutdown (); // shutdown the challenges 
-    PlayerCards_Shutdown (); // shutdown the player cards 
-   
-    // shutdown all possible opened UI windows 
-    if (IsWindow (hChatterChannelsWnd)) 
-       DestroyWindow (hChatterChannelsWnd); 
-    hChatterChannelsWnd = NULL; 
-    if (IsWindow (hGamesWnd)) 
-       DestroyWindow (hGamesWnd); 
-    hGamesWnd = NULL; 
-    if (IsWindow (hMOTDWnd)) 
-       DestroyWindow (hMOTDWnd); 
-    hMOTDWnd = NULL; 
-    if (IsWindow (hOpponentsWnd)) 
-       DestroyWindow (hOpponentsWnd); 
-    hOpponentsWnd = NULL; 
-    if (IsWindow (hSoughtWnd)) 
-       DestroyWindow (hSoughtWnd); 
-    hSoughtWnd = NULL; 
-   
-    // shutdown WinSock 
-    WSACleanup (); 
-   
-    return; // finished 
- } 
-   
-   
- bool PlayerNetwork_Think (player_t *player) 
- { 
-    // this function is called once per game tick to listen to the network and react to what the server tells us. Returns TRUE 
-    // if we need to update the scene. 
-   
-    player_t *local_player; 
-    char *block_start; 
-    char *block_end; 
-    char *ascii_sendbuffer; // mallocated 
-    wchar_t *widechar_buffer; // mallocated 
-    wchar_t *widechar_line; 
-    int send_retval; 
-    int recv_retval; 
-    int byte_index; 
-    int rewrite_index; 
-    int length; 
-    bool do_update; 
-   
-    // are we NOT connected yet ? 
-    if (!player->is_connected) 
-       return (false); // consistency check: if we aren't connected, don't do anything 
-   
-    // are we NOT logged in yet AND is our socket valid AND is there nothing in the center of the screen yet ? 
-    if (!player->is_logged_in && (player->our_socket != INVALID_SOCKET) && !the_scene.gui.central_text.is_displayed) 
-    { 
-       Scene_SetText (&the_scene.gui.central_text, 50.0f, 40.0f, -1, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, centermsg_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191), 
-                      999999.0f, true, LOCALIZE (L"Connecting")); // display "connecting" in the middle of the screen 
-       the_scene.gui.want_spinwheel = true; // start spinning wheel 
-    } 
-   
-    do_update = false; // assume we don't need to update the scene until told otherwise 
-   
-    //////////////////////////// 
-    // BEGIN listening to server 
-   
-    // proceed in filling the ascii buffer with what's coming 
-   
-    // get a hand on the end of the player's recv buffer and compute its remaining size 
-    length = strlen (player->ascii_recvbuffer); 
-   
-    // if the server sent us something, append it to what we've received 
-    recv_retval = recv (player->our_socket, &player->ascii_recvbuffer[length], player->recvbuffer_size - length, 0); 
-    if (recv_retval > 0) 
-    { 
-       // parse all received data and eradicate all carriage returns. Also convert %'s to a safer glyph. 
-       rewrite_index = 0; 
-       for (byte_index = 0; byte_index < recv_retval; byte_index++) 
-          if (player->ascii_recvbuffer[length + byte_index] != '\r') 
-          { 
-             if (player->ascii_recvbuffer[length + byte_index] == '%') 
-                player->ascii_recvbuffer[length + rewrite_index] = '¤'; 
-             else 
-                player->ascii_recvbuffer[length + rewrite_index] = player->ascii_recvbuffer[length + byte_index]; 
-             rewrite_index++; 
-          } 
-       player->ascii_recvbuffer[length + rewrite_index] = 0; // terminate the buffer ourselves 
-       length = strlen (player->ascii_recvbuffer); // and update the new recvbuffer's length 
-    } 
-    else if (recv_retval == SOCKET_ERROR) 
-    { 
-       recv_retval = WSAGetLastError (); // ask Windows to be more specific about the error 
-       if (recv_retval == WSAEWOULDBLOCK) 
-          ; // it's okay. We're a non-blocking socket. 
-       else if (recv_retval == WSAETIMEDOUT) 
-       { 
-          messagebox.hWndParent = hMainWnd; 
-          wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-          wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ConnectionToChessServerLost")); 
-          messagebox.flags = MB_ICONWARNING | MB_OK; 
-          DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-          PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-          return (false); 
-       } 
-    } 
-   
-    // END listening to server 
-    ////////////////////////// 
-   
-    ////////////////////////////////// 
-    // BEGIN evaluating server replies 
-   
-    // are we NOT logged in yet ? 
-    if (!player->is_logged_in) 
-    { 
-       // is it a login prompt ? 
-       if ((length > 7) && (strcmp (&player->ascii_recvbuffer[length - 7], "login: ") == 0)) 
-       { 
-          Player_SendBuffer_Add (player, 1000, L"%s\n", options.network.login); // send the login string 
-          player->ascii_recvbuffer[0] = 0; // and discard recvbuffer as we've processed this message 
-       } 
-   
-       // else is it a password prompt ? 
-       else if (((length > 10) && (strcmp (&player->ascii_recvbuffer[length - 10], "password: ") == 0)) 
-                || ((length > 3) && (strcmp (&player->ascii_recvbuffer[length - 3], "\":\n") == 0))) 
-       { 
-          Player_SendBuffer_Add (player, 1000, L"%s\n", options.network.password); // send the password string 
-          player->ascii_recvbuffer[0] = 0; // and discard recvbuffer as we've processed this message 
-       } 
-   
-       // else is it a confirmation that we've successfully logged in ? 
-       else if (((block_start = strstr (player->ascii_recvbuffer, "\n**** Starting FICS session as ")) != NULL) 
-                && (block_end = strstr (player->ascii_recvbuffer, " ****\n"))) 
-       { 
-          player->is_logged_in = true; // remember we are logged in 
-          the_scene.gui.central_text.disappear_time = current_time + 1.0f; // fade the "connecting" phrase out now (FIXME: ugly) 
-          the_scene.gui.want_spinwheel = false; // stop spinning wheel 
-          do_update = true; // and update the scene 
-   
-          block_start += 31; // skip the "\n**** Starting FICS session as " substring 
-          local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the local player 
-   
-          // copy out local player's login name, as reported by server, and convert it to wchar_t 
-          for (byte_index = 0; (byte_index < WCHAR_SIZEOF (local_player->name)) && isalpha (block_start[byte_index]); byte_index++) 
-             local_player->name[byte_index] = (wchar_t) block_start[byte_index]; // copy nickname one character after the other. Very dirty conversion. 
-          if (byte_index < WCHAR_SIZEOF (local_player->name)) 
-             local_player->name[byte_index] = 0; // finish the string ourselves 
-          else 
-             local_player->name[WCHAR_SIZEOF (local_player->name) - 1] = 0; // truncate it if neeeded 
-   
-          server_motd[0] = 0; // remember we haven't read the MOTD yet 
-          the_board.reevaluate = true; // reeevaluate the board (to update the title bar) 
-   
-          // see if there's more data to parse after this message, in which case move it in place 
-          length = strlen (block_end + 6); 
-          if (length > 0) 
-             memmove (player->ascii_recvbuffer, block_end + 6, length + 1); 
-          else 
-             player->ascii_recvbuffer[0] = 0; // else discard recvbuffer as we've processed this message 
-       } 
-    } 
-   
-    // else we are logged in 
-    else 
-    { 
-       // see if we have a complete reply block (remember: percent sign was replaced with '¤') 
-       block_end = strstr (player->ascii_recvbuffer, "\nfics¤ "); 
-   
-       // as long as we can find some... 
-       while (block_end != NULL) 
-       { 
-          *block_end = 0; // break the string here 
-   
-          // convert the block to wide char and evaluate it, taking action if needed 
-          ConvertToWideChar (player->recvbuffer, player->recvbuffer_size, player->ascii_recvbuffer); 
-          Debug_Log (L"RECEIVED:[%s]\n", player->recvbuffer); // log what we've received 
-          EvaluateServerReply (player); // and evaluate it 
-   
-          // see if there's more data to parse after this message, in which case move it in place 
-          length = strlen (block_end + 7); 
-          if (length > 0) 
-             memmove (player->ascii_recvbuffer, block_end + 7, length + 1); 
-          else 
-          { 
-             player->ascii_recvbuffer[0] = 0; 
-             break; // it was the last prompt, so discard recvbuffer as we've processed this message 
-          } 
-          block_end = strstr (player->ascii_recvbuffer, "\nfics¤ "); // remember: percent sign was replaced with '¤' 
-       } 
-    } 
-   
-    // END evaluating server replies 
-    //////////////////////////////// 
-   
-    // have we been notified that the current player just changed, has at least one move been played AND is it the remote player's turn now ? 
-    if (the_board.has_playerchanged && (the_board.move_count > 1) && (Board_ColorToMove (&the_board) == player->color)) 
-    { 
-       Debug_Log (L"===Local player just played, sending the chosen move to chess server===\n"); 
-       Player_SendBuffer_Add (player, 1000, L"%s\n", the_board.moves[the_board.move_count - 1].pgntext); // send the move string to the chess server 
-    } 
-   
-    // has the user entered some chatter text ? 
-    if (!the_scene.gui.is_entering_text && (the_scene.gui.entered_ccreply.text != NULL)) 
-    { 
-       // is player logged in AND has a chatter channel been selected ? if so, send chat string to server 
-       if (player->is_logged_in && (selected_chatterchannel != NULL)) 
-          Player_SendBuffer_Add (player, 1000, L"+channel %d\ntell %d %s\n", selected_chatterchannel->id, selected_chatterchannel->id, the_scene.gui.entered_ccreply.text); 
-   
-       // reset the entered text buffer 
-       SAFE_free ((void **) &the_scene.gui.entered_ccreply.text); // reset the entered text buffer 
-       the_scene.gui.entered_ccreply.text_length = 0; // and set its length to zero 
-    } 
-   
-    // does the local player want to cancel its last move ? this can be either HIS move or HIS OPPONENTS's move... 
-    local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the local player 
-    if ((local_player != NULL) && local_player->wants_cancel) 
-    { 
-       Player_SendBuffer_Add (player, 1000, L"takeback\n"); // send the takeback request to our opponent 
-       Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRequestSent"), player->name); // send a notification to its chat window 
-       local_player->wants_cancel = false; // don't do this all day long 
-    } 
-    if (player->wants_cancel) 
-    { 
-       Player_SendBuffer_Add (player, 1000, L"takeback\n"); // send the takeback request to our opponent 
-       Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRequestSent"), player->name); // send a notification to its chat window 
-       player->wants_cancel = false; // don't do this all day long 
-    } 
-   
-    // if we have something to send, do it 
-    if (!player->sendbuffer_locked && (player->sendbuffer[0] != 0) && (animation_endtime + 1.0f < current_time)) 
-    { 
-       // log what we're sending 
-       Debug_Log (L"SENDING:[%s]\n", player->sendbuffer); 
-   
-       player->sendbuffer_locked = true; // lock the buffer 
-   
-       // now read line per line (mallocate a line buffer as large as necessary) 
-       length = wcslen (player->sendbuffer) + 1 + 1; // +1 for \n, +1 for null terminator 
-       ascii_sendbuffer = (char *) SAFE_malloc (length, sizeof (char), false); 
-       widechar_buffer = (wchar_t *) SAFE_malloc (length, sizeof (wchar_t), false); 
-       widechar_line = player->sendbuffer; // start at the first character 
-       while ((widechar_line = wcsgets (widechar_buffer, length, widechar_line)) != NULL) 
-       { 
-          wcscat_s (widechar_buffer, length, L"\n"); // put the carriage return back 
-          ConvertTo7BitASCII (ascii_sendbuffer, length, widechar_buffer); // convert to ASCII 
-          send_retval = send (player->our_socket, ascii_sendbuffer, strlen (ascii_sendbuffer), 0); // send data 
-       } 
-       SAFE_free ((void **) &widechar_buffer); // we no longer need the line buffer, so free it 
-       SAFE_free ((void **) &ascii_sendbuffer); 
-   
-       player->sendbuffer[0] = 0; // what we had to send has been sent, reset the send buffer 
-       player->sendbuffer_locked = false; // and unlock it 
-   
-       // did send() report an error ? 
-       if (send_retval == SOCKET_ERROR) 
-       { 
-          messagebox.hWndParent = hMainWnd; 
-          wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); 
-          wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ConnectionToChessServerLost")); 
-          messagebox.flags = MB_ICONERROR | MB_OK; 
-          DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-          PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message 
-          return (true); // on error, cancel 
-       } 
-    } 
-   
-    return (do_update); // finished 
- } 
-   
-   
- static void EvaluateServerReply (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    ////////////////////// 
-    // OUT OF GAME REPLIES 
-   
-    // the MOTD is the first reply to be evaluated 
-    EvaluateServerReply_MOTD (player); 
-   
-    // evaluate dangerous replies first, i.e. those that contain user-settable text 
-    EvaluateServerReply_Announcement (player); 
-    EvaluateServerReply_ChannelMessage (player); 
-    EvaluateServerReply_PrivateMessage (player); 
-    EvaluateServerReply_Finger (player); 
-   
-    // the less dangerous replies can now be evaluated without ambiguity 
-    EvaluateServerReply_Seek (player); 
-    EvaluateServerReply_Challenge (player); 
-    EvaluateServerReply_ChallengeAccepted (player); 
-    EvaluateServerReply_ChallengeDeclined (player); 
-    EvaluateServerReply_Takeback (player); 
-    EvaluateServerReply_TakebackDeclinedByOther (player); 
-    EvaluateServerReply_TakebackDeclinedByYou (player); 
-    EvaluateServerReply_PlayNotAllowed (player); 
-    EvaluateServerReply_PlayUnexistent (player); 
-    EvaluateServerReply_PlayWrongRating (player); 
-    EvaluateServerReply_ChannelsAndMembers (player); 
-    EvaluateServerReply_SoughtList (player); 
-    EvaluateServerReply_PlayersList (player); 
-   
-    ////////////////// 
-    // IN GAME REPLIES 
-   
-    EvaluateServerReply_GameStarting (player); 
-    EvaluateServerReply_GameState (player); 
-    EvaluateServerReply_GameResults (player); 
-   
-    return; // finished evaluating this reply 
- } 
-