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