3 * Daniel Nelson - 8/30/0
5 * Copyright (C) 2000 Daniel Nelson
6 * Copyright (C) 2004 Andrew Sayman
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * Daniel Nelson - aluminumangel.org
26 * Handles all that socket stuff. We should be more worried about the size of
27 * integers on various systems.
30 #include "Communicator.h"
34 #include <sys/types.h>
38 # include <sys/socket.h>
39 # include <sys/poll.h>
40 # include <netinet/in.h>
42 # include <arpa/inet.h>
45 # include <winsock2.h>
46 # define sleep(x) Sleep(x)
50 #include "Displayer.h"
51 #include "GarbageGenerator.h"
52 #include "GarbageFlavorImage.h"
53 #include "LevelLights.h"
54 #include "MetaState.h"
59 ENetHost
*Communicator::host
;
60 ENetPeer
*Communicator::peer
;
61 int Communicator::time_step
;
62 bool Communicator::comm_link_active
;
63 bool Communicator::no_communication
;
64 bool Communicator::have_communicated
;
65 int Communicator::last_recv_sync
;
66 int Communicator::last_own_sync
;
67 CommunicationBuffer
Communicator::send_buffer
;
68 CommunicationBuffer
Communicator::recv_buffer
;
69 GarbageBuffer
Communicator::send_garb_buffer
;
70 GarbageBuffer
Communicator::recv_garb_buffer
;
71 CommunicationBuffer
Communicator::work_buffer
;
72 bool Communicator::win_ties
;
73 char Communicator::opponent_name
[GC_PLAYER_NAME_LENGTH
];
75 static void print_ip_address( int ip
)
78 cout
<< ((ip
& 0xFF000000) >> 24) << ".";
79 cout
<< ((ip
& 0x00FF0000) >> 16) << ".";
80 cout
<< ((ip
& 0x0000FF00) >> 8) << ".";
81 cout
<< (ip
& 0x000000FF);
84 void Communicator::startupExchange ( char player_name
[GC_PLAYER_NAME_LENGTH
] )
87 MESSAGE("Exchanging names");
88 commSendRel(player_name
, GC_PLAYER_NAME_LENGTH
);
89 commGetRel(opponent_name
, GC_PLAYER_NAME_LENGTH
);
91 // notify if we have a personal garbage flavor image
92 MESSAGE("Exchanging garbage");
93 uint32 flag
= GarbageFlavorImage::personalGarbageFlavorImageExists();
98 GLubyte
*texture
= GarbageFlavorImage::loadPersonalGarbageFlavorImage();
99 commSendRel(texture
, 4 * DC_GARBAGE_TEX_LENGTH
* DC_GARBAGE_TEX_LENGTH
104 if (texture
!= null
) {
111 // check to see if they have a personal garbage flavor image
116 GLubyte texture
[4 * DC_GARBAGE_TEX_LENGTH
* DC_GARBAGE_TEX_LENGTH
];
117 commGetRel(texture
, 4 * DC_GARBAGE_TEX_LENGTH
* DC_GARBAGE_TEX_LENGTH
119 GarbageFlavorImage::handleNetworkGarbageFlavorImage(texture
);
123 void Communicator::initialize ( int mode
, int port
, char host_name
[256],
124 char player_name
[GC_PLAYER_NAME_LENGTH
] )
129 comm_link_active
= false;
131 if (enet_initialize () != 0)
133 cerr
<< "An error occurred while initializing ENet." << endl
;
138 cout
<< "ENet properly initialized." << endl
;
140 atexit (enet_deinitialize
);
143 address
.port
= CO_DEFAULT_PORT
;
147 switch (mode
& (CM_SERVER
| CM_CLIENT
)) {
149 address
.host
= ENET_HOST_ANY
;
151 host
= enet_host_create(&address
, 1, 0, 0);
153 cerr
<< "An error occured while trying to create the server" << endl
;
157 cout
<< "Waiting for connection at port " << address
.port
<< "..." << endl
;
160 if (enet_host_service(host
, &event
, CO_SERVER_TIME_OUT
* 1000) > 0)
162 switch (event
.type
) {
163 case ENET_EVENT_TYPE_CONNECT
: {
165 cout
<< "Accepting connection from ";
166 print_ip_address(peer
->address
.host
);
168 comm_link_active
= true;
171 case ENET_EVENT_TYPE_RECEIVE
: {
172 enet_packet_destroy(event
.packet
);
175 case ENET_EVENT_TYPE_DISCONNECT
: {
176 event
.peer
->data
= NULL
;
179 case ENET_EVENT_TYPE_NONE
: {
184 } while((mode
& CM_NO_TIME_OUT
) && !comm_link_active
);
186 if (!comm_link_active
)
188 cerr
<< "Time out." << endl
;
194 commSendRel(CO_VERSION
, strlen(CO_VERSION
) + 1);
196 // server sends extremeness level
197 enet_uint8 X_level
= ((mode
& CM_X
) ? 1 : 0);
198 commSendRel(&X_level
, sizeof(X_level
));
200 // for simplicity, server wins ties - but don't tell anyone; it's the only
201 // available symmetry breaking term
204 cout
<< "Connection made by ";
205 print_ip_address(peer
->address
.host
);
211 host
= enet_host_create(NULL
, 1, 57600 / 8, 14400 / 8);
213 cerr
<< "Could not create ENet host." << endl
;
216 comm_link_active
= true;
219 cout
<< "Hostname: " << host_name
<< endl
;
221 if (enet_address_set_host(&address
, host_name
)!=0) {
222 cerr
<< "Host '" << host_name
<< "' not found." << endl
;
227 peer
= enet_host_connect(host
, &address
, NUM_CHANNELS
);
229 cerr
<< "No available peers for initiating an ENet connection." << endl
;
232 /* Wait up to 5 seconds for the connection attempt to succeed. */
234 MESSAGE("Waiting five seconds for connection attempt to succeed");
235 if (enet_host_service (host
, & event
, 5000) > 0 &&
236 event
.type
== ENET_EVENT_TYPE_CONNECT
)
238 cout
<< "Connection to " << host_name
<< " succeeded." << endl
;
242 enet_peer_reset (peer
);
243 cerr
<< "Connection to " << host_name
<< " failed." << endl
;
248 char sversion
[CO_VERSION_MAX_LENGTH
];
249 commGetRel(sversion
, CO_VERSION_MAX_LENGTH
);
250 if(strncmp(sversion
, CO_VERSION
, CO_VERSION_MAX_LENGTH
)!=0) {
251 cerr
<< "Connected to incompatible version: " << sversion
<< endl
;
255 // server sends extremeness level
257 commGetRel(&X_level
, sizeof(X_level
));
258 if (X_level
== 1) MetaState::mode
|= CM_X
;
260 // for simplicity, client loses ties - but don't tell anyone
263 cout
<< "Connection made to " << peer
->address
.host
<< ':'
264 << peer
->address
.port
<< '.' << endl
;
268 startupExchange(player_name
);
271 no_communication
= false;
272 have_communicated
= false;
275 void Communicator::cleanUp ( )
277 if (comm_link_active
) {
279 comm_link_active
= false;
283 void Communicator::exchangeRandomSeed ( )
287 // server sends the the random seed
288 MESSAGE("Sending seed");
289 if (MetaState::mode
& CM_SERVER
) {
290 seed
= Random::generateSeed();
298 void Communicator::gameStart ( )
301 no_communication
= false;
302 have_communicated
= false;
305 send_garb_buffer
.count
= 0;
306 recv_garb_buffer
.count
= 0;
307 send_buffer
.level_lights
= 0;
308 recv_buffer
.level_lights
= 0;
309 send_buffer
.game_state
= 0;
311 exchangeRandomSeed();
314 void Communicator::gameFinish ( )
317 no_communication
= false;
318 have_communicated
= false;
321 void Communicator::handlePlayRecv ( )
326 while((code
= enet_host_service(host
, &event
, 0)) > 0) switch(event
.type
)
328 case ENET_EVENT_TYPE_RECEIVE
:
329 MESSAGE("Receiving message for channel " << (int)event
.channelID
<< "|"
330 << " len " << event
.packet
->dataLength
<< "|");
331 switch(event
.channelID
) {
332 case CO_CHANNEL_GARBAGE
:
333 handlePlayRecvGarbage(event
);
335 case CO_CHANNEL_STATUS
:
336 MESSAGE("Receiving status");
337 handlePlayRecvStatus (event
);
339 case CO_CHANNEL_BOARD
:
340 MESSAGE("Receiving board data (unimplemented)");
341 handlePlayRecvBoard (event
);
343 MESSAGE("Channel reliable used! Should not be during play!");
344 MESSAGE("Testing: Ignore leftovers?");
347 cerr
<< "Networking message handled incorrectly:" << endl
;
348 cerr
<< "Invalid channel id " << (int)event
.channelID
<< endl
;
351 enet_packet_destroy(event
.packet
);
353 case ENET_EVENT_TYPE_DISCONNECT
:
354 cerr
<< "Disconnected from host." << endl
;
357 case ENET_EVENT_TYPE_CONNECT
:
358 cerr
<< "Received an additional connection..." << endl
;
361 case ENET_EVENT_TYPE_NONE
:
362 MESSAGE("Nothing to see here... move along.");
366 cerr
<< "Unknown networking failure." << endl
;
371 void Communicator::handlePlayRecvBoard ( ENetEvent event
)
373 cerr
<< "Currently not handling board events." << endl
;
377 void Communicator::handlePlayRecvGarbage ( ENetEvent event
)
379 fillGarbageBuffer(event
.packet
);
381 // add new garbage to the queue
382 GarbageGenerator::addToQueue(recv_garb_buffer
);
385 void Communicator::handlePlayRecvStatus ( ENetEvent event
)
387 fillCommunicationBuffer(event
.packet
);
389 MESSAGE("Receiving other player status");
391 // handle the recved level light data
392 LevelLights::handleRecvBuffer();
394 // if we have been remotely paused
395 if (recv_buffer
.game_state
& GS_PAUSED
)
396 Game::netSignalPause();
398 // if we have been remotely unpaused
399 else if (recv_buffer
.game_state
& GS_UNPAUSED
)
400 Game::netSignalUnpause();
402 // store the current sync state
403 last_recv_sync
= recv_buffer
.sync
;
404 last_own_sync
= time_step
- Game::time_step
;
406 // if we're out of sync with our opponent, enter a sync pause
407 if (last_recv_sync
> last_own_sync
&& (Game::state
& GS_NORMAL
))
408 Game::syncPause(last_recv_sync
- last_own_sync
);
410 // if our opponent thinks he may have lost
411 if (recv_buffer
.game_state
& GS_MAY_HAVE_LOST
) {
412 MESSAGE("Opponent may have lost");
413 // if it's a concession
414 if (recv_buffer
.game_state
& GS_CONCESSION
) {
415 MetaState::state
|= MS_CONCESSION
;
416 MESSAGE("by concession");
419 // if we also think we may have lost
420 if (Game::state
& GS_MAY_HAVE_LOST
) {
421 MESSAGE("We also think we may have lost");
423 if (recv_buffer
.loss_time_stamp
< send_buffer
.loss_time_stamp
424 || (recv_buffer
.loss_time_stamp
== send_buffer
.loss_time_stamp
426 MESSAGE("Chose myself as the winner");
436 // if the opponent has confirmed our loss
437 } else if (recv_buffer
.game_state
& GS_MUST_CONFIRM_LOSS
) {
438 MESSAGE("Opponent has confirmed my loss");
439 Game::lossConfirmation();
440 no_communication
= true;
444 // if we were waiting a cycle for our opponent to recv his loss confirmation
445 if (Game::state
& GS_CONFIRMATION_HOLD
) {
446 Game::state
&= ~GS_CONFIRMATION_HOLD
;
447 no_communication
= true;
448 Game::state
|= GS_END_PLAY
;
453 void Communicator::timeStepPlay_inline_split_ ( )
456 if(have_communicated
) {
459 have_communicated
= true;
460 if(no_communication
) return;
466 void Communicator::handlePlaySend ( )
469 // ready the level light data for sending
470 LevelLights::readySendBuffer();
472 // ready the game state information for sending - pause and unpause
473 // information have already been set
474 if (Game::state
& GS_MAY_HAVE_LOST
) {
475 MESSAGE("May have lost");
476 send_buffer
.game_state
|= GS_MAY_HAVE_LOST
;
477 if (MetaState::state
& MS_CONCESSION
) {
478 MESSAGE("Conceeding");
479 send_buffer
.game_state
|= GS_CONCESSION
;
481 } else if (Game::state
& GS_MUST_CONFIRM_LOSS
) {
482 MESSAGE("Must confirm loss");
483 send_buffer
.game_state
|= GS_MUST_CONFIRM_LOSS
;
484 Game::state
&= ~GS_MUST_CONFIRM_LOSS
;
485 Game::state
|= GS_CONFIRMATION_HOLD
;
488 // ready the sync for sending
489 if (!(Game::state
& GS_PAUSED
))
490 send_buffer
.sync
= (uint32
) (time_step
- Game::time_step
);
496 if(send_garb_buffer
.count
> 0)
501 send_garb_buffer
.count
= 0;
502 send_buffer
.level_lights
= 0;
503 send_buffer
.game_state
= 0;
506 void Communicator::timeStepMeta_inline_split_ ( )
509 bool state_available
;
511 if(have_communicated
) {
512 state_available
= commGetRelNoBlock(state
);
513 if (state_available
) {
514 // handle recved state data
515 if (state
& MS_REMOTE_KEY_WAIT
)
516 MetaState::remoteKeyPressed();
517 else if (state
& MS_READY_GAME_START
)
518 MetaState::remoteReady();
521 have_communicated
= true;
523 if (MetaState::state
& MS_GAME_PLAY
) return;
525 // ready state data for sending
526 state
= MetaState::state
& (MS_REMOTE_KEY_WAIT
| MS_READY_GAME_START
);
531 void Communicator::barrier ( )
533 uint32 c
= CO_TEST_INT
;
538 assert(c
== CO_TEST_INT
);