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 #ifdef NETPLAY
20 
21 #include "checksum.h"
22 #include "netoptions.h"
23 
24 #ifdef NETPLAY_CHECKSUM
25 
26 #include "checkbuf.h"
27 #include "crc.h"
28 		// for DUMP_CRC_OPS
29 #include "netconnection.h"
30 #include "netmelee.h"
31 #include "libs/log.h"
32 #include "libs/mathlib.h"
33 
34 ChecksumBuffer localChecksumBuffer;
35 
36 void
crc_processEXTENT(crc_State * state,const EXTENT * val)37 crc_processEXTENT(crc_State *state, const EXTENT *val) {
38 #ifdef DUMP_CRC_OPS
39 	crc_log("START crc_processEXTENT().");
40 #endif
41 	crc_processCOORD(state, val->width);
42 	crc_processCOORD(state, val->height);
43 #ifdef DUMP_CRC_OPS
44 	crc_log("END   crc_processEXTENT().");
45 #endif
46 }
47 
48 void
crc_processVELOCITY_DESC(crc_State * state,const VELOCITY_DESC * val)49 crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val) {
50 #ifdef DUMP_CRC_OPS
51 	crc_log("START crc_processVELOCITY_DESC().");
52 #endif
53 	crc_processCOUNT(state, val->TravelAngle);
54 	crc_processEXTENT(state, &val->vector);
55 	crc_processEXTENT(state, &val->fract);
56 	crc_processEXTENT(state, &val->error);
57 	crc_processEXTENT(state, &val->incr);
58 #ifdef DUMP_CRC_OPS
59 	crc_log("END   crc_processVELOCITY_DESC().");
60 #endif
61 }
62 
63 void
crc_processPOINT(crc_State * state,const POINT * val)64 crc_processPOINT(crc_State *state, const POINT *val) {
65 #ifdef DUMP_CRC_OPS
66 	crc_log("START crc_processPOINT().");
67 #endif
68 	crc_processCOORD(state, val->x);
69 	crc_processCOORD(state, val->y);
70 #ifdef DUMP_CRC_OPS
71 	crc_log("END   crc_processPOINT().");
72 #endif
73 }
74 
75 #if 0
76 void
77 crc_processSTAMP(crc_State *state, const STAMP *val) {
78 #ifdef DUMP_CRC_OPS
79 	crc_log("START crc_processSTAMP().");
80 #endif
81 	crc_processPOINT(state, val->origin);
82 	crc_processFRAME(state, val->frame);
83 #ifdef DUMP_CRC_OPS
84 	crc_log("END   crc_processSTAMP().");
85 #endif
86 }
87 
88 void
89 crc_processINTERSECT_CONTROL(crc_State *state, const INTERSECT_CONTROL *val) {
90 #ifdef DUMP_CRC_OPS
91 	crc_log("START crc_processINTERSECT_CONTROL().");
92 #endif
93 	crc_processTIME_VALUE(state, val->last_time_val);
94 	crc_processPOINT(state, &val->EndPoint);
95 #ifdef DUMP_CRC_OPS
96 	crc_log("END   crc_processINTERSECT_CONTROL().");
97 #endif
98 }
99 #endif
100 
101 void
crc_processSTATE(crc_State * state,const STATE * val)102 crc_processSTATE(crc_State *state, const STATE *val) {
103 	crc_processPOINT(state, &val->location);
104 }
105 
106 void
crc_processELEMENT(crc_State * state,const ELEMENT * val)107 crc_processELEMENT(crc_State *state, const ELEMENT *val) {
108 #ifdef DUMP_CRC_OPS
109 	crc_log("START crc_processELEMENT().");
110 #endif
111 	if (val->state_flags & BACKGROUND_OBJECT) {
112 		// The element never influences the state of other elements,
113 		// and is to be excluded from checksums.
114 #ifdef DUMP_CRC_OPS
115 		crc_log("      BACKGROUND_OBJECT element omited");
116 #endif
117 	} else {
118 		crc_processELEMENT_FLAGS(state, val->state_flags);
119 		crc_processCOUNT(state, val->life_span);
120 		crc_processCOUNT(state, val->crew_level);
121 		crc_processBYTE(state, val->mass_points);
122 		crc_processBYTE(state, val->turn_wait);
123 		crc_processBYTE(state, val->thrust_wait);
124 		crc_processVELOCITY_DESC(state, &val->velocity);
125 		crc_processSTATE(state, &val->current);
126 		crc_processSTATE(state, &val->next);
127 	}
128 #ifdef DUMP_CRC_OPS
129 	crc_log("END   crc_processELEMENT().");
130 #endif
131 }
132 
133 void
crc_processDispQueue(crc_State * state)134 crc_processDispQueue(crc_State *state) {
135 	HELEMENT element;
136 	HELEMENT nextElement;
137 
138 #ifdef DUMP_CRC_OPS
139 	size_t i = 0;
140 	crc_log("START crc_processDispQueue().");
141 #endif
142 	for (element = GetHeadElement(); element != 0; element = nextElement) {
143 		ELEMENT *elementPtr;
144 
145 #ifdef DUMP_CRC_OPS
146 		crc_log("===== disp_q[%d]:", i);
147 #endif
148 		LockElement(element, &elementPtr);
149 
150 		crc_processELEMENT(state, elementPtr);
151 
152 		nextElement = GetSuccElement(elementPtr);
153 		UnlockElement(element);
154 #ifdef DUMP_CRC_OPS
155 		i++;
156 #endif
157 	}
158 #ifdef DUMP_CRC_OPS
159 	crc_log("END   crc_processDispQueue().");
160 #endif
161 }
162 
163 void
crc_processRNG(crc_State * state)164 crc_processRNG(crc_State *state) {
165 	DWORD seed;
166 
167 #ifdef DUMP_CRC_OPS
168 	crc_log("START crc_processRNG().");
169 #endif
170 
171 	seed = TFB_SeedRandom(0);
172 			// This modifies the seed too.
173 	crc_processDWORD(state, seed);
174 	TFB_SeedRandom(seed);
175 			// Restore the old seed.
176 
177 #ifdef DUMP_CRC_OPS
178 	crc_log("END   crc_processRNG().");
179 #endif
180 }
181 
182 void
crc_processState(crc_State * state)183 crc_processState(crc_State *state) {
184 #ifdef DUMP_CRC_OPS
185 	crc_log("--------------------\n"
186 			"START crc_processState() (frame %u).", battleFrameCount);
187 #endif
188 
189 	crc_processRNG(state);
190 	crc_processDispQueue(state);
191 
192 #ifdef DUMP_CRC_OPS
193 	crc_log("END   crc_processState() (frame %u).",
194 			battleFrameCount);
195 #endif
196 }
197 
198 void
initChecksumBuffers(void)199 initChecksumBuffers(void) {
200 	size_t player;
201 
202 	for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
203 	{
204 		NetConnection *conn;
205 		ChecksumBuffer *cb;
206 
207 		conn = netConnections[player];
208 		if (conn == NULL)
209 			continue;
210 
211 		cb = NetConnection_getChecksumBuffer(conn);
212 		ChecksumBuffer_init(cb, getBattleInputDelay(),
213 				NETPLAY_CHECKSUM_INTERVAL);
214 	}
215 
216 	ChecksumBuffer_init(&localChecksumBuffer, getBattleInputDelay(),
217 			NETPLAY_CHECKSUM_INTERVAL);
218 }
219 
220 void
uninitChecksumBuffers(void)221 uninitChecksumBuffers(void)
222 {
223 	size_t player;
224 
225 	for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
226 	{
227 		NetConnection *conn;
228 		ChecksumBuffer *cb;
229 
230 		conn = netConnections[player];
231 		if (conn == NULL)
232 			continue;
233 
234 		cb = NetConnection_getChecksumBuffer(conn);
235 
236 		ChecksumBuffer_uninit(cb);
237 	}
238 
239 	ChecksumBuffer_uninit(&localChecksumBuffer);
240 }
241 
242 void
addLocalChecksum(BattleFrameCounter frameNr,Checksum checksum)243 addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum) {
244 	assert(frameNr == battleFrameCount);
245 
246 	ChecksumBuffer_addChecksum(&localChecksumBuffer, frameNr, checksum);
247 }
248 
249 void
addRemoteChecksum(NetConnection * conn,BattleFrameCounter frameNr,Checksum checksum)250 addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
251 		Checksum checksum) {
252 	ChecksumBuffer *cb;
253 
254 	assert(frameNr <= battleFrameCount + getBattleInputDelay() + 1);
255 	assert(frameNr + getBattleInputDelay() >= battleFrameCount);
256 
257 	cb = NetConnection_getChecksumBuffer(conn);
258 	ChecksumBuffer_addChecksum(cb, frameNr, checksum);
259 }
260 
261 bool
verifyChecksums(BattleFrameCounter frameNr)262 verifyChecksums(BattleFrameCounter frameNr) {
263 	Checksum localChecksum;
264 	size_t player;
265 
266 	if (!ChecksumBuffer_getChecksum(&localChecksumBuffer, frameNr,
267 			&localChecksum)) {
268 		// Right now, we require that a checksum is present.
269 		// If/when we move to UDP, and packets may get lost, we may prefer
270 		// not to do any checks in this case.
271 		return false;
272 	}
273 
274 	for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
275 	{
276 		NetConnection *conn;
277 		ChecksumBuffer *cb;
278 		Checksum remoteChecksum;
279 
280 		conn = netConnections[player];
281 		if (conn == NULL)
282 			continue;
283 
284 		cb = NetConnection_getChecksumBuffer(conn);
285 
286 		if (!ChecksumBuffer_getChecksum(cb, frameNr, &remoteChecksum))
287 			return false;
288 
289 		if (localChecksum != remoteChecksum) {
290 			log_add(log_Error, "Network connections have gone out of "
291 					"sync.\n");
292 			return false;
293 		}
294 	}
295 	return true;
296 }
297 
298 
299 #endif  /* NETPLAY_CHECKSUM */
300 
301 #endif  /* NETPLAY */
302 
303