Subversion Repositories Games.Chess Giants

Rev

Rev 177 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// network.cpp
2
 
3
#include "common.h"
4
 
5
 
6
// prototypes of local functions
7
static void EvaluateServerReply (player_t *player);
8
 
9
 
10
bool PlayerNetwork_Init (player_t *player)
11
{
12
   // this function initializes the network layer and connects to the chess server
13
 
14
   struct hostent *hostinfo;
15
   struct sockaddr_in service;
16
   unsigned long nonblocking_mode;
17
   char ascii_hostname[256];
18
 
19
   onlineplayers = NULL; // ensure the online players array is empty
20
   onlineplayer_count = 0; // and reset the online players count
21
   onlineplayers_updated = false;
22
 
23
   soughtgames = NULL; // ensure the sought games array is empty
24
   soughtgame_count = 0; // and reset the sought games count
25
   soughtgames_updated = false;
26
   lastsought_time = 0;
27
 
28
   chatterchannels = NULL; // ensure the chatter channels array is empty
29
   chatterchannel_count = 0; // and reset the chatter channels count
30
   chatterchannels_updated = false;
31
   selected_chatterchannel = NULL;
32
 
33
   PlayerCards_Init (); // initialize the player cards array
34
   Challenges_Init (); // initialize the challenges array
35
   Interlocutors_Init (); // initialize the interlocutors array
36
 
37
   server_motd[0] = 0; // reset the server MOTD
38
 
39
   player->is_connected = false; // remember we aren't connected yet
40
   player->is_logged_in = false; // and not logged in either
41
   player->is_in_game = false; // we aren't in game either
42
   player->game_number = 0;
43
   player->remaining_seconds = 0;
44
   player->our_socket = INVALID_SOCKET; // and that we have no socket yet
45
   player->name[0] = 0; // player name undefined
46
 
47
   // get the chess server's IP address from the host name
48
   ConvertTo7BitASCII (ascii_hostname, sizeof (ascii_hostname), options.network.server_address);
49
   hostinfo = gethostbyname (ascii_hostname);
50
   if (hostinfo == NULL)
51
   {
52
      messagebox.hWndParent = hMainWnd;
53
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
54
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed"));
55
      messagebox.flags = MB_ICONWARNING | MB_OK;
56
      DialogBox_Message (&messagebox); // display a modeless error message box
57
 
58
      PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
59
      return (false);
60
   }
61
 
62
   // fill in the sockaddr server structure with the server hostinfo data
63
   service.sin_family = AF_INET;
64
   service.sin_addr.s_addr = inet_addr (inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0]));
65
   service.sin_port = htons (options.network.server_port);
66
 
67
   // create our socket
