1 /*
2 * Copyright 2006 Serge van den Boom <svdb@stack.nl>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #define NETCONNECTION_INTERNAL
20 #include "netplay.h"
21 #include "netconnection.h"
22
23 #include "netrcv.h"
24
25 #if defined(DEBUG) || defined(NETPLAY_DEBUG)
26 # include "libs/log.h"
27 #endif
28 #if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
29 # include "options.h"
30 // for configDir
31 #endif
32
33 #include <assert.h>
34 #include <stdlib.h>
35 #if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
36 # include <errno.h>
37 # include <time.h>
38 #endif
39
40
41 static void closeCallback(NetDescriptor *nd);
42 static void NetConnection_doClose(NetConnection *conn);
43
44
45 #include "nc_connect.ci"
46
47 #if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
48 uio_Stream *netplayDebugFile;
49 #endif
50
51 // Used as initial value for Agreement structures, by structure assignment.
52 const Agreement Agreement_nothingAgreed;
53
54
55 // The NetConnection keeps a pointer to the passed NetplayPeerOptions;
56 // do not free it as long as the NetConnection exists.
57 NetConnection *
NetConnection_open(int player,const NetplayPeerOptions * options,NetConnection_ConnectCallback connectCallback,NetConnection_CloseCallback closeCallback,NetConnection_ErrorCallback errorCallback,NetConnection_DeleteCallback deleteCallback,void * extra)58 NetConnection_open(int player, const NetplayPeerOptions *options,
59 NetConnection_ConnectCallback connectCallback,
60 NetConnection_CloseCallback closeCallback,
61 NetConnection_ErrorCallback errorCallback,
62 NetConnection_DeleteCallback deleteCallback, void *extra) {
63 NetConnection *conn;
64
65 conn = malloc(sizeof (NetConnection));
66
67 #if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
68 {
69 char dumpFileName[PATH_MAX];
70 time_t now;
71 struct tm *nowTm;
72 size_t strftimeResult;
73
74 now = time (NULL);
75 if (now == (time_t) -1) {
76 log_add (log_Fatal, "time() failed: %s.", strerror (errno));
77 abort ();
78 }
79
80 nowTm = localtime(&now);
81 // XXX: I would like to use localtime_r(), but it isn't very
82 // portable (yet), and adding a check for it to the build.sh script
83 // is not worth the effort for a debugging function right now.
84
85 strftimeResult = strftime (dumpFileName, sizeof dumpFileName,
86 "debug/netlog-%Y%m%d%H%M%S", nowTm);
87 if (strftimeResult == 0) {
88 log_add (log_Fatal, "strftime() failed: %s.", strerror (errno));
89 abort ();
90 }
91
92 // The user needs to create the debug/ dir manually. If there
93 // is no debug/ dir, no log will be created.
94 conn->debugFile = uio_fopen (configDir, dumpFileName, "wt");
95 if (conn->debugFile == NULL) {
96 log_add (log_Debug, "Not creating a netplay debug log for "
97 "player %d.", player);
98 } else {
99 log_add (log_Debug, "Creating netplay debug log '%s' for "
100 "player %d.", dumpFileName, player);
101 if (netplayDebugFile == NULL) {
102 // Debug info relating to no specific network connection
103 // is sent to the first opened one.
104 netplayDebugFile = conn->debugFile;
105 }
106 }
107 }
108 #endif
109
110 conn->nd = NULL;
111 conn->player = player;
112 conn->state = NetState_unconnected;
113 conn->options = options;
114 conn->extra = extra;
115 PacketQueue_init(&conn->queue);
116
117 conn->connectCallback = connectCallback;
118 conn->closeCallback = closeCallback;
119 conn->errorCallback = errorCallback;
120 conn->deleteCallback = deleteCallback;
121 conn->readyCallback = NULL;
122 conn->readyCallbackArg = NULL;
123 conn->resetCallback = NULL;
124 conn->resetCallbackArg = NULL;
125
126 conn->readBuf = malloc(NETPLAY_READBUFSIZE);
127 conn->readEnd = conn->readBuf;
128
129 conn->stateData = NULL;
130 conn->stateFlags.connected = false;
131 conn->stateFlags.disconnected = false;
132 conn->stateFlags.discriminant = false;
133 conn->stateFlags.handshake.localOk = false;
134 conn->stateFlags.handshake.remoteOk = false;
135 conn->stateFlags.handshake.canceling = false;
136 conn->stateFlags.ready.localReady = false;
137 conn->stateFlags.ready.remoteReady = false;
138 conn->stateFlags.reset.localReset = false;
139 conn->stateFlags.reset.remoteReset = false;
140 conn->stateFlags.agreement = Agreement_nothingAgreed;
141 conn->stateFlags.inputDelay = 0;
142 #ifdef NETPLAY_CHECKSUM
143 conn->stateFlags.checksumInterval = NETPLAY_CHECKSUM_INTERVAL;
144 #endif
145
146 #ifdef NETPLAY_STATISTICS
147 {
148 size_t i;
149
150 conn->statistics.packetsReceived = 0;
151 conn->statistics.packetsSent = 0;
152 for (i = 0; i < PACKET_NUM; i++)
153 {
154 conn->statistics.packetTypeReceived[i] = 0;
155 conn->statistics.packetTypeSent[i] = 0;
156 }
157 }
158 #endif
159
160 NetConnection_go(conn);
161
162 return conn;
163 }
164
165 static void
NetConnection_doDeleteCallback(NetConnection * conn)166 NetConnection_doDeleteCallback(NetConnection *conn) {
167 if (conn->deleteCallback != NULL) {
168 //NetConnection_incRef(conn);
169 conn->deleteCallback(conn);
170 //NetConnection_decRef(conn);
171 }
172 }
173
174 static void
NetConnection_delete(NetConnection * conn)175 NetConnection_delete(NetConnection *conn) {
176 NetConnection_doDeleteCallback(conn);
177 if (conn->stateData != NULL) {
178 NetConnectionStateData_release(conn->stateData);
179 conn->stateData = NULL;
180 }
181 free(conn->readBuf);
182 PacketQueue_uninit(&conn->queue);
183
184 #ifdef NETPLAY_DEBUG_FILE
185 if (conn->debugFile != NULL) {
186 if (netplayDebugFile == conn->debugFile) {
187 // There may be other network connections, with an open
188 // debug file, but we don't know about that.
189 // The debugging person just has to work around that.
190 netplayDebugFile = NULL;
191 }
192 uio_fclose(conn->debugFile);
193 }
194 #endif
195
196 free(conn);
197 }
198
199 static void
Netplay_doCloseCallback(NetConnection * conn)200 Netplay_doCloseCallback(NetConnection *conn) {
201 if (conn->closeCallback != NULL) {
202 //NetConnection_incRef(conn);
203 conn->closeCallback(conn);
204 //NetConnection_decRef(conn);
205 }
206 }
207
208 // Auxiliary function for closing, used by both closeCallback() and
209 // NetConnection_close()
210 static void
NetConnection_doClose(NetConnection * conn)211 NetConnection_doClose(NetConnection *conn) {
212 conn->stateFlags.connected = false;
213 conn->stateFlags.disconnected = true;
214
215 // First the callback, so that it can still use the information
216 // of what is the current state, and the stateData:
217 Netplay_doCloseCallback(conn);
218
219 NetConnection_setState(conn, NetState_unconnected);
220 }
221
222 // Called when the NetDescriptor is shut down.
223 static void
closeCallback(NetDescriptor * nd)224 closeCallback(NetDescriptor *nd) {
225 NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
226 if (conn == NULL)
227 return;
228 conn->nd = NULL;
229 NetConnection_doClose(conn);
230 }
231
232 // Close and release a NetConnection.
233 void
NetConnection_close(NetConnection * conn)234 NetConnection_close(NetConnection *conn) {
235 if (conn->nd != NULL) {
236 NetDescriptor_setCloseCallback(conn->nd, NULL);
237 // We're not interested in the close callback of the
238 // NetDescriptor anymore.
239 NetDescriptor_close(conn->nd);
240 // This would queue the close callback.
241 conn->nd = NULL;
242 }
243 if (!conn->stateFlags.disconnected)
244 NetConnection_doClose(conn);
245 NetConnection_delete(conn);
246 }
247
248 void
NetConnection_doErrorCallback(NetConnection * nd,int err)249 NetConnection_doErrorCallback(NetConnection *nd, int err) {
250 NetConnectionError error;
251
252 if (nd->errorCallback != NULL) {
253 error.state = nd->state;
254 error.err = err;
255 }
256 (*nd->errorCallback)(nd, &error);
257 }
258
259 void
NetConnection_setStateData(NetConnection * conn,NetConnectionStateData * stateData)260 NetConnection_setStateData(NetConnection *conn,
261 NetConnectionStateData *stateData) {
262 conn->stateData = stateData;
263 }
264
265 NetConnectionStateData *
NetConnection_getStateData(const NetConnection * conn)266 NetConnection_getStateData(const NetConnection *conn) {
267 return conn->stateData;
268 }
269
270 void
NetConnection_setExtra(NetConnection * conn,void * extra)271 NetConnection_setExtra(NetConnection *conn, void *extra) {
272 conn->extra = extra;
273 }
274
275 void *
NetConnection_getExtra(const NetConnection * conn)276 NetConnection_getExtra(const NetConnection *conn) {
277 return conn->extra;
278 }
279
280 void
NetConnection_setReadyCallback(NetConnection * conn,NetConnection_ReadyCallback callback,void * arg)281 NetConnection_setReadyCallback(NetConnection *conn,
282 NetConnection_ReadyCallback callback, void *arg) {
283 conn->readyCallback = callback;
284 conn->readyCallbackArg = arg;
285 }
286
287 NetConnection_ReadyCallback
NetConnection_getReadyCallback(const NetConnection * conn)288 NetConnection_getReadyCallback(const NetConnection *conn) {
289 return conn->readyCallback;
290 }
291
292 void *
NetConnection_getReadyCallbackArg(const NetConnection * conn)293 NetConnection_getReadyCallbackArg(const NetConnection *conn) {
294 return conn->readyCallbackArg;
295 }
296
297 void
NetConnection_setResetCallback(NetConnection * conn,NetConnection_ResetCallback callback,void * arg)298 NetConnection_setResetCallback(NetConnection *conn,
299 NetConnection_ResetCallback callback, void *arg) {
300 conn->resetCallback = callback;
301 conn->resetCallbackArg = arg;
302 }
303
304 NetConnection_ResetCallback
NetConnection_getResetCallback(const NetConnection * conn)305 NetConnection_getResetCallback(const NetConnection *conn) {
306 return conn->resetCallback;
307 }
308
309 void *
NetConnection_getResetCallbackArg(const NetConnection * conn)310 NetConnection_getResetCallbackArg(const NetConnection *conn) {
311 return conn->resetCallbackArg;
312 }
313
314 void
NetConnection_setState(NetConnection * conn,NetState state)315 NetConnection_setState(NetConnection *conn, NetState state) {
316 #ifdef NETPLAY_DEBUG
317 log_add(log_Debug, "NETPLAY: [%d] +/- Connection state changed to: "
318 "%s.\n", conn->player, netStateData[state].name);
319 #endif
320 #ifdef DEBUG
321 if (state == conn->state) {
322 log_add(log_Warning, "NETPLAY: [%d] Connection state set to %s "
323 "while already in that state.\n",
324 conn->player, netStateData[state].name);
325 }
326 #endif
327 conn->state = state;
328 }
329
330 NetState
NetConnection_getState(const NetConnection * conn)331 NetConnection_getState(const NetConnection *conn) {
332 return conn->state;
333 }
334
335 bool
NetConnection_getDiscriminant(const NetConnection * conn)336 NetConnection_getDiscriminant(const NetConnection *conn) {
337 return conn->stateFlags.discriminant;
338 }
339
340 const NetplayPeerOptions *
NetConnection_getPeerOptions(const NetConnection * conn)341 NetConnection_getPeerOptions(const NetConnection *conn) {
342 return conn->options;
343 }
344
345 bool
NetConnection_isConnected(const NetConnection * conn)346 NetConnection_isConnected(const NetConnection *conn) {
347 return conn->stateFlags.connected;
348 }
349
350 int
NetConnection_getPlayerNr(const NetConnection * conn)351 NetConnection_getPlayerNr(const NetConnection *conn) {
352 return conn->player;
353 }
354
355 size_t
NetConnection_getInputDelay(const NetConnection * conn)356 NetConnection_getInputDelay(const NetConnection *conn) {
357 return conn->stateFlags.inputDelay;
358 }
359
360 #ifdef NETPLAY_CHECKSUM
361 ChecksumBuffer *
NetConnection_getChecksumBuffer(NetConnection * conn)362 NetConnection_getChecksumBuffer(NetConnection *conn) {
363 return &conn->checksumBuffer;
364 }
365
366 size_t
NetConnection_getChecksumInterval(const NetConnection * conn)367 NetConnection_getChecksumInterval(const NetConnection *conn) {
368 return conn->stateFlags.checksumInterval;
369 }
370 #endif /* NETPLAY_CHECKSUM */
371
372 #ifdef NETPLAY_STATISTICS
373 NetStatistics *
NetConnection_getStatistics(NetConnection * conn)374 NetConnection_getStatistics(NetConnection *conn) {
375 return &conn->statistics;
376 }
377 #endif
378
379