Fixes for datatype size on amd64.
[crack-attack.git] / src / Communicator.cxx
blobc96cbb5e2951d7ef43af59d792243808984f391b
1 /*
2 * Communicator.cxx
3 * Daniel Nelson - 8/30/0
5 * Copyright (C) 2000 Daniel Nelson
6 * Copyright (C) 2004 Andrew Sayman
7 *
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
23 * 174 W. 18th Ave.
24 * Columbus, OH 43210
26 * Handles all that socket stuff. We should be more worried about the size of
27 * integers on various systems.
30 #include "Communicator.h"
32 #include <cassert>
33 #include <iostream>
34 #include <sys/types.h>
35 #include <cstring>
37 #ifndef _WIN32
38 # include <sys/socket.h>
39 # include <sys/poll.h>
40 # include <netinet/in.h>
41 # include <netdb.h>
42 # include <arpa/inet.h>
43 #else
44 # include <stdlib.h>
45 # include <winsock2.h>
46 # define sleep(x) Sleep(x)
47 #endif
49 #include "Game.h"
50 #include "Displayer.h"
51 #include "GarbageGenerator.h"
52 #include "GarbageFlavorImage.h"
53 #include "LevelLights.h"
54 #include "MetaState.h"
55 #include "Random.h"
57 using namespace std;
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 )
77 ip = ntohl(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] )
86 // exchange names
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();
94 commSendRel(flag);
96 // and send it out
97 if (flag) {
98 GLubyte *texture = GarbageFlavorImage::loadPersonalGarbageFlavorImage();
99 commSendRel(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
100 * sizeof(GLubyte));
101 #ifndef _WIN32
102 delete [] texture;
103 #else
104 if (texture != null) {
105 delete [] texture;
106 texture = null;
108 #endif
111 // check to see if they have a personal garbage flavor image
112 commGetRel(flag);
114 // and recv it
115 if (flag) {
116 GLubyte texture[4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH];
117 commGetRel(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
118 * sizeof(GLubyte));
119 GarbageFlavorImage::handleNetworkGarbageFlavorImage(texture);
123 void Communicator::initialize ( int mode, int port, char host_name[256],
124 char player_name[GC_PLAYER_NAME_LENGTH] )
126 ENetAddress address;
127 host = NULL;
128 peer = NULL;
129 comm_link_active = false;
131 if (enet_initialize () != 0)
133 cerr << "An error occurred while initializing ENet." << endl;
134 exit(1);
136 else
138 cout << "ENet properly initialized." << endl;
140 atexit (enet_deinitialize);
142 if (port == 0)
143 address.port = CO_DEFAULT_PORT;
144 else
145 address.port = port;
147 switch (mode & (CM_SERVER | CM_CLIENT)) {
148 case CM_SERVER: {
149 address.host = ENET_HOST_ANY;
151 host = enet_host_create(&address, 1, 0, 0);
152 if (NULL == host) {
153 cerr << "An error occured while trying to create the server" << endl;
154 exit(1);
157 cout << "Waiting for connection at port " << address.port << "..." << endl;
158 ENetEvent event;
159 do {
160 if (enet_host_service(host, &event, CO_SERVER_TIME_OUT * 1000) > 0)
162 switch (event.type) {
163 case ENET_EVENT_TYPE_CONNECT: {
164 peer = event.peer;
165 cout << "Accepting connection from ";
166 print_ip_address(peer->address.host);
167 cout << endl;
168 comm_link_active = true;
169 break;
171 case ENET_EVENT_TYPE_RECEIVE: {
172 enet_packet_destroy(event.packet);
173 break;
175 case ENET_EVENT_TYPE_DISCONNECT: {
176 event.peer->data = NULL;
177 break;
179 case ENET_EVENT_TYPE_NONE: {
180 break;
184 } while((mode & CM_NO_TIME_OUT) && !comm_link_active);
186 if (!comm_link_active)
188 cerr << "Time out." << endl;
189 exit(1);
192 // check version id
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
202 win_ties = true;
204 cout << "Connection made by ";
205 print_ip_address(peer->address.host);
206 cout << endl;
207 break;
210 case CM_CLIENT: {
211 host = enet_host_create(NULL, 1, 57600 / 8, 14400 / 8);
212 if(NULL == host) {
213 cerr << "Could not create ENet host." << endl;
214 exit(1);
216 comm_link_active = true;
218 #ifdef DEVELOPMENT
219 cout << "Hostname: " << host_name << endl;
220 #endif
221 if (enet_address_set_host(&address, host_name)!=0) {
222 cerr << "Host '" << host_name << "' not found." << endl;
223 exit(1);
227 peer = enet_host_connect(host, &address, NUM_CHANNELS);
228 if(NULL == peer) {
229 cerr << "No available peers for initiating an ENet connection." << endl;
230 exit(1);
232 /* Wait up to 5 seconds for the connection attempt to succeed. */
233 ENetEvent event;
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;
240 else
242 enet_peer_reset (peer);
243 cerr << "Connection to " << host_name << " failed." << endl;
244 exit(1);
247 // check version id
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;
252 exit(1);
255 // server sends extremeness level
256 enet_uint8 X_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
261 win_ties = false;
263 cout << "Connection made to " << peer->address.host << ':'
264 << peer->address.port << '.' << endl;
265 break;
268 startupExchange(player_name);
270 time_step = 1;
271 no_communication = false;
272 have_communicated = false;
275 void Communicator::cleanUp ( )
277 if (comm_link_active) {
278 enet_deinitialize();
279 comm_link_active = false;
283 void Communicator::exchangeRandomSeed ( )
285 uint32 seed;
287 // server sends the the random seed
288 MESSAGE("Sending seed");
289 if (MetaState::mode & CM_SERVER) {
290 seed = Random::generateSeed();
291 commSendRel(seed);
292 } else
293 commGetRel(seed);
295 Random::seed(seed);
298 void Communicator::gameStart ( )
300 time_step = 1;
301 no_communication = false;
302 have_communicated = false;
303 last_recv_sync = 0;
304 last_own_sync = 0;
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 ( )
316 time_step = 1;
317 no_communication = false;
318 have_communicated = false;
321 void Communicator::handlePlayRecv ( )
323 ENetEvent event;
324 int code;
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);
334 break;
335 case CO_CHANNEL_STATUS:
336 MESSAGE("Receiving status");
337 handlePlayRecvStatus (event);
338 break;
339 case CO_CHANNEL_BOARD:
340 MESSAGE("Receiving board data (unimplemented)");
341 handlePlayRecvBoard (event);
342 case CO_CHANNEL_REL:
343 MESSAGE("Channel reliable used! Should not be during play!");
344 MESSAGE("Testing: Ignore leftovers?");
345 break;
346 default:
347 cerr << "Networking message handled incorrectly:" << endl;
348 cerr << "Invalid channel id " << (int)event.channelID << endl;
349 exit(1);
351 enet_packet_destroy(event.packet);
352 break;
353 case ENET_EVENT_TYPE_DISCONNECT:
354 cerr << "Disconnected from host." << endl;
355 exit(1);
356 break;
357 case ENET_EVENT_TYPE_CONNECT:
358 cerr << "Received an additional connection..." << endl;
359 exit(1);
360 break;
361 case ENET_EVENT_TYPE_NONE:
362 MESSAGE("Nothing to see here... move along.");
363 break;
365 if (code < 0) {
366 cerr << "Unknown networking failure." << endl;
367 exit(1);
371 void Communicator::handlePlayRecvBoard ( ENetEvent event )
373 cerr << "Currently not handling board events." << endl;
374 exit(1);
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");
422 // pick a winner
423 if (recv_buffer.loss_time_stamp < send_buffer.loss_time_stamp
424 || (recv_buffer.loss_time_stamp == send_buffer.loss_time_stamp
425 && win_ties)) {
426 MESSAGE("Chose myself as the winner");
427 Game::won();
430 // otherwise, we win
431 } else {
432 MESSAGE("I win");
433 Game::won();
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;
441 return;
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;
449 return;
453 void Communicator::timeStepPlay_inline_split_ ( )
455 // recv
456 if(have_communicated) {
457 handlePlayRecv();
458 } else
459 have_communicated = true;
460 if(no_communication) return;
462 // send
463 handlePlaySend();
466 void Communicator::handlePlaySend ( )
468 // send
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);
492 #ifdef DEVELOPMENT
493 send_buffer.print();
494 #endif
496 if(send_garb_buffer.count > 0)
497 commSendGarbage();
498 commSendStatus();
500 // reset send buffer
501 send_garb_buffer.count = 0;
502 send_buffer.level_lights = 0;
503 send_buffer.game_state = 0;
506 void Communicator::timeStepMeta_inline_split_ ( )
508 uint32 state;
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();
520 } else
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);
528 commSendRel(state);
531 void Communicator::barrier ( )
533 uint32 c = CO_TEST_INT;
535 commSendRel(c);
536 commGetRel(c);
538 assert(c == CO_TEST_INT);