68
   if ((player->our_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
69
   {
70
      messagebox.hWndParent = hMainWnd;
71
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
72
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed"));
73
      messagebox.flags = MB_ICONWARNING | MB_OK;
74
      DialogBox_Message (&messagebox); // display a modeless error message box
75
 
76
      PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
77
      return (false);
78
   }
79
 
80
   // connect the game to the chess server using that socket
81
   if (connect (player->our_socket, (struct sockaddr *) &service, sizeof (service)) == -1)
82
   {
83
      messagebox.hWndParent = hMainWnd;
84
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
85
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed"));
86
      messagebox.flags = MB_ICONWARNING | MB_OK;
87
      DialogBox_Message (&messagebox); // display a modeless error message box
88
 
89
      PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
90
      return (false);
91
   }
92
 
93
   // set it to be non-blocking (but only AFTER the connection is made)
94
   nonblocking_mode = 1;
95
   if (ioctlsocket (player->our_socket, FIONBIO, &nonblocking_mode) == SOCKET_ERROR)
96
   {
97
      messagebox.hWndParent = hMainWnd;
98
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
99
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_NetworkInitializationFailed"));
100
      messagebox.flags = MB_ICONWARNING | MB_OK;
101
      DialogBox_Message (&messagebox); // display a modeless error message box
102
 
103
      PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
104
      return (false);
105
   }
106
 
107
   // initialize the debug log file
108
   Debug_Init (L"Chess server output.txt");
109
 
110
   player->is_connected = true; // remember we are connected
111
   return (true); // finished, we have a valid, connected socket
112
}
113
 
114
 
115
void PlayerNetwork_Shutdown (player_t *player)
116
{
117
   // this function shutdowns the network layer
118
 
119
   int chatterchannel_index;
120
 
121
   // shutdown the socket if it is open. This will disconnect us from the server.
122
   if (player->our_socket != INVALID_SOCKET)
123
      closesocket (player->our_socket); // close the network socket
124
   player->our_socket = INVALID_SOCKET;
125
 
126
   player->is_connected = false; // remember we are no longer connected
127
   player->is_logged_in = false; // and not logged in either
128
   player->is_in_game = false; // we aren't in game either
129
   player->game_number = 0;
130
   player->remaining_seconds = 0;
131
   player->name[0] = 0; // player name undefined
132
 
133
   server_motd[0] = 0; // reset the server MOTD
134
 
135
   // cycle through all chatter channels we know...
136
   for (chatterchannel_index = 0; chatterchannel_index < chatterchannel_count; chatterchannel_index++)
137
   {
138
      SAFE_free ((void **) &chatterchannels[chatterchannel_index].members); // free their member list
139
      chatterchannels[chatterchannel_index].member_count = 0; // reset their members count
140
   }
141
   SAFE_free ((void **) &chatterchannels); // free the chatter channels array
142
   chatterchannel_count = 0; // and reset the chatter channels count
143
   chatterchannels_updated = false;
144
   selected_chatterchannel = NULL;
145
 
146
   SAFE_free ((void **) &soughtgames); // free the sought games array
147
   soughtgame_count = 0; // and reset the sought games count
148
   soughtgames_updated = false;
149
 
150
   SAFE_free ((void **) &onlineplayers); // free the online players array
151
   onlineplayer_count = 0; // and reset the online players count
152
   onlineplayers_updated = false;
153
 
154
   Interlocutors_Shutdown (); // shutdown the interlocutors
155
   Challenges_Shutdown (); // shutdown the challenges
156
   PlayerCards_Shutdown (); // shutdown the player cards
157
 
158
   // shutdown all possible opened UI windows
159
   if (IsWindow (hChatterChannelsWnd))
160
      DestroyWindow (hChatterChannelsWnd);
161
   hChatterChannelsWnd = NULL;
162
   if (IsWindow (hGamesWnd))
163
      DestroyWindow (hGamesWnd);
164
   hGamesWnd = NULL;
165
   if (IsWindow (hMOTDWnd))
166
      DestroyWindow (hMOTDWnd);
167
   hMOTDWnd = NULL;
168
   if (IsWindow (hOpponentsWnd))
169
      DestroyWindow (hOpponentsWnd);
170
   hOpponentsWnd = NULL;
171
   if (IsWindow (hSoughtWnd))
172
      DestroyWindow (hSoughtWnd);
173
   hSoughtWnd = NULL;
174
 
175
   return; // finished
176
}
177
 
178
 
179
bool PlayerNetwork_Think (player_t *player)
180
{
181
   // this function is called once per game tick to listen to the network and react to what the server tells us. Returns TRUE
182
   // if we need to update the scene.
183
 
184
   player_t *local_player;
185
   char *block_start;
186
   char *block_end;
187
   char *ascii_sendbuffer; // mallocated
188
   wchar_t *widechar_buffer; // mallocated
189
   wchar_t *widechar_line;
190
   int send_retval;
191
   int recv_retval;
192
   int byte_index;
193
   int rewrite_index;
194
   int length;
195
   bool do_update;
196
 
197
   // are we NOT connected yet ?
198
   if (!player->is_connected)
199
      return (false); // consistency check: if we aren't connected, don't do anything
200
 
201
   // are we NOT logged in yet AND is our socket valid AND is there nothing in the center of the screen yet ?
202
   if (!player->is_logged_in && (player->our_socket != INVALID_SOCKET) && !the_scene.gui.central_text.is_displayed)
203
   {
140 pmbaty 204
      Scene_UpdateText (&the_scene.gui.central_text, RGBA_TO_RGBACOLOR (255, 255, 255, 191), DURATION_INFINITE, true, LOCALIZE (L"Connecting")); // display "connecting" in the middle of the screen
1 pmbaty 205
      the_scene.gui.want_spinwheel = true; // start spinning wheel
206
   }
207
 
208
   do_update = false; // assume we don't need to update the scene until told otherwise
209
 
210
   ////////////////////////////
211
   // BEGIN listening to server
212
 
213
   // proceed in filling the ascii buffer with what's coming
214
 
215
   // get a hand on the end of the player's recv buffer and compute its remaining size
216
   length = strlen (player->ascii_recvbuffer);
217
 
218
   // if the server sent us something, append it to what we've received
219
   recv_retval = recv (player->our_socket, &player->ascii_recvbuffer[length], player->recvbuffer_size - length, 0);
220
   if (recv_retval > 0)
221
   {
222
      // parse all received data and eradicate all carriage returns. Also convert %'s to a safer glyph.
223
      rewrite_index = 0;
224
      for (byte_index = 0; byte_index < recv_retval; byte_index++)
225
         if (player->ascii_recvbuffer[length + byte_index] != '\r')
226
         {
227
            if (player->ascii_recvbuffer[length + byte_index] == '%')
228
               player->ascii_recvbuffer[length + rewrite_index] = '¤';
229
            else
230
               player->ascii_recvbuffer[length + rewrite_index] = player->ascii_recvbuffer[length + byte_index];
231
            rewrite_index++;
232
         }
233
      player->ascii_recvbuffer[length + rewrite_index] = 0; // terminate the buffer ourselves
234
      length = strlen (player->ascii_recvbuffer); // and update the new recvbuffer's length
235
   }
236
   else if (recv_retval == SOCKET_ERROR)
237
   {
238
      recv_retval = WSAGetLastError (); // ask Windows to be more specific about the error
239
      if (recv_retval == WSAEWOULDBLOCK)
240
         ; // it's okay. We're a non-blocking socket.
241
      else if (recv_retval == WSAETIMEDOUT)
242
      {
243
         messagebox.hWndParent = hMainWnd;
244
         wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
245
         wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ConnectionToChessServerLost"));
246
         messagebox.flags = MB_ICONWARNING | MB_OK;
247
         DialogBox_Message (&messagebox); // display a modeless error message box
248
 
249
         PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
250
         return (false);
251
      }
252
   }
253
 
254
   // END listening to server
255
   //////////////////////////
256
 
257
   //////////////////////////////////
258
   // BEGIN evaluating server replies
259
 
260
   // are we NOT logged in yet ?
261
   if (!player->is_logged_in)
262
   {
263
      // is it a login prompt ?
264
      if ((length > 7) && (strcmp (&player->ascii_recvbuffer[length - 7], "login: ") == 0))
265
      {
266
         Player_SendBuffer_Add (player, 1000, L"%s\n", options.network.login); // send the login string
267
         player->ascii_recvbuffer[0] = 0; // and discard recvbuffer as we've processed this message
268
      }
269
 
270
      // else is it a password prompt ?
271
      else if (((length > 10) && (strcmp (&player->ascii_recvbuffer[length - 10], "password: ") == 0))
272
               || ((length > 3) && (strcmp (&player->ascii_recvbuffer[length - 3], "\":\n") == 0)))
273
      {
274
         Player_SendBuffer_Add (player, 1000, L"%s\n", options.network.password); // send the password string
275
         player->ascii_recvbuffer[0] = 0; // and discard recvbuffer as we've processed this message
276
      }
277
 
278
      // else is it a confirmation that we've successfully logged in ?
279
      else if (((block_start = strstr (player->ascii_recvbuffer, "\n**** Starting FICS session as ")) != NULL)
280
               && (block_end = strstr (player->ascii_recvbuffer, " ****\n")))
281
      {
282
         player->is_logged_in = true; // remember we are logged in
283
         the_scene.gui.central_text.disappear_time = current_time + 1.0f; // fade the "connecting" phrase out now (FIXME: ugly)
284
         the_scene.gui.want_spinwheel = false; // stop spinning wheel
285
         do_update = true; // and update the scene
286
 
287
         block_start += 31; // skip the "\n**** Starting FICS session as " substring
288
         local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the local player
289
 
290
         // copy out local player's login name, as reported by server, and convert it to wchar_t
291
         for (byte_index = 0; (byte_index < WCHAR_SIZEOF (local_player->name)) && isalpha (block_start[byte_index]); byte_index++)
292
            local_player->name[byte_index] = (wchar_t) block_start[byte_index]; // copy nickname one character after the other. Very dirty conversion.
293
         if (byte_index < WCHAR_SIZEOF (local_player->name))
294
            local_player->name[byte_index] = 0; // finish the string ourselves
295
         else
296
            local_player->name[WCHAR_SIZEOF (local_player->name) - 1] = 0; // truncate it if neeeded
297
 
298
         server_motd[0] = 0; // remember we haven't read the MOTD yet
299
         the_board.reevaluate = true; // reeevaluate the board (to update the title bar)
300
 
301
         // see if there's more data to parse after this message, in which case move it in place
302
         length = strlen (block_end + 6);
303
         if (length > 0)
304
            memmove (player->ascii_recvbuffer, block_end + 6, length + 1);
305
         else
306
            player->ascii_recvbuffer[0] = 0; // else discard recvbuffer as we've processed this message
307
      }
308
   }
309
 
310
   // else we are logged in
311
   else
312
   {
313
      // see if we have a complete reply block (remember: percent sign was replaced with '¤')
314
      block_end = strstr (player->ascii_recvbuffer, "\nfics¤ ");
315
 
316
      // as long as we can find some...
317
      while (block_end != NULL)
318
      {
319
         *block_end = 0; // break the string here
320
 
321
         // convert the block to wide char and evaluate it, taking action if needed
322
         ConvertToWideChar (player->recvbuffer, player->recvbuffer_size, player->ascii_recvbuffer);
323
         Debug_Log (L"RECEIVED:[%s]\n", player->recvbuffer); // log what we've received
324
         EvaluateServerReply (player); // and evaluate it
325
 
326
         // see if there's more data to parse after this message, in which case move it in place
327
         length = strlen (block_end + 7);
328
         if (length > 0)
329
            memmove (player->ascii_recvbuffer, block_end + 7, length + 1);
330
         else
331
         {
332
            player->ascii_recvbuffer[0] = 0;
333
            break; // it was the last prompt, so discard recvbuffer as we've processed this message
334
         }
335
         block_end = strstr (player->ascii_recvbuffer, "\nfics¤ "); // remember: percent sign was replaced with '¤'
336
      }
337
   }
338
 
339
   // END evaluating server replies
340
   ////////////////////////////////
341
 
171 pmbaty 342
   // have we been notified that the current player just changed ?
343
   if (player->should_wakeup)
1 pmbaty 344
   {
171 pmbaty 345
      // has at least one move been played AND is it the remote player's turn now ?
346
      if ((the_board.move_count > 1) && (Board_ColorToMove (&the_board) == player->color))
347
      {
177 pmbaty 348
         Debug_LogMove (&the_board.moves[the_board.move_count - 1], L"===Local player just played, sending the chosen move to chess server===\n");
171 pmbaty 349
         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
350
      }
351
      player->should_wakeup = false; // remember we took this notification into account
1 pmbaty 352
   }
353
 
354
   // has the user entered some chatter text ?
355
   if (!the_scene.gui.is_entering_text && (the_scene.gui.entered_ccreply.text != NULL))
356
   {
357
      // is player logged in AND has a chatter channel been selected ? if so, send chat string to server
358
      if (player->is_logged_in && (selected_chatterchannel != NULL))
359
         Player_SendBuffer_Add (player, 1000, L"+channel %d\ntell %d %s\n", selected_chatterchannel->id, selected_chatterchannel->id, the_scene.gui.entered_ccreply.text);
360
 
361
      // reset the entered text buffer
362
      SAFE_free ((void **) &the_scene.gui.entered_ccreply.text); // reset the entered text buffer
363
      the_scene.gui.entered_ccreply.text_length = 0; // and set its length to zero
364
   }
365
 
366
   // does the local player want to cancel its last move ? this can be either HIS move or HIS OPPONENTS's move...
367
   local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the local player
368
   if ((local_player != NULL) && local_player->wants_cancel)
369
   {
370
      Player_SendBuffer_Add (player, 1000, L"takeback\n"); // send the takeback request to our opponent
371
      Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRequestSent"), player->name); // send a notification to its chat window
136 pmbaty 372
      the_board.game_state = STATE_PLAYING; // remember the game is now playing (in case we wanted to cancel the closing move of a finished game, this opens the game again)
1 pmbaty 373
      local_player->wants_cancel = false; // don't do this all day long
374
   }
375
   if (player->wants_cancel)
376
   {
377
      Player_SendBuffer_Add (player, 1000, L"takeback\n"); // send the takeback request to our opponent
378
      Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRequestSent"), player->name); // send a notification to its chat window
136 pmbaty 379
      the_board.game_state = STATE_PLAYING; // remember the game is now playing (in case we wanted to cancel the closing move of a finished game, this opens the game again)
1 pmbaty 380
      player->wants_cancel = false; // don't do this all day long
381
   }
382
 
383
   // if we have something to send, do it
384
   if (!player->sendbuffer_locked && (player->sendbuffer[0] != 0) && (animation_endtime + 1.0f < current_time))
385
   {
386
      // log what we're sending
387
      Debug_Log (L"SENDING:[%s]\n", player->sendbuffer);
388
 
389
      player->sendbuffer_locked = true; // lock the buffer
390
 
391
      // now read line per line (mallocate a line buffer as large as necessary)
392
      length = wcslen (player->sendbuffer) + 1 + 1; // +1 for \n, +1 for null terminator
393
      ascii_sendbuffer = (char *) SAFE_malloc (length, sizeof (char), false);
394
      widechar_buffer = (wchar_t *) SAFE_malloc (length, sizeof (wchar_t), false);
395
      widechar_line = player->sendbuffer; // start at the first character
396
      while ((widechar_line = wcsgets (widechar_buffer, length, widechar_line)) != NULL)
397
      {
398
         wcscat_s (widechar_buffer, length, L"\n"); // put the carriage return back
399
         ConvertTo7BitASCII (ascii_sendbuffer, length, widechar_buffer); // convert to ASCII
400
         send_retval = send (player->our_socket, ascii_sendbuffer, strlen (ascii_sendbuffer), 0); // send data
401
      }
402
      SAFE_free ((void **) &widechar_buffer); // we no longer need the line buffer, so free it
403
      SAFE_free ((void **) &ascii_sendbuffer);
404
 
405
      player->sendbuffer[0] = 0; // what we had to send has been sent, reset the send buffer
406
      player->sendbuffer_locked = false; // and unlock it
407
 
408
      // did send() report an error ?
409
      if (send_retval == SOCKET_ERROR)
410
      {
411
         messagebox.hWndParent = hMainWnd;
412
         wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
413
         wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ConnectionToChessServerLost"));
414
         messagebox.flags = MB_ICONERROR | MB_OK;
415
         DialogBox_Message (&messagebox); // display a modeless error message box
416
 
417
         PlayerNetwork_Shutdown (player); // on error, shutdown the network and display an error message
418
         return (true); // on error, cancel
419
      }
420
   }
421
 
422
   return (do_update); // finished
423
}
424
 
425
 
426
static void EvaluateServerReply (player_t *player)
427
{
428
   // this function parses a network reply and evaluates it, deciding what to do
429
 
430
   //////////////////////
431
   // OUT OF GAME REPLIES
432
 
433
   // the MOTD is the first reply to be evaluated
434
   EvaluateServerReply_MOTD (player);
435
 
436
   // evaluate dangerous replies first, i.e. those that contain user-settable text
437
   EvaluateServerReply_Announcement (player);
438
   EvaluateServerReply_ChannelMessage (player);
439
   EvaluateServerReply_PrivateMessage (player);
440
   EvaluateServerReply_Finger (player);
441
 
442
   // the less dangerous replies can now be evaluated without ambiguity
443
   EvaluateServerReply_Seek (player);
444
   EvaluateServerReply_Challenge (player);
445
   EvaluateServerReply_ChallengeAccepted (player);
446
   EvaluateServerReply_ChallengeDeclined (player);
447
   EvaluateServerReply_Takeback (player);
448
   EvaluateServerReply_TakebackDeclinedByOther (player);
449
   EvaluateServerReply_TakebackDeclinedByYou (player);
450
   EvaluateServerReply_PlayNotAllowed (player);
451
   EvaluateServerReply_PlayUnexistent (player);
452
   EvaluateServerReply_PlayWrongRating (player);
453
   EvaluateServerReply_ChannelsAndMembers (player);
454
   EvaluateServerReply_SoughtList (player);
455
   EvaluateServerReply_PlayersList (player);
456
 
457
   //////////////////
458
   // IN GAME REPLIES
459
 
460
   EvaluateServerReply_GameStarting (player);
461
   EvaluateServerReply_GameState (player);
462
   EvaluateServerReply_GameResults (player);
463
 
464
   return; // finished evaluating this reply
465
}