summaryrefslogtreecommitdiff
path: root/src/uqm/supermelee/netplay/checkbuf.c
blob: e9c5a323ae2f0fd6037c4dcc4d247de1d931c51d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 *  Copyright 2006  Serge van den Boom <svdb@stack.nl>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define PORT_WANT_ERRNO
#include "port.h"

#include "netplay.h"
#include "checkbuf.h"
#include "libs/log.h"

#include "../../battle.h"
		// for battleFrameCount


#include <errno.h>
#include <stdlib.h>



static inline BattleFrameCounter
ChecksumBuffer_getCurrentFrameNr(void) {
	return battleFrameCount;
}

void
ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval) {
	// The input buffer lags BattleInput_inputDelay frames behind,
	// but only every interval frames will there be a checksum to be
	// checked.

	// Checksums will be checked when 'frameNr % interval == 0'.
	// (and frameNr is zero-based).
	// The checksum of frame n will be processed in frame 'n + delay'.

	// In the worst case, side 1 processes frames 'n' through 'n + delay - 1',
	// then blocks in frame 'n + delay' (after sending a checksum, if that's
	// pertinent for that frame).
	// Then side 2 receives all this input and these checksums, and
	// progresses to 'delay' frames after the last frame the received input
	// originated from, and blocks in the frame after it (after sending a
	// checksum, if that's pertinent for the frame).
	// So it sent input and checksums for frames 'n' through
	// 'n + delay + delay + 1'.
	// The input and checksums for these '2*delay + 2' frames are still
	// unhandled by side 1, so it needs buffer space for this.
	// With checksums only sent every interval frames, the buffer space
	// needed will be 'roundUp(2*delay + 2)' spaces.

	size_t bufSize = ((2 * delay + 2) + (interval - 1)) / interval;

	{
#ifdef NETPLAY_DEBUG
		size_t i;
#endif

		cb->checksums = malloc(bufSize * sizeof (ChecksumEntry));
		cb->maxSize = bufSize;
		cb->interval = interval;

#ifdef NETPLAY_DEBUG
		for (i = 0; i < bufSize; i++) {
			cb->checksums[i].checksum = 0;
			cb->checksums[i].frameNr = (BattleFrameCounter) -1;
		}
#endif
	}
}

void
ChecksumBuffer_uninit(ChecksumBuffer *cb) {
	if (cb->checksums != NULL) {
		free(cb->checksums);
		cb->checksums = NULL;
	}
}

// Returns the entry that would be used for the checksum for the specified
// frame. Whether the entry is actually valid is not checked.
static ChecksumEntry *
ChecksumBuffer_getChecksumEntry(ChecksumBuffer *cb,
		BattleFrameCounter frameNr) {
	size_t index;
	ChecksumEntry *entry;

	assert(frameNr % cb->interval == 0);
			// We only record checksums exactly every 'interval' frames.

	index = (frameNr / cb->interval) % cb->maxSize;
	entry = &cb->checksums[index];

	return entry;
}

bool
ChecksumBuffer_addChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
		Checksum checksum) {
	ChecksumEntry *entry;
	
	assert(frameNr % cb->interval == 0);

	entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);

#ifdef NETPLAY_DEBUG
	entry->frameNr = frameNr;
#endif
	entry->checksum = checksum;
	return true;
}

// Pre: frameNr is within the range of the checksums stored in cb.
bool
ChecksumBuffer_getChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
		Checksum *result) {
	ChecksumEntry *entry;

	entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);
	
#ifdef NETPLAY_DEBUG
	if (frameNr != entry->frameNr) {
		log_add(log_Error, "Checksum buffer entry for requested frame %u "
				"(still?) contains a checksum for frame %u.\n",
				frameNr, entry->frameNr);
		return false;
	}
#endif

	*result = entry->checksum;
	return true;
}