- Fixed gui bug where unchecking extremely low undid low
[crack-attack.git] / src / Communicator.cxx
blob06fac78b8c4d9d427ca8ac3f7ca5736e5335c8cf
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 <cassert>
31 #include <iostream>
32 #include <sys/types.h>
33 #include <cstring>
35 #ifndef _WIN32
36 # include <unistd.h>
37 # include <sys/socket.h>
38 # include <sys/poll.h>
39 # include <netinet/in.h>
40 # include <netdb.h>
41 # include <arpa/inet.h>
42 #else
43 # include <winsock2.h>
44 #endif
46 using namespace std;
48 #include "Game.h"
49 #include "Communicator.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 int Communicator::comm_link;
58 int Communicator::time_step;
59 bool Communicator::comm_link_active;
60 bool Communicator::no_communication;
61 bool Communicator::have_communicated;
62 int Communicator::last_recv_sync;
63 int Communicator::last_own_sync;
64 CommunicationBuffer Communicator::send_buffer;
65 CommunicationBuffer Communicator::recv_buffer;
66 CommunicationBuffer Communicator::work_buffer;
67 bool Communicator::win_ties;
68 char Communicator::opponent_name[GC_PLAYER_NAME_LENGTH];
70 void Communicator::startupExchange ( char player_name[GC_PLAYER_NAME_LENGTH] )
72 // exchange names
73 commSend(player_name, GC_PLAYER_NAME_LENGTH);
74 commRecv(opponent_name, GC_PLAYER_NAME_LENGTH);
76 // notify if we have a personal garbage flavor image
77 uint32 flag = GarbageFlavorImage::personalGarbageFlavorImageExists();
78 commSend(flag);
80 // and send it out
81 if (flag) {
82 GLubyte *texture = GarbageFlavorImage::loadPersonalGarbageFlavorImage();
83 commSend(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
84 * sizeof(GLubyte));
85 #ifndef _WIN32
86 delete [] texture;
87 #else
88 if (texture != null) {
89 delete [] texture;
90 texture = null;
92 #endif
95 // check to see if they have a personal garbage flavor image
96 commRecv(flag);
98 // and recv it
99 if (flag) {
100 GLubyte texture[4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH];
101 commRecv(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
102 * sizeof(GLubyte));
103 GarbageFlavorImage::handleNetworkGarbageFlavorImage(texture);
107 void Communicator::initialize ( int mode, int port, char host_name[256],
108 char player_name[GC_PLAYER_NAME_LENGTH] )
110 comm_link_active = false;
112 #ifndef _WIN32
113 #else
114 WSAData wsa_data;
115 if (WSAStartup(MAKEWORD(1, 1), &wsa_data) != 0) {
116 cerr << "Winsock startup failed." << endl;
117 exit(1);
119 #endif
121 if (port == 0)
122 port = CO_DEFAULT_PORT;
124 switch (mode & (CM_SERVER | CM_CLIENT)) {
125 case CM_SERVER: {
126 int connection_socket = socket(AF_INET, SOCK_STREAM, 0);
128 sockaddr_in address;
129 int val = 1;
130 setsockopt (connection_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (int));
131 address.sin_family = AF_INET;
132 address.sin_addr.s_addr = htonl(INADDR_ANY);
133 address.sin_port = htons(port);
135 if (bind(connection_socket, (sockaddr *) &address, sizeof(address)) < 0) {
136 cerr << "Port " << port << " is busy." << endl;
137 exit(1);
140 cout << "Waiting for connection at port " << port << "..." << endl;
141 listen(connection_socket, 1);
143 #ifndef _WIN32
144 if (!(mode & CM_NO_TIME_OUT)) {
145 pollfd ufds;
146 ufds.fd = connection_socket;
147 ufds.events = POLLIN;
149 if (!poll(&ufds, 1, CO_SERVER_TIME_OUT * 1000)) {
150 cerr << "Timed out." << endl;
151 exit(1);
154 #endif
156 #ifndef _WIN32
157 unsigned int length = sizeof(address);
158 #else
159 int length = sizeof(address);
160 #endif
161 comm_link = accept(connection_socket, (sockaddr *) &address, &length);
162 comm_link_active = true;
164 // check version id
165 uint32 version_id = CO_TEST_INT;
166 for (char *c = CO_VERSION; *c; c++)
167 version_id += *c;
168 #ifdef DEVELOPMENT
169 cout << "Version id: " << version_id << endl;
170 #endif
171 version_id = htonl(version_id);
172 if (send(comm_link, (char *) &version_id, sizeof(version_id), 0) < 1) {
173 cerr << "Connection failed." << endl;
174 exit(1);
177 // server sends extremeness level
178 uint32 X_level = ((mode & CM_X) ? 1 : 0);
179 commSend(X_level);
181 // for simplicity, server wins ties - but don't tell anyone; it's the only
182 // available symmetry breaking term
183 win_ties = true;
185 cout << "Connection made by " << inet_ntoa(address.sin_addr) << '.' << endl;
186 break;
189 case CM_CLIENT: {
190 comm_link = socket(AF_INET, SOCK_STREAM, 0);
191 comm_link_active = true;
193 #ifdef DEVELOPMENT
194 cout << "Hostname: " << host_name << endl;
195 #endif
196 hostent *host = gethostbyname(host_name);
197 if (!host) {
198 cerr << "Host '" << host_name << "' not found." << endl;
199 exit(1);
202 sockaddr_in address;
203 address.sin_family = AF_INET;
204 address.sin_addr = *(struct in_addr *) host->h_addr;
205 address.sin_port = htons((short) port);
206 if (connect(comm_link, (sockaddr *) &address, sizeof(address)) < 0) {
207 cerr << "Connection failed. Unable to connect to address." << endl;
208 exit(1);
211 // check version id
212 uint32 version_id = CO_TEST_INT;
213 for (char *c = CO_VERSION; *c; c++)
214 version_id += *c;
215 #ifdef DEVELOPMENT
216 cout << "Version id: " << version_id << endl;
217 #endif
218 uint32 server_version_id;
219 if (recv(comm_link, (char *) &server_version_id, sizeof(server_version_id),
220 0) != sizeof(server_version_id)) {
221 cerr << "Connection failed. Unable to read version information." << endl;
222 exit(1);
224 if (ntohl(server_version_id) != version_id) {
225 cerr << "Connected to incompatible version." << endl;
226 exit(1);
229 // server sends extremeness level
230 uint32 X_level;
231 commRecv(X_level);
232 if (X_level == 1) MetaState::mode |= CM_X;
234 // for simplicity, client loses ties - but don't tell anyone
235 win_ties = false;
237 cout << "Connection made to " << inet_ntoa(address.sin_addr) << ':'
238 << (short) port << '.' << endl;
239 break;
242 startupExchange(player_name);
244 time_step = 1;
245 no_communication = false;
246 have_communicated = false;
249 void Communicator::cleanUp ( )
251 if (comm_link_active) {
252 /* One solution to avoid "port is busy" after a game
253 is to have the client initiate the closure, so let's
254 wait a few seconds if we're the server.
255 http://hea-www.harvard.edu/~fine/Tech/addrinuse.html
257 if (MetaState::mode & CM_SERVER)
258 sleep(1);
259 #ifndef _WIN32
260 close(comm_link);
261 #else
262 closesocket(comm_link);
263 #endif
264 comm_link_active = false;
268 void Communicator::exchangeRandomSeed ( )
270 uint32 seed;
272 // server sends the the random seed
273 if (MetaState::mode & CM_SERVER) {
274 seed = Random::generateSeed();
275 commSend(seed);
276 } else
277 commRecv(seed);
279 Random::seed(seed);
282 void Communicator::gameStart ( )
284 time_step = 1;
285 no_communication = false;
286 have_communicated = false;
287 last_recv_sync = 0;
288 last_own_sync = 0;
289 send_buffer.count = 0;
290 recv_buffer.count = 0;
291 send_buffer.level_lights = 0;
292 recv_buffer.level_lights = 0;
293 send_buffer.game_state = 0;
295 exchangeRandomSeed();
298 void Communicator::gameFinish ( )
300 time_step = 1;
301 no_communication = false;
302 have_communicated = false;
305 void Communicator::timeStepPlay_inline_split_ ( )
307 // recv
309 if (have_communicated) {
310 commRecv(recv_buffer);
312 // add new garbage to the queue
313 GarbageGenerator::addToQueue(recv_buffer);
315 // handle the recved level light data
316 LevelLights::handleRecvBuffer();
318 // if we have been remotely paused
319 if (recv_buffer.game_state & GS_PAUSED)
320 Game::netSignalPause();
322 // if we have been remotely unpaused
323 else if (recv_buffer.game_state & GS_UNPAUSED)
324 Game::netSignalUnpause();
326 // store the current sync state
327 last_recv_sync = recv_buffer.sync;
328 last_own_sync = time_step - Game::time_step;
330 // if we're out of sync with our opponent, enter a sync pause
331 if (last_recv_sync > last_own_sync && (Game::state & GS_NORMAL))
332 Game::syncPause(last_recv_sync - last_own_sync);
334 // if our opponent thinks he may have lost
335 if (recv_buffer.game_state & GS_MAY_HAVE_LOST) {
337 // if it's a concession
338 if (recv_buffer.game_state & GS_CONCESSION)
339 MetaState::state |= MS_CONCESSION;
341 // if we also think we may have lost
342 if (Game::state & GS_MAY_HAVE_LOST) {
344 // pick a winner
345 if (recv_buffer.loss_time_stamp < send_buffer.loss_time_stamp
346 || (recv_buffer.loss_time_stamp == send_buffer.loss_time_stamp
347 && win_ties))
348 Game::won();
350 // otherwise, we win
351 } else
352 Game::won();
354 // if the opponent has confirmed our loss
355 } else if (recv_buffer.game_state & GS_MUST_CONFIRM_LOSS) {
356 Game::lossConfirmation();
357 no_communication = true;
358 return;
361 // if we were waiting a cycle for our opponent to recv his loss confirmation
362 if (Game::state & GS_CONFIRMATION_HOLD) {
363 Game::state &= ~GS_CONFIRMATION_HOLD;
364 no_communication = true;
365 Game::state |= GS_END_PLAY;
366 return;
368 } else
369 have_communicated = true;
371 // send
373 // ready the level light data for sending
374 LevelLights::readySendBuffer();
376 // ready the game state information for sending - pause and unpause
377 // information have already been set
378 if (Game::state & GS_MAY_HAVE_LOST) {
379 send_buffer.game_state |= GS_MAY_HAVE_LOST;
380 if (MetaState::state & MS_CONCESSION)
381 send_buffer.game_state |= GS_CONCESSION;
382 } else if (Game::state & GS_MUST_CONFIRM_LOSS) {
383 send_buffer.game_state |= GS_MUST_CONFIRM_LOSS;
384 Game::state &= ~GS_MUST_CONFIRM_LOSS;
385 Game::state |= GS_CONFIRMATION_HOLD;
388 // ready the sync for sending
389 send_buffer.sync = (uint32) (time_step - Game::time_step);
391 commSend(send_buffer);
393 // reset send buffer
394 send_buffer.count = 0;
395 send_buffer.level_lights = 0;
396 send_buffer.game_state = 0;
399 void Communicator::timeStepMeta_inline_split_ ( )
401 uint32 state;
403 if (have_communicated) {
404 commRecv(state);
406 // handle recved state data
407 if (state & MS_REMOTE_KEY_WAIT)
408 MetaState::remoteKeyPressed();
409 else if (state & MS_READY_GAME_START)
410 MetaState::remoteReady();
412 if (MetaState::state & MS_GAME_PLAY) return;
414 } else
415 have_communicated = true;
417 // ready state data for sending
418 state = MetaState::state & (MS_REMOTE_KEY_WAIT | MS_READY_GAME_START);
420 commSend(state);
423 void Communicator::barrier ( )
425 uint32 c = CO_TEST_INT;
427 commSend(c);
428 commRecv(c);
430 assert(c == CO_TEST_INT);