Game end works this way now:
[crack-attack.git] / src / Communicator.cxx
blob6569152d5c82fc837ff160b5ba316ce760f2aaba
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 <stdlib.h>
44 # include <winsock2.h>
45 # define sleep(x) Sleep(x)
46 #endif
48 using namespace std;
50 #include "Game.h"
51 #include "Communicator.h"
52 #include "Displayer.h"
53 #include "GarbageGenerator.h"
54 #include "GarbageFlavorImage.h"
55 #include "LevelLights.h"
56 #include "MetaState.h"
57 #include "Random.h"
59 int Communicator::comm_link;
60 int Communicator::time_step;
61 bool Communicator::comm_link_active;
62 bool Communicator::no_communication;
63 bool Communicator::have_communicated;
64 int Communicator::last_recv_sync;
65 int Communicator::last_own_sync;
66 CommunicationBuffer Communicator::send_buffer;
67 CommunicationBuffer Communicator::recv_buffer;
68 CommunicationBuffer Communicator::work_buffer;
69 bool Communicator::win_ties;
70 char Communicator::opponent_name[GC_PLAYER_NAME_LENGTH];
72 void Communicator::startupExchange ( char player_name[GC_PLAYER_NAME_LENGTH] )
74 // exchange names
75 commSend(player_name, GC_PLAYER_NAME_LENGTH);
76 commRecv(opponent_name, GC_PLAYER_NAME_LENGTH);
78 // notify if we have a personal garbage flavor image
79 uint32 flag = GarbageFlavorImage::personalGarbageFlavorImageExists();
80 commSend(flag);
82 // and send it out
83 if (flag) {
84 GLubyte *texture = GarbageFlavorImage::loadPersonalGarbageFlavorImage();
85 commSend(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
86 * sizeof(GLubyte));
87 #ifndef _WIN32
88 delete [] texture;
89 #else
90 if (texture != null) {
91 delete [] texture;
92 texture = null;
94 #endif
97 // check to see if they have a personal garbage flavor image
98 commRecv(flag);
100 // and recv it
101 if (flag) {
102 GLubyte texture[4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH];
103 commRecv(texture, 4 * DC_GARBAGE_TEX_LENGTH * DC_GARBAGE_TEX_LENGTH
104 * sizeof(GLubyte));
105 GarbageFlavorImage::handleNetworkGarbageFlavorImage(texture);
109 void Communicator::initialize ( int mode, int port, char host_name[256],
110 char player_name[GC_PLAYER_NAME_LENGTH] )
112 comm_link_active = false;
114 #ifndef _WIN32
115 #else
116 WSAData wsa_data;
117 if (WSAStartup(MAKEWORD(1, 1), &wsa_data) != 0) {
118 cerr << "Winsock startup failed." << endl;
119 exit(1);
121 #endif
123 if (port == 0)
124 port = CO_DEFAULT_PORT;
126 switch (mode & (CM_SERVER | CM_CLIENT)) {
127 case CM_SERVER: {
128 int connection_socket = socket(AF_INET, SOCK_STREAM, 0);
130 sockaddr_in address;
131 #ifndef _WIN32
132 int val = 1;
133 setsockopt (connection_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (int));
134 #endif
135 address.sin_family = AF_INET;
136 address.sin_addr.s_addr = htonl(INADDR_ANY);
137 address.sin_port = htons(port);
139 if (bind(connection_socket, (sockaddr *) &address, sizeof(address)) < 0) {
140 cerr << "Port " << port << " is busy." << endl;
141 exit(1);
144 cout << "Waiting for connection at port " << port << "..." << endl;
145 listen(connection_socket, 1);
147 #ifndef _WIN32
148 if (!(mode & CM_NO_TIME_OUT)) {
149 pollfd ufds;
150 ufds.fd = connection_socket;
151 ufds.events = POLLIN;
153 if (!poll(&ufds, 1, CO_SERVER_TIME_OUT * 1000)) {
154 cerr << "Timed out." << endl;
155 exit(1);
158 #endif
160 #ifndef _WIN32
161 unsigned int length = sizeof(address);
162 #else
163 int length = sizeof(address);
164 #endif
165 comm_link = accept(connection_socket, (sockaddr *) &address, &length);
166 comm_link_active = true;
168 // check version id
169 uint32 version_id = CO_TEST_INT;
170 for (char *c = CO_VERSION; *c; c++)
171 version_id += *c;
172 #ifdef DEVELOPMENT
173 cout << "Version id: " << version_id << endl;
174 #endif
175 version_id = htonl(version_id);
176 if (send(comm_link, (char *) &version_id, sizeof(version_id), 0) < 1) {
177 cerr << "Connection failed." << endl;
178 exit(1);
181 // server sends extremeness level
182 uint32 X_level = ((mode & CM_X) ? 1 : 0);
183 commSend(X_level);
185 // for simplicity, server wins ties - but don't tell anyone; it's the only
186 // available symmetry breaking term
187 win_ties = true;
189 cout << "Connection made by " << inet_ntoa(address.sin_addr) << '.' << endl;
190 break;
193 case CM_CLIENT: {
194 comm_link = socket(AF_INET, SOCK_STREAM, 0);
195 comm_link_active = true;
197 #ifdef DEVELOPMENT
198 cout << "Hostname: " << host_name << endl;
199 #endif
200 hostent *host = gethostbyname(host_name);
201 if (!host) {
202 cerr << "Host '" << host_name << "' not found." << endl;
203 exit(1);
206 sockaddr_in address;
207 address.sin_family = AF_INET;
208 address.sin_addr = *(struct in_addr *) host->h_addr;
209 address.sin_port = htons((short) port);
210 if (connect(comm_link, (sockaddr *) &address, sizeof(address)) < 0) {
211 cerr << "Connection failed. Unable to connect to address." << endl;
212 exit(1);
215 // check version id
216 uint32 version_id = CO_TEST_INT;
217 for (char *c = CO_VERSION; *c; c++)
218 version_id += *c;
219 #ifdef DEVELOPMENT
220 cout << "Version id: " << version_id << endl;
221 #endif
222 uint32 server_version_id;
223 if (recv(comm_link, (char *) &server_version_id, sizeof(server_version_id),
224 0) != sizeof(server_version_id)) {
225 cerr << "Connection failed. Unable to read version information." << endl;
226 exit(1);
228 if (ntohl(server_version_id) != version_id) {
229 cerr << "Connected to incompatible version." << endl;
230 exit(1);
233 // server sends extremeness level
234 uint32 X_level;
235 commRecv(X_level);
236 if (X_level == 1) MetaState::mode |= CM_X;
238 // for simplicity, client loses ties - but don't tell anyone
239 win_ties = false;
241 cout << "Connection made to " << inet_ntoa(address.sin_addr) << ':'
242 << (short) port << '.' << endl;
243 break;
246 startupExchange(player_name);
248 time_step = 1;
249 no_communication = false;
250 have_communicated = false;
253 void Communicator::cleanUp ( )
255 if (comm_link_active) {
256 /* One solution to avoid "port is busy" after a game
257 is to have the client initiate the closure, so let's
258 wait a few seconds if we're the server.
259 http://hea-www.harvard.edu/~fine/Tech/addrinuse.html
261 if (MetaState::mode & CM_SERVER)
262 sleep(1);
263 #ifndef _WIN32
264 close(comm_link);
265 #else
266 closesocket(comm_link);
267 #endif
268 comm_link_active = false;
272 void Communicator::exchangeRandomSeed ( )
274 uint32 seed;
276 // server sends the the random seed
277 if (MetaState::mode & CM_SERVER) {
278 seed = Random::generateSeed();
279 commSend(seed);
280 } else
281 commRecv(seed);
283 Random::seed(seed);
286 void Communicator::gameStart ( )
288 time_step = 1;
289 no_communication = false;
290 have_communicated = false;
291 last_recv_sync = 0;
292 last_own_sync = 0;
293 send_buffer.count = 0;
294 recv_buffer.count = 0;
295 send_buffer.level_lights = 0;
296 recv_buffer.level_lights = 0;
297 send_buffer.game_state = 0;
299 exchangeRandomSeed();
302 void Communicator::gameFinish ( )
304 time_step = 1;
305 no_communication = false;
306 have_communicated = false;
309 void Communicator::timeStepPlay_inline_split_ ( )
311 // recv
313 if (have_communicated) {
314 commRecv(recv_buffer);
316 // add new garbage to the queue
317 GarbageGenerator::addToQueue(recv_buffer);
319 // handle the recved level light data
320 LevelLights::handleRecvBuffer();
322 // if we have been remotely paused
323 if (recv_buffer.game_state & GS_PAUSED)
324 Game::netSignalPause();
326 // if we have been remotely unpaused
327 else if (recv_buffer.game_state & GS_UNPAUSED)
328 Game::netSignalUnpause();
330 // store the current sync state
331 last_recv_sync = recv_buffer.sync;
332 last_own_sync = time_step - Game::time_step;
334 // if we're out of sync with our opponent, enter a sync pause
335 if (last_recv_sync > last_own_sync && (Game::state & GS_NORMAL))
336 Game::syncPause(last_recv_sync - last_own_sync);
338 // if our opponent thinks he may have lost
339 if (recv_buffer.game_state & GS_MAY_HAVE_LOST) {
341 // if it's a concession
342 if (recv_buffer.game_state & GS_CONCESSION)
343 MetaState::state |= MS_CONCESSION;
345 // if we also think we may have lost
346 if (Game::state & GS_MAY_HAVE_LOST) {
348 // pick a winner
349 if (recv_buffer.loss_time_stamp < send_buffer.loss_time_stamp
350 || (recv_buffer.loss_time_stamp == send_buffer.loss_time_stamp
351 && win_ties))
352 Game::won();
354 // otherwise, we win
355 } else
356 Game::won();
358 // if the opponent has confirmed our loss
359 } else if (recv_buffer.game_state & GS_MUST_CONFIRM_LOSS) {
360 Game::lossConfirmation();
361 no_communication = true;
362 return;
365 // if we were waiting a cycle for our opponent to recv his loss confirmation
366 if (Game::state & GS_CONFIRMATION_HOLD) {
367 Game::state &= ~GS_CONFIRMATION_HOLD;
368 no_communication = true;
369 Game::state |= GS_END_PLAY;
370 return;
372 } else
373 have_communicated = true;
375 // send
377 // ready the level light data for sending
378 LevelLights::readySendBuffer();
380 // ready the game state information for sending - pause and unpause
381 // information have already been set
382 if (Game::state & GS_MAY_HAVE_LOST) {
383 send_buffer.game_state |= GS_MAY_HAVE_LOST;
384 if (MetaState::state & MS_CONCESSION)
385 send_buffer.game_state |= GS_CONCESSION;
386 } else if (Game::state & GS_MUST_CONFIRM_LOSS) {
387 send_buffer.game_state |= GS_MUST_CONFIRM_LOSS;
388 Game::state &= ~GS_MUST_CONFIRM_LOSS;
389 Game::state |= GS_CONFIRMATION_HOLD;
392 // ready the sync for sending
393 send_buffer.sync = (uint32) (time_step - Game::time_step);
395 commSend(send_buffer);
397 // reset send buffer
398 send_buffer.count = 0;
399 send_buffer.level_lights = 0;
400 send_buffer.game_state = 0;
403 void Communicator::timeStepMeta_inline_split_ ( )
405 uint32 state;
407 if (have_communicated) {
408 commRecv(state);
410 // handle recved state data
411 if (state & MS_REMOTE_KEY_WAIT)
412 MetaState::remoteKeyPressed();
413 else if (state & MS_READY_GAME_START)
414 MetaState::remoteReady();
416 if (MetaState::state & MS_GAME_PLAY) return;
418 } else
419 have_communicated = true;
421 // ready state data for sending
422 state = MetaState::state & (MS_REMOTE_KEY_WAIT | MS_READY_GAME_START);
424 commSend(state);
427 void Communicator::barrier ( )
429 uint32 c = CO_TEST_INT;
431 commSend(c);
432 commRecv(c);
434 assert(c == CO_TEST_INT);