summaryrefslogtreecommitdiff
path: root/src/uqm/supermelee/netplay
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/supermelee/netplay')
-rw-r--r--src/uqm/supermelee/netplay/FILES50
-rw-r--r--src/uqm/supermelee/netplay/Makeinfo4
-rw-r--r--src/uqm/supermelee/netplay/checkbuf.c145
-rw-r--r--src/uqm/supermelee/netplay/checkbuf.h77
-rw-r--r--src/uqm/supermelee/netplay/checksum.c302
-rw-r--r--src/uqm/supermelee/netplay/checksum.h99
-rw-r--r--src/uqm/supermelee/netplay/crc.c142
-rw-r--r--src/uqm/supermelee/netplay/crc.h60
-rw-r--r--src/uqm/supermelee/netplay/nc_connect.ci300
-rw-r--r--src/uqm/supermelee/netplay/netconnection.c378
-rw-r--r--src/uqm/supermelee/netplay/netconnection.h260
-rw-r--r--src/uqm/supermelee/netplay/netinput.c157
-rw-r--r--src/uqm/supermelee/netplay/netinput.h57
-rw-r--r--src/uqm/supermelee/netplay/netmelee.c740
-rw-r--r--src/uqm/supermelee/netplay/netmelee.h90
-rw-r--r--src/uqm/supermelee/netplay/netmisc.c134
-rw-r--r--src/uqm/supermelee/netplay/netmisc.h77
-rw-r--r--src/uqm/supermelee/netplay/netoptions.c39
-rw-r--r--src/uqm/supermelee/netplay/netoptions.h56
-rw-r--r--src/uqm/supermelee/netplay/netplay.h77
-rw-r--r--src/uqm/supermelee/netplay/netrcv.c193
-rw-r--r--src/uqm/supermelee/netplay/netrcv.h34
-rw-r--r--src/uqm/supermelee/netplay/netsend.c95
-rw-r--r--src/uqm/supermelee/netplay/netsend.h35
-rw-r--r--src/uqm/supermelee/netplay/netstate.c48
-rw-r--r--src/uqm/supermelee/netplay/netstate.h83
-rw-r--r--src/uqm/supermelee/netplay/notify.c118
-rw-r--r--src/uqm/supermelee/netplay/notify.h62
-rw-r--r--src/uqm/supermelee/netplay/notifyall.c146
-rw-r--r--src/uqm/supermelee/netplay/notifyall.h49
-rw-r--r--src/uqm/supermelee/netplay/packet.c263
-rw-r--r--src/uqm/supermelee/netplay/packet.h299
-rw-r--r--src/uqm/supermelee/netplay/packethandlers.c649
-rw-r--r--src/uqm/supermelee/netplay/packethandlers.h56
-rw-r--r--src/uqm/supermelee/netplay/packetq.c149
-rw-r--r--src/uqm/supermelee/netplay/packetq.h59
-rw-r--r--src/uqm/supermelee/netplay/packetsenders.c197
-rw-r--r--src/uqm/supermelee/netplay/packetsenders.h67
-rw-r--r--src/uqm/supermelee/netplay/proto/Makeinfo2
-rw-r--r--src/uqm/supermelee/netplay/proto/npconfirm.c81
-rw-r--r--src/uqm/supermelee/netplay/proto/npconfirm.h36
-rw-r--r--src/uqm/supermelee/netplay/proto/ready.c106
-rw-r--r--src/uqm/supermelee/netplay/proto/ready.h38
-rw-r--r--src/uqm/supermelee/netplay/proto/reset.c166
-rw-r--r--src/uqm/supermelee/netplay/proto/reset.h41
45 files changed, 6316 insertions, 0 deletions
diff --git a/src/uqm/supermelee/netplay/FILES b/src/uqm/supermelee/netplay/FILES
new file mode 100644
index 0000000..7b93fd1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/FILES
@@ -0,0 +1,50 @@
+In netplay/:
+crc.{c,h} Generic CRC routines.
+checkbuf.{c,h} Buffer of checksums.
+checksum.{c,h} Routines for checksumming the game state.
+netconnection.{c,h} Definition of NetConnection, being the state of a
+ network connection, and operations on it.
+nc_connect.ci Part of netconnection.c that handles establishing
+ connections.
+netinput.{c,h} Definitions and operations for melee input commands
+ over a network connection.
+netmelee.{c,h} Keeps track of network connections used in the game,
+ with methods to control them all at once.
+ Functions that directly access the game data are kept
+ in the relevant files (melee.c, pickmele.c, battle.c).
+netmisc.{c,h} Miscelaneous functions that didn't fit in elsewhere.
+netoptions.{c,h} Description of a network connection to be established.
+netplay.h Some global netplay definitions.
+netrcv.{c,h} Processes incoming packets. Does know about the protocol
+ and will do consistency checking.
+ Does not directly manipulate the game state.
+netsend.{c,h} Enqueues all sorts of packets for sending.
+ Does not know about the protocol and hence will do
+ no checks for protocol sanity.
+netstate.{c,h} Definitions of the states of a network connection.
+notify.{c,h} Routines for notifying a remote side of local changes.
+ Knows about the protocal and has assert()s to
+ check for local consistency.
+packet.{c,h} Definition and creation of packets. Create functions
+ should only be called from netsend.c.
+packethandlers.{c,h} Routines for processing each type of incoming packet.
+packetq.{c,h} Manages the packet queue.
+packetsenders.{c,h} Creates and sends/queues packets.
+
+In netplay/proto/:
+npconfirm.{c,h} Functions for handing the 'confirmation' protocol.
+ready.{c,h} Functions for handling the 'ready' protocol.
+reset.{c,h} Functions for handling the 'reset' protocol.
+
+
+
+
+
+TODO:
+Division:
+- files that interface with sockets and knows nothing of the game
+ (these are in libs/network)
+- files that know of sockets and the game but don't directly interface
+ with either
+- files that interface with the game and know nothing of sockets
+
diff --git a/src/uqm/supermelee/netplay/Makeinfo b/src/uqm/supermelee/netplay/Makeinfo
new file mode 100644
index 0000000..ff31011
--- /dev/null
+++ b/src/uqm/supermelee/netplay/Makeinfo
@@ -0,0 +1,4 @@
+uqm_SUBDIRS="proto"
+uqm_CFILES="checkbuf.c checksum.c crc.c netconnection.c netinput.c netmelee.c netmisc.c netoptions.c netrcv.c netsend.c netstate.c notify.c notifyall.c packet.c packethandlers.c packetsenders.c packetq.c"
+uqm_HFILES="checkbuf.h checksum.h crc.h netconnection.h netinput.h netmelee.h netmisc.h netoptions.h netplay.h netrcv.h netsend.h netstate.h notifyall.h notify.h packet.h packethandlers.h packetq.h packetsenders.h"
+
diff --git a/src/uqm/supermelee/netplay/checkbuf.c b/src/uqm/supermelee/netplay/checkbuf.c
new file mode 100644
index 0000000..e9c5a32
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checkbuf.c
@@ -0,0 +1,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;
+}
+
diff --git a/src/uqm/supermelee/netplay/checkbuf.h b/src/uqm/supermelee/netplay/checkbuf.h
new file mode 100644
index 0000000..f609448
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checkbuf.h
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_
+#define UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct ChecksumEntry ChecksumEntry;
+typedef struct ChecksumBuffer ChecksumBuffer;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "../../battle.h"
+ // for BattleFrameCounter
+#include "checksum.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+struct ChecksumEntry {
+#ifdef NETPLAY_DEBUG
+ BattleFrameCounter frameNr;
+ // The number of the frame this checksum originated from.
+ // If the checksumming code is working correctly, the checksum
+ // can only come from one frame, so this value is not needed
+ // for normal operation.
+ // Its only use is to detect some cases where checksumming code
+ // is *not* working correctly.
+#endif
+ Checksum checksum;
+};
+
+struct ChecksumBuffer {
+ ChecksumEntry *checksums;
+ // Cyclic buffer. if 'size' > 0, then 'first' is an index to
+ // the first used entry, 'size' is the number of used
+ // entries in the buffer, and (first + size) % maxSize is the
+ // index to just past the end of the buffer.
+ size_t maxSize;
+
+ size_t interval;
+};
+
+void ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval);
+void ChecksumBuffer_uninit(ChecksumBuffer *cb);
+bool ChecksumBuffer_addChecksum(ChecksumBuffer *cb,
+ BattleFrameCounter frameNr, Checksum checksum);
+bool ChecksumBuffer_getChecksum(ChecksumBuffer *cb,
+ BattleFrameCounter frameNr, Checksum *result);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_ */
diff --git a/src/uqm/supermelee/netplay/checksum.c b/src/uqm/supermelee/netplay/checksum.c
new file mode 100644
index 0000000..5d687f0
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checksum.c
@@ -0,0 +1,302 @@
+/*
+ * 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
+ */
+
+#ifdef NETPLAY
+
+#include "checksum.h"
+#include "netoptions.h"
+
+#ifdef NETPLAY_CHECKSUM
+
+#include "checkbuf.h"
+#include "crc.h"
+ // for DUMP_CRC_OPS
+#include "netconnection.h"
+#include "netmelee.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+
+ChecksumBuffer localChecksumBuffer;
+
+void
+crc_processEXTENT(crc_State *state, const EXTENT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processEXTENT().");
+#endif
+ crc_processCOORD(state, val->width);
+ crc_processCOORD(state, val->height);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processEXTENT().");
+#endif
+}
+
+void
+crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processVELOCITY_DESC().");
+#endif
+ crc_processCOUNT(state, val->TravelAngle);
+ crc_processEXTENT(state, &val->vector);
+ crc_processEXTENT(state, &val->fract);
+ crc_processEXTENT(state, &val->error);
+ crc_processEXTENT(state, &val->incr);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processVELOCITY_DESC().");
+#endif
+}
+
+void
+crc_processPOINT(crc_State *state, const POINT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processPOINT().");
+#endif
+ crc_processCOORD(state, val->x);
+ crc_processCOORD(state, val->y);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processPOINT().");
+#endif
+}
+
+#if 0
+void
+crc_processSTAMP(crc_State *state, const STAMP *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processSTAMP().");
+#endif
+ crc_processPOINT(state, val->origin);
+ crc_processFRAME(state, val->frame);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processSTAMP().");
+#endif
+}
+
+void
+crc_processINTERSECT_CONTROL(crc_State *state, const INTERSECT_CONTROL *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processINTERSECT_CONTROL().");
+#endif
+ crc_processTIME_VALUE(state, val->last_time_val);
+ crc_processPOINT(state, &val->EndPoint);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processINTERSECT_CONTROL().");
+#endif
+}
+#endif
+
+void
+crc_processSTATE(crc_State *state, const STATE *val) {
+ crc_processPOINT(state, &val->location);
+}
+
+void
+crc_processELEMENT(crc_State *state, const ELEMENT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processELEMENT().");
+#endif
+ if (val->state_flags & BACKGROUND_OBJECT) {
+ // The element never influences the state of other elements,
+ // and is to be excluded from checksums.
+#ifdef DUMP_CRC_OPS
+ crc_log(" BACKGROUND_OBJECT element omited");
+#endif
+ } else {
+ crc_processELEMENT_FLAGS(state, val->state_flags);
+ crc_processCOUNT(state, val->life_span);
+ crc_processCOUNT(state, val->crew_level);
+ crc_processBYTE(state, val->mass_points);
+ crc_processBYTE(state, val->turn_wait);
+ crc_processBYTE(state, val->thrust_wait);
+ crc_processVELOCITY_DESC(state, &val->velocity);
+ crc_processSTATE(state, &val->current);
+ crc_processSTATE(state, &val->next);
+ }
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processELEMENT().");
+#endif
+}
+
+void
+crc_processDispQueue(crc_State *state) {
+ HELEMENT element;
+ HELEMENT nextElement;
+
+#ifdef DUMP_CRC_OPS
+ size_t i = 0;
+ crc_log("START crc_processDispQueue().");
+#endif
+ for (element = GetHeadElement(); element != 0; element = nextElement) {
+ ELEMENT *elementPtr;
+
+#ifdef DUMP_CRC_OPS
+ crc_log("===== disp_q[%d]:", i);
+#endif
+ LockElement(element, &elementPtr);
+
+ crc_processELEMENT(state, elementPtr);
+
+ nextElement = GetSuccElement(elementPtr);
+ UnlockElement(element);
+#ifdef DUMP_CRC_OPS
+ i++;
+#endif
+ }
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processDispQueue().");
+#endif
+}
+
+void
+crc_processRNG(crc_State *state) {
+ DWORD seed;
+
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processRNG().");
+#endif
+
+ seed = TFB_SeedRandom(0);
+ // This modifies the seed too.
+ crc_processDWORD(state, seed);
+ TFB_SeedRandom(seed);
+ // Restore the old seed.
+
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processRNG().");
+#endif
+}
+
+void
+crc_processState(crc_State *state) {
+#ifdef DUMP_CRC_OPS
+ crc_log("--------------------\n"
+ "START crc_processState() (frame %u).", battleFrameCount);
+#endif
+
+ crc_processRNG(state);
+ crc_processDispQueue(state);
+
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processState() (frame %u).",
+ battleFrameCount);
+#endif
+}
+
+void
+initChecksumBuffers(void) {
+ size_t player;
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+ ChecksumBuffer_init(cb, getBattleInputDelay(),
+ NETPLAY_CHECKSUM_INTERVAL);
+ }
+
+ ChecksumBuffer_init(&localChecksumBuffer, getBattleInputDelay(),
+ NETPLAY_CHECKSUM_INTERVAL);
+}
+
+void
+uninitChecksumBuffers(void)
+{
+ size_t player;
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+
+ ChecksumBuffer_uninit(cb);
+ }
+
+ ChecksumBuffer_uninit(&localChecksumBuffer);
+}
+
+void
+addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum) {
+ assert(frameNr == battleFrameCount);
+
+ ChecksumBuffer_addChecksum(&localChecksumBuffer, frameNr, checksum);
+}
+
+void
+addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ ChecksumBuffer *cb;
+
+ assert(frameNr <= battleFrameCount + getBattleInputDelay() + 1);
+ assert(frameNr + getBattleInputDelay() >= battleFrameCount);
+
+ cb = NetConnection_getChecksumBuffer(conn);
+ ChecksumBuffer_addChecksum(cb, frameNr, checksum);
+}
+
+bool
+verifyChecksums(BattleFrameCounter frameNr) {
+ Checksum localChecksum;
+ size_t player;
+
+ if (!ChecksumBuffer_getChecksum(&localChecksumBuffer, frameNr,
+ &localChecksum)) {
+ // Right now, we require that a checksum is present.
+ // If/when we move to UDP, and packets may get lost, we may prefer
+ // not to do any checks in this case.
+ return false;
+ }
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+ Checksum remoteChecksum;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+
+ if (!ChecksumBuffer_getChecksum(cb, frameNr, &remoteChecksum))
+ return false;
+
+ if (localChecksum != remoteChecksum) {
+ log_add(log_Error, "Network connections have gone out of "
+ "sync.\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#endif /* NETPLAY_CHECKSUM */
+
+#endif /* NETPLAY */
+
diff --git a/src/uqm/supermelee/netplay/checksum.h b/src/uqm/supermelee/netplay/checksum.h
new file mode 100644
index 0000000..cfb48d6
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checksum.h
@@ -0,0 +1,99 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_
+#define UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_
+
+
+#include "types.h"
+
+typedef uint32 Checksum;
+
+
+#include "netplay.h"
+#include "crc.h"
+
+#include "../../element.h"
+#include "libs/gfxlib.h"
+
+#include "netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+static inline void
+crc_processELEMENT_FLAGS(crc_State *state, ELEMENT_FLAGS val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+static inline void
+crc_processCOUNT(crc_State *state, COUNT val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+static inline void
+crc_processBYTE(crc_State *state, BYTE val) {
+ crc_processUint8(state, (uint8) val);
+}
+
+static inline void
+crc_processDWORD(crc_State *state, DWORD val) {
+ crc_processUint32(state, (uint32) val);
+}
+
+static inline void
+crc_processCOORD(crc_State *state, COORD val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+#if 0
+static inline void
+crc_processTIME_VALUE(crc_State *state, const TIME_VALUE val) {
+ crc_processUint16(state, (uint16) val);
+}
+#endif
+
+void crc_processEXTENT(crc_State *state, const EXTENT *val);
+void crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val);
+void crc_processPOINT(crc_State *state, const POINT *val);
+#if 0
+void crc_processSTAMP(crc_State *state, const STAMP *val);
+void crc_processINTERSECT_CONTROL(crc_State *state,
+ const INTERSECT_CONTROL *val);
+#endif
+void crc_processSTATE(crc_State *state, const STATE *val);
+void crc_processELEMENT(crc_State *state, const ELEMENT *val);
+void crc_processDispQueue(crc_State *state);
+void crc_processRNG(crc_State *state);
+void crc_processState(crc_State *state);
+
+
+void initChecksumBuffers(void);
+void uninitChecksumBuffers(void);
+void addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum);
+void addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum);
+bool verifyChecksums(BattleFrameCounter frameNr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_ */
diff --git a/src/uqm/supermelee/netplay/crc.c b/src/uqm/supermelee/netplay/crc.c
new file mode 100644
index 0000000..677b36f
--- /dev/null
+++ b/src/uqm/supermelee/netplay/crc.c
@@ -0,0 +1,142 @@
+/*
+ * 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
+ */
+
+#include "netplay.h"
+ // For DUMP_CRC_OPS
+
+#include "crc.h"
+
+#ifdef DUMP_CRC_OPS
+# include "libs/log.h"
+#endif
+
+
+// CRC table for Polynomial 0x04c11db7 (0xedb88320 reversed)
+uint32 crcTable[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+void
+crc_init(crc_State *state) {
+ state->crc = 0xffffffff;
+}
+
+void
+crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen) {
+ uint8 *end = buf + bufLen;
+ uint32 newCrc = state->crc;
+
+ while (buf < end)
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ *buf) & 0xff];
+
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processBytes(%08x, [%zu bytes]) --> %08x.",
+ state->crc, bufLen, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint8(crc_State *state, uint8 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ val) & 0xff];
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint8(%08x, %02x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint16(crc_State *state, uint16 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val >> 8) ) & 0xff];
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint16(%08x, %04x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint32(crc_State *state, uint32 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 8) & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 16) & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 24) )) & 0xff];
+
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint32(%08x, %08x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+uint32
+crc_finish(const crc_State *state) {
+ return ~state->crc;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/crc.h b/src/uqm/supermelee/netplay/crc.h
new file mode 100644
index 0000000..1744d11
--- /dev/null
+++ b/src/uqm/supermelee/netplay/crc.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CRC_H_
+#define UQM_SUPERMELEE_NETPLAY_CRC_H_
+
+typedef struct crc_State crc_State;
+
+#include "types.h"
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct crc_State {
+ uint32 crc;
+};
+
+void crc_init(crc_State *state);
+void crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen);
+void crc_processUint8(crc_State *state, uint8 val);
+void crc_processUint16(crc_State *state, uint16 val);
+void crc_processUint32(crc_State *state, uint32 val);
+uint32 crc_finish(const crc_State *state);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef DUMP_CRC_OPS
+#include "netconnection.h"
+ // for netplayDebugFile
+//#define crc_log(...) log_add (logDebug, __VA_ARGS__)
+#define crc_log(...) if (netplayDebugFile != NULL) \
+ { \
+ uio_fprintf (netplayDebugFile, __VA_ARGS__); \
+ uio_putc ('\n', netplayDebugFile); \
+ } else \
+ (void) 0
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CRC_H_ */
+
diff --git a/src/uqm/supermelee/netplay/nc_connect.ci b/src/uqm/supermelee/netplay/nc_connect.ci
new file mode 100644
index 0000000..6c67fed
--- /dev/null
+++ b/src/uqm/supermelee/netplay/nc_connect.ci
@@ -0,0 +1,300 @@
+/*
+ * 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
+ */
+
+// This file is part of netconnection.c, from where it is #included.
+
+static inline ConnectStateData *ConnectStateData_alloc(void);
+static inline ConnectStateData *ConnectStateData_new(void);
+static inline void ConnectStateData_free(ConnectStateData *connectStateData);
+static void ConnectStateData_delete(ConnectStateData *connectStateData);
+
+static int NetConnection_go(NetConnection *conn);
+static ListenState *NetConnection_serverGo(NetConnection *conn);
+static ConnectState *NetConnection_clientGo(NetConnection *conn);
+static void NetConnection_connected(NetConnection *conn);
+static void NetConnection_connectedServerCallback(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen);
+static void NetConnection_connectedClientCallback(ConnectState *connectState,
+ NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen);
+static void NetConnection_connectedServerErrorCallback(
+ ListenState *listenState, const ListenError *listenError);
+static void NetConnection_connectedClientErrorCallback(
+ ConnectState *connectState, const ConnectError *connectError);
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static inline ConnectStateData *
+ConnectStateData_alloc(void) {
+ return (ConnectStateData *) malloc(sizeof (ConnectStateData));
+}
+
+static inline ConnectStateData *
+ConnectStateData_new(void) {
+ ConnectStateData *connectStateData = ConnectStateData_alloc();
+ connectStateData->releaseFunction =
+ (NetConnectionStateData_ReleaseFunction) ConnectStateData_delete;
+ return connectStateData;
+}
+
+static inline void
+ConnectStateData_free(ConnectStateData *connectStateData) {
+ free(connectStateData);
+}
+
+static void
+ConnectStateData_delete(ConnectStateData *connectStateData) {
+ if (connectStateData->isServer) {
+ ListenState *listenState = connectStateData->state.listenState;
+ ListenState_close(listenState);
+ } else {
+ ConnectState *connectState = connectStateData->state.connectState;
+ ConnectState_close(connectState);
+ }
+ ConnectStateData_free(connectStateData);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static int
+NetConnection_go(NetConnection *conn) {
+ ConnectStateData *connectStateData;
+
+ if (NetConnection_isConnected(conn))
+ return 0;
+
+ if (conn->options->isServer) {
+ ListenState *listenState;
+ listenState = NetConnection_serverGo(conn);
+ if (listenState == NULL) {
+ // errno is set
+ return -1;
+ }
+ connectStateData = ConnectStateData_new();
+ connectStateData->state.listenState = listenState;
+ } else {
+ ConnectState *connectState;
+ connectState = NetConnection_clientGo(conn);
+ if (connectState == NULL) {
+ // errno is set
+ return -1;
+ }
+ connectStateData = ConnectStateData_new();
+ connectStateData->state.connectState = connectState;
+ }
+ connectStateData->isServer = conn->options->isServer;
+
+ NetConnection_setStateData(conn, (void *) connectStateData);
+ return 0;
+}
+
+static ListenState *
+NetConnection_serverGo(NetConnection *conn) {
+ ListenFlags listenFlags;
+ ListenState *result;
+
+ assert(conn->state == NetState_unconnected);
+ assert(conn->options->isServer);
+
+ NetConnection_setState(conn, NetState_connecting);
+
+ memset (&listenFlags, 0, sizeof listenFlags);
+ listenFlags.familyDemand =
+#if NETPLAY == NETPLAY_IPV4
+ PF_inet;
+#else
+ PF_unspec;
+#endif
+ listenFlags.familyPrefer = PF_unspec;
+ listenFlags.backlog = NETPLAY_LISTEN_BACKLOG;
+
+ result = listenPort(conn->options->port, IPProto_tcp, &listenFlags,
+ NetConnection_connectedServerCallback,
+ NetConnection_connectedServerErrorCallback, (void *) conn);
+
+ return result;
+}
+
+static ConnectState *
+NetConnection_clientGo(NetConnection *conn) {
+ ConnectFlags connectFlags;
+ ConnectState *result;
+
+ assert(conn->state == NetState_unconnected);
+ assert(!conn->options->isServer);
+
+ NetConnection_setState(conn, NetState_connecting);
+
+ memset (&connectFlags, 0, sizeof connectFlags);
+ connectFlags.familyDemand =
+#if NETPLAY == NETPLAY_IPV4
+ PF_inet;
+#else
+ PF_unspec;
+#endif
+ connectFlags.familyPrefer = PF_unspec;
+ connectFlags.timeout = NETPLAY_CONNECTTIMEOUT;
+ connectFlags.retryDelayMs = NETPLAY_RETRYDELAY;
+
+ result = connectHostByName(conn->options->host, conn->options->port,
+ IPProto_tcp, &connectFlags, NetConnection_connectedClientCallback,
+ NetConnection_connectedClientErrorCallback, (void *) conn);
+
+ return result;
+}
+
+// Called when an incoming connection has been established.
+// The caller gives up ownership of newNd
+static void
+NetConnection_connectedServerCallback(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ NetConnection *conn =
+ (NetConnection *) ListenState_getExtra(listenState);
+
+ assert(conn->nd == NULL);
+
+ conn->nd = newNd;
+ // No incRef(); the caller gives up ownership.
+ NetDescriptor_setExtra(conn->nd, (void *) conn);
+
+ (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
+ // Ignore errors; it's not a big deal. In debug mode, a message
+ // will already have been printed from the function itself.
+
+ conn->stateFlags.discriminant = true;
+
+ NetConnection_connected(conn);
+ (void) listenNd;
+ (void) addr;
+ (void) addrLen;
+}
+
+// Called when an outgoing connection has been established.
+// The caller gives up ownership of newNd
+static void
+NetConnection_connectedClientCallback(ConnectState *connectState,
+ NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ NetConnection *conn =
+ (NetConnection *) ConnectState_getExtra(connectState);
+
+ assert(conn->nd == NULL);
+
+ conn->nd = newNd;
+ // No incRef(); the caller gives up ownership.
+ NetDescriptor_setExtra(conn->nd, (void *) conn);
+
+ (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
+ // Ignore errors; it's not a big deal. In debug mode, a message
+ // will already have been printed from the function itself.
+
+ conn->stateFlags.discriminant = false;
+
+ NetConnection_connected(conn);
+ (void) addr;
+ (void) addrLen;
+}
+
+// Called when a connection has been established.
+static void
+NetConnection_connected(NetConnection *conn) {
+ ConnectStateData *connectStateData;
+
+ conn->stateFlags.connected = true;
+ NetConnection_setState(conn, NetState_init);
+
+ connectStateData = (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_delete(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+
+ NetDescriptor_setReadCallback(conn->nd, dataReadyCallback);
+ NetDescriptor_setCloseCallback(conn->nd, closeCallback);
+
+ (*conn->connectCallback)(conn);
+}
+
+static void
+NetConnection_connectedServerErrorCallback(ListenState *listenState,
+ const ListenError *listenError) {
+ NetConnection *conn =
+ (NetConnection *) ListenState_getExtra(listenState);
+ NetConnectionError error;
+
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+ ListenState_close(listenState);
+ NetConnection_setState(conn, NetState_unconnected);
+
+ {
+ ConnectStateData *connectStateData;
+ connectStateData =
+ (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_free(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+ }
+
+ if (conn->errorCallback != NULL) {
+ error.state = NetState_connecting;
+ error.err = listenError->err;
+ error.extra.listenError = listenError;
+ //NetConnection_incRef(conn);
+ (*conn->errorCallback)(conn, &error);
+ //NetConnection_decRef(conn);
+ }
+
+ NetConnection_close(conn);
+}
+
+static void
+NetConnection_connectedClientErrorCallback(ConnectState *connectState,
+ const ConnectError *connectError) {
+ NetConnection *conn =
+ (NetConnection *) ConnectState_getExtra(connectState);
+ NetConnectionError error;
+
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+ ConnectState_close(connectState);
+ NetConnection_setState(conn, NetState_unconnected);
+
+ {
+ ConnectStateData *connectStateData;
+ connectStateData =
+ (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_free(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+ }
+
+ if (conn->errorCallback != NULL) {
+ error.state = NetState_connecting;
+ error.err = connectError->err;
+ error.extra.connectError = connectError;
+ //NetConnection_incRef(conn);
+ (*conn->errorCallback)(conn, &error);
+ //NetConnection_decRef(conn);
+ }
+
+ NetConnection_close(conn);
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netconnection.c b/src/uqm/supermelee/netplay/netconnection.c
new file mode 100644
index 0000000..48ab46b
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netconnection.c
@@ -0,0 +1,378 @@
+/*
+ * 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 NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "netconnection.h"
+
+#include "netrcv.h"
+
+#if defined(DEBUG) || defined(NETPLAY_DEBUG)
+# include "libs/log.h"
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include "options.h"
+ // for configDir
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include <errno.h>
+# include <time.h>
+#endif
+
+
+static void closeCallback(NetDescriptor *nd);
+static void NetConnection_doClose(NetConnection *conn);
+
+
+#include "nc_connect.ci"
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+uio_Stream *netplayDebugFile;
+#endif
+
+// Used as initial value for Agreement structures, by structure assignment.
+const Agreement Agreement_nothingAgreed;
+
+
+// The NetConnection keeps a pointer to the passed NetplayPeerOptions;
+// do not free it as long as the NetConnection exists.
+NetConnection *
+NetConnection_open(int player, const NetplayPeerOptions *options,
+ NetConnection_ConnectCallback connectCallback,
+ NetConnection_CloseCallback closeCallback,
+ NetConnection_ErrorCallback errorCallback,
+ NetConnection_DeleteCallback deleteCallback, void *extra) {
+ NetConnection *conn;
+
+ conn = malloc(sizeof (NetConnection));
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+ {
+ char dumpFileName[PATH_MAX];
+ time_t now;
+ struct tm *nowTm;
+ size_t strftimeResult;
+
+ now = time (NULL);
+ if (now == (time_t) -1) {
+ log_add (log_Fatal, "time() failed: %s.", strerror (errno));
+ abort ();
+ }
+
+ nowTm = localtime(&now);
+ // XXX: I would like to use localtime_r(), but it isn't very
+ // portable (yet), and adding a check for it to the build.sh script
+ // is not worth the effort for a debugging function right now.
+
+ strftimeResult = strftime (dumpFileName, sizeof dumpFileName,
+ "debug/netlog-%Y%m%d%H%M%S", nowTm);
+ if (strftimeResult == 0) {
+ log_add (log_Fatal, "strftime() failed: %s.", strerror (errno));
+ abort ();
+ }
+
+ // The user needs to create the debug/ dir manually. If there
+ // is no debug/ dir, no log will be created.
+ conn->debugFile = uio_fopen (configDir, dumpFileName, "wt");
+ if (conn->debugFile == NULL) {
+ log_add (log_Debug, "Not creating a netplay debug log for "
+ "player %d.", player);
+ } else {
+ log_add (log_Debug, "Creating netplay debug log '%s' for "
+ "player %d.", dumpFileName, player);
+ if (netplayDebugFile == NULL) {
+ // Debug info relating to no specific network connection
+ // is sent to the first opened one.
+ netplayDebugFile = conn->debugFile;
+ }
+ }
+ }
+#endif
+
+ conn->nd = NULL;
+ conn->player = player;
+ conn->state = NetState_unconnected;
+ conn->options = options;
+ conn->extra = extra;
+ PacketQueue_init(&conn->queue);
+
+ conn->connectCallback = connectCallback;
+ conn->closeCallback = closeCallback;
+ conn->errorCallback = errorCallback;
+ conn->deleteCallback = deleteCallback;
+ conn->readyCallback = NULL;
+ conn->readyCallbackArg = NULL;
+ conn->resetCallback = NULL;
+ conn->resetCallbackArg = NULL;
+
+ conn->readBuf = malloc(NETPLAY_READBUFSIZE);
+ conn->readEnd = conn->readBuf;
+
+ conn->stateData = NULL;
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = false;
+ conn->stateFlags.discriminant = false;
+ conn->stateFlags.handshake.localOk = false;
+ conn->stateFlags.handshake.remoteOk = false;
+ conn->stateFlags.handshake.canceling = false;
+ conn->stateFlags.ready.localReady = false;
+ conn->stateFlags.ready.remoteReady = false;
+ conn->stateFlags.reset.localReset = false;
+ conn->stateFlags.reset.remoteReset = false;
+ conn->stateFlags.agreement = Agreement_nothingAgreed;
+ conn->stateFlags.inputDelay = 0;
+#ifdef NETPLAY_CHECKSUM
+ conn->stateFlags.checksumInterval = NETPLAY_CHECKSUM_INTERVAL;
+#endif
+
+#ifdef NETPLAY_STATISTICS
+ {
+ size_t i;
+
+ conn->statistics.packetsReceived = 0;
+ conn->statistics.packetsSent = 0;
+ for (i = 0; i < PACKET_NUM; i++)
+ {
+ conn->statistics.packetTypeReceived[i] = 0;
+ conn->statistics.packetTypeSent[i] = 0;
+ }
+ }
+#endif
+
+ NetConnection_go(conn);
+
+ return conn;
+}
+
+static void
+NetConnection_doDeleteCallback(NetConnection *conn) {
+ if (conn->deleteCallback != NULL) {
+ //NetConnection_incRef(conn);
+ conn->deleteCallback(conn);
+ //NetConnection_decRef(conn);
+ }
+}
+
+static void
+NetConnection_delete(NetConnection *conn) {
+ NetConnection_doDeleteCallback(conn);
+ if (conn->stateData != NULL) {
+ NetConnectionStateData_release(conn->stateData);
+ conn->stateData = NULL;
+ }
+ free(conn->readBuf);
+ PacketQueue_uninit(&conn->queue);
+
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ if (netplayDebugFile == conn->debugFile) {
+ // There may be other network connections, with an open
+ // debug file, but we don't know about that.
+ // The debugging person just has to work around that.
+ netplayDebugFile = NULL;
+ }
+ uio_fclose(conn->debugFile);
+ }
+#endif
+
+ free(conn);
+}
+
+static void
+Netplay_doCloseCallback(NetConnection *conn) {
+ if (conn->closeCallback != NULL) {
+ //NetConnection_incRef(conn);
+ conn->closeCallback(conn);
+ //NetConnection_decRef(conn);
+ }
+}
+
+// Auxiliary function for closing, used by both closeCallback() and
+// NetConnection_close()
+static void
+NetConnection_doClose(NetConnection *conn) {
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+
+ // First the callback, so that it can still use the information
+ // of what is the current state, and the stateData:
+ Netplay_doCloseCallback(conn);
+
+ NetConnection_setState(conn, NetState_unconnected);
+}
+
+// Called when the NetDescriptor is shut down.
+static void
+closeCallback(NetDescriptor *nd) {
+ NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
+ if (conn == NULL)
+ return;
+ conn->nd = NULL;
+ NetConnection_doClose(conn);
+}
+
+// Close and release a NetConnection.
+void
+NetConnection_close(NetConnection *conn) {
+ if (conn->nd != NULL) {
+ NetDescriptor_setCloseCallback(conn->nd, NULL);
+ // We're not interested in the close callback of the
+ // NetDescriptor anymore.
+ NetDescriptor_close(conn->nd);
+ // This would queue the close callback.
+ conn->nd = NULL;
+ }
+ if (!conn->stateFlags.disconnected)
+ NetConnection_doClose(conn);
+ NetConnection_delete(conn);
+}
+
+void
+NetConnection_doErrorCallback(NetConnection *nd, int err) {
+ NetConnectionError error;
+
+ if (nd->errorCallback != NULL) {
+ error.state = nd->state;
+ error.err = err;
+ }
+ (*nd->errorCallback)(nd, &error);
+}
+
+void
+NetConnection_setStateData(NetConnection *conn,
+ NetConnectionStateData *stateData) {
+ conn->stateData = stateData;
+}
+
+NetConnectionStateData *
+NetConnection_getStateData(const NetConnection *conn) {
+ return conn->stateData;
+}
+
+void
+NetConnection_setExtra(NetConnection *conn, void *extra) {
+ conn->extra = extra;
+}
+
+void *
+NetConnection_getExtra(const NetConnection *conn) {
+ return conn->extra;
+}
+
+void
+NetConnection_setReadyCallback(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg) {
+ conn->readyCallback = callback;
+ conn->readyCallbackArg = arg;
+}
+
+NetConnection_ReadyCallback
+NetConnection_getReadyCallback(const NetConnection *conn) {
+ return conn->readyCallback;
+}
+
+void *
+NetConnection_getReadyCallbackArg(const NetConnection *conn) {
+ return conn->readyCallbackArg;
+}
+
+void
+NetConnection_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *arg) {
+ conn->resetCallback = callback;
+ conn->resetCallbackArg = arg;
+}
+
+NetConnection_ResetCallback
+NetConnection_getResetCallback(const NetConnection *conn) {
+ return conn->resetCallback;
+}
+
+void *
+NetConnection_getResetCallbackArg(const NetConnection *conn) {
+ return conn->resetCallbackArg;
+}
+
+void
+NetConnection_setState(NetConnection *conn, NetState state) {
+#ifdef NETPLAY_DEBUG
+ log_add(log_Debug, "NETPLAY: [%d] +/- Connection state changed to: "
+ "%s.\n", conn->player, netStateData[state].name);
+#endif
+#ifdef DEBUG
+ if (state == conn->state) {
+ log_add(log_Warning, "NETPLAY: [%d] Connection state set to %s "
+ "while already in that state.\n",
+ conn->player, netStateData[state].name);
+ }
+#endif
+ conn->state = state;
+}
+
+NetState
+NetConnection_getState(const NetConnection *conn) {
+ return conn->state;
+}
+
+bool
+NetConnection_getDiscriminant(const NetConnection *conn) {
+ return conn->stateFlags.discriminant;
+}
+
+const NetplayPeerOptions *
+NetConnection_getPeerOptions(const NetConnection *conn) {
+ return conn->options;
+}
+
+bool
+NetConnection_isConnected(const NetConnection *conn) {
+ return conn->stateFlags.connected;
+}
+
+int
+NetConnection_getPlayerNr(const NetConnection *conn) {
+ return conn->player;
+}
+
+size_t
+NetConnection_getInputDelay(const NetConnection *conn) {
+ return conn->stateFlags.inputDelay;
+}
+
+#ifdef NETPLAY_CHECKSUM
+ChecksumBuffer *
+NetConnection_getChecksumBuffer(NetConnection *conn) {
+ return &conn->checksumBuffer;
+}
+
+size_t
+NetConnection_getChecksumInterval(const NetConnection *conn) {
+ return conn->stateFlags.checksumInterval;
+}
+#endif /* NETPLAY_CHECKSUM */
+
+#ifdef NETPLAY_STATISTICS
+NetStatistics *
+NetConnection_getStatistics(NetConnection *conn) {
+ return &conn->statistics;
+}
+#endif
+
diff --git a/src/uqm/supermelee/netplay/netconnection.h b/src/uqm/supermelee/netplay/netconnection.h
new file mode 100644
index 0000000..485d3c4
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netconnection.h
@@ -0,0 +1,260 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_
+#define UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_
+
+#include "netplay.h"
+ // for NETPLAY_STATISTICS
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct NetConnection NetConnection;
+typedef struct NetConnectionError NetConnectionError;
+typedef struct ConnectStateData ConnectStateData;
+#ifdef NETPLAY_STATISTICS
+typedef struct NetStatistics NetStatistics;
+#endif
+
+typedef void (*NetConnection_ConnectCallback)(NetConnection *nd);
+typedef void (*NetConnection_CloseCallback)(NetConnection *nd);
+typedef void (*NetConnection_ErrorCallback)(NetConnection *nd,
+ const NetConnectionError *error);
+typedef void (*NetConnection_DeleteCallback)(NetConnection *nd);
+
+typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg);
+typedef void (*NetConnection_ResetCallback)(NetConnection *conn, void *arg);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "netstate.h"
+#include "netoptions.h"
+#ifdef NETPLAY_CHECKSUM
+# include "checkbuf.h"
+#endif
+#if defined(NETPLAY_STATISTICS) || defined(NETCONNECTION_INTERNAL)
+# include "packet.h"
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include "libs/uio.h"
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct NetConnectionError {
+ NetState state;
+ int err;
+ union {
+ const struct ListenError *listenError;
+ const struct ConnectError *connectError;
+ } extra;
+};
+
+#ifdef NETPLAY_STATISTICS
+struct NetStatistics {
+ size_t packetsReceived;
+ size_t packetTypeReceived[PACKET_NUM];
+ size_t packetsSent;
+ size_t packetTypeSent[PACKET_NUM];
+};
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef NETCONNECTION_INTERNAL
+#include "libs/net.h"
+#include "packetq.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct {
+ // For actions that require agreement by both parties.
+ bool localOk : 1; /* Action confirmed by us */
+ bool remoteOk : 1; /* Action confirmed by the remote party */
+ bool canceling : 1; /* Awaiting cancel confirmation */
+} HandShakeFlags;
+
+typedef struct {
+ // For actions that do not require agreement, but for which it
+ // only is relevant that both sides are ready.
+ bool localReady : 1;
+ bool remoteReady : 1;
+} ReadyFlags;
+
+typedef struct {
+ bool localReset : 1;
+ bool remoteReset : 1;
+} ResetFlags;
+
+// Which parameters have we both sides of a connection reached agreement on?
+typedef struct {
+ bool randomSeed : 1;
+} Agreement;
+
+typedef struct {
+ bool connected;
+ /* This NetConnection is connected. */
+ bool disconnected;
+ /* This NetConnection has been disconnected. This implies
+ * !connected. It is only set if the NetConnection was once
+ * connected, but is no longer. */
+ bool discriminant;
+ /* If it is true here, it is false on the remote side
+ * of the same connection. It may be used to break ties.
+ * It is guaranteed not to change during a connection. Undefined
+ * while not connected. */
+ HandShakeFlags handshake;
+ ReadyFlags ready;
+ ResetFlags reset;
+ Agreement agreement;
+ size_t inputDelay;
+ /* Used during negotiation of the actual inputDelay. This
+ * field does NOT necessarilly contain the actual input delay,
+ * which is a property of the game, not of any specific
+ * connection. Use getBattleInputDelay() to get at it. */
+#ifdef NETPLAY_CHECKSUM
+ size_t checksumInterval;
+#endif
+} NetStateFlags;
+
+struct NetConnection {
+ NetDescriptor *nd;
+ int player;
+ // Number of the player for this connection, as it is
+ // known locally. For the other player, it may be
+ // differently.
+ NetState state;
+ NetStateFlags stateFlags;
+
+ NetConnection_ReadyCallback readyCallback;
+ // Called when both sides have indicated that they are ready.
+ // Set by Netplay_localReady().
+ void *readyCallbackArg;
+ // Extra argument for readyCallback().
+ // XXX: when is this cleaned up if a connection is broken?
+
+ NetConnection_ResetCallback resetCallback;
+ // Called when a reset has been signalled and confirmed.
+ // Set by Netplay_localReset().
+ void *resetCallbackArg;
+ // Extra argument for resetCallback().
+ // XXX: when is this cleaned up if a connection is broken?
+
+ const NetplayPeerOptions *options;
+ PacketQueue queue;
+#ifdef NETPLAY_STATISTICS
+ NetStatistics statistics;
+#endif
+#ifdef NETPLAY_CHECKSUM
+ ChecksumBuffer checksumBuffer;
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+ uio_Stream *debugFile;
+#endif
+
+ NetConnection_ConnectCallback connectCallback;
+ NetConnection_CloseCallback closeCallback;
+ // Called when the NetConnection becomes disconnected.
+ NetConnection_ErrorCallback errorCallback;
+ NetConnection_DeleteCallback deleteCallback;
+ // Called when the NetConnection is destroyed.
+ uint8 *readBuf;
+ uint8 *readEnd;
+ NetConnectionStateData *stateData;
+ // State dependant information.
+ void *extra;
+};
+
+struct ConnectStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+
+ bool isServer;
+ union {
+ struct ConnectState *connectState;
+ struct ListenState *listenState;
+ } state;
+};
+
+#endif /* NETCONNECTION_INTERNAL */
+
+
+NetConnection *NetConnection_open(int player,
+ const NetplayPeerOptions *options,
+ NetConnection_ConnectCallback connectCallback,
+ NetConnection_CloseCallback closeCallback,
+ NetConnection_ErrorCallback errorCallback,
+ NetConnection_DeleteCallback deleteCallback, void *extra);
+void NetConnection_close(NetConnection *conn);
+bool NetConnection_isConnected(const NetConnection *conn);
+
+void NetConnection_doErrorCallback(NetConnection *nd, int err);
+
+void NetConnection_setStateData(NetConnection *conn,
+ NetConnectionStateData *stateData);
+NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn);
+void NetConnection_setExtra(NetConnection *conn, void *extra);
+void *NetConnection_getExtra(const NetConnection *conn);
+void NetConnection_setState(NetConnection *conn, NetState state);
+NetState NetConnection_getState(const NetConnection *conn);
+bool NetConnection_getDiscriminant(const NetConnection *conn);
+const NetplayPeerOptions *NetConnection_getPeerOptions(
+ const NetConnection *conn);
+int NetConnection_getPlayerNr(const NetConnection *conn);
+size_t NetConnection_getInputDelay(const NetConnection *conn);
+#ifdef NETPLAY_CHECKSUM
+ChecksumBuffer *NetConnection_getChecksumBuffer(NetConnection *conn);
+size_t NetConnection_getChecksumInterval(const NetConnection *conn);
+#endif
+#ifdef NETPLAY_STATISTICS
+NetStatistics *NetConnection_getStatistics(NetConnection *conn);
+#endif
+
+void NetConnection_setReadyCallback(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg);
+NetConnection_ReadyCallback NetConnection_getReadyCallback(
+ const NetConnection *conn);
+void *NetConnection_getReadyCallbackArg(const NetConnection *conn);
+
+void NetConnection_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *arg);
+NetConnection_ResetCallback NetConnection_getResetCallback(
+ const NetConnection *conn);
+void *NetConnection_getResetCallbackArg(const NetConnection *conn);
+
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+extern uio_Stream *netplayDebugFile;
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_ */
+
+
diff --git a/src/uqm/supermelee/netplay/netinput.c b/src/uqm/supermelee/netplay/netinput.c
new file mode 100644
index 0000000..9823deb
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netinput.c
@@ -0,0 +1,157 @@
+/*
+ * 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 "netinput.h"
+
+#include "../../intel.h"
+ // for NETWORK_CONTROL
+#include "../../setup.h"
+ // For PlayerControl
+#include "libs/log.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+
+static BattleInputBuffer battleInputBuffers[NUM_PLAYERS];
+static size_t BattleInput_inputDelay;
+
+// Call before initBattleInputBuffers()
+void
+setBattleInputDelay(size_t delay) {
+ BattleInput_inputDelay = delay;
+}
+
+size_t
+getBattleInputDelay(void) {
+ return BattleInput_inputDelay;
+}
+
+static void
+BattleInputBuffer_init(BattleInputBuffer *bib, size_t bufSize) {
+ bib->buf = malloc(bufSize * sizeof (BATTLE_INPUT_STATE));
+ bib->maxSize = bufSize;
+ bib->first = 0;
+ bib->size = 0;
+}
+
+static void
+BattleInputBuffer_uninit(BattleInputBuffer *bib) {
+ if (bib->buf != NULL) {
+ free(bib->buf);
+ bib->buf = NULL;
+ }
+ bib->maxSize = 0;
+ bib->first = 0;
+ bib->size = 0;
+}
+
+void
+initBattleInputBuffers(void) {
+ size_t player;
+ int bufSize = BattleInput_inputDelay * 2 + 2;
+
+ // The input 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'.
+ // Then side 2 receives all this input, and progresses to 'delay' frames
+ // after the last frame the received input originated from, and blocks
+ // in the frame after it.
+ // So it sent input for frames 'n' through 'n + delay + delay + 1'.
+ // The input for these '2*delay + 2' frames are still
+ // unhandled by side 1, so it needs buffer space for this.
+ //
+ // Initially the buffer is filled with inputDelay zeroes,
+ // so that a party can process at least that much frames.
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleInputBuffer *bib = &battleInputBuffers[player];
+ BattleInputBuffer_init(bib, bufSize);
+
+ {
+ // Initially a party must be able to process at least inputDelay
+ // frames, so we fill the buffer with inputDelay zeros.
+ size_t i;
+ for (i = 0; i < BattleInput_inputDelay; i++)
+ BattleInputBuffer_push(bib, (BATTLE_INPUT_STATE) 0);
+ }
+ }
+}
+
+void
+uninitBattleInputBuffers(void)
+{
+ size_t player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleInputBuffer *bib;
+
+ bib = &battleInputBuffers[player];
+ BattleInputBuffer_uninit(bib);
+ }
+}
+
+// On error, returns false and sets errno.
+bool
+BattleInputBuffer_push(BattleInputBuffer *bib, BATTLE_INPUT_STATE input)
+{
+ size_t next;
+
+ if (bib->size == bib->maxSize) {
+ // No more space.
+ log_add(log_Error, "NETPLAY: battleInputBuffer full.\n");
+ errno = ENOBUFS;
+ return false;
+ }
+
+ next = (bib->first + bib->size) % bib->maxSize;
+ bib->buf[next] = input;
+ bib->size++;
+ return true;
+}
+
+// On error, returns false and sets errno, and *input remains unchanged.
+bool
+BattleInputBuffer_pop(BattleInputBuffer *bib, BATTLE_INPUT_STATE *input)
+{
+ if (bib->size == 0)
+ {
+ // Buffer is empty.
+ errno = EAGAIN;
+ return false;
+ }
+
+ *input = bib->buf[bib->first];
+ bib->first = (bib->first + 1) % bib->maxSize;
+ bib->size--;
+ return true;
+}
+
+BattleInputBuffer *
+getBattleInputBuffer(size_t player) {
+ return &battleInputBuffers[player];
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netinput.h b/src/uqm/supermelee/netplay/netinput.h
new file mode 100644
index 0000000..2afdf02
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netinput.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETINPUT_H_
+#define UQM_SUPERMELEE_NETPLAY_NETINPUT_H_
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ // for NUM_PLAYERS
+
+typedef struct BattleInputBuffer {
+ BATTLE_INPUT_STATE *buf;
+ // Cyclic buffer. if 'size' > 0, then 'first' is an index to
+ // the first used entry, 'size' is the number of used
+ // entries in the buffer, and (first + size) % maxSize is the
+ // index to just past the end of the buffer.
+ size_t maxSize;
+ size_t first;
+ size_t size;
+} BattleInputBuffer;
+
+void setBattleInputDelay(size_t delay);
+size_t getBattleInputDelay(void);
+void initBattleInputBuffers(void);
+void uninitBattleInputBuffers(void);
+bool BattleInputBuffer_push(BattleInputBuffer *bib,
+ BATTLE_INPUT_STATE input);
+bool BattleInputBuffer_pop(BattleInputBuffer *bib,
+ BATTLE_INPUT_STATE *input);
+
+BattleInputBuffer *getBattleInputBuffer(size_t player);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETINPUT_H_ */
diff --git a/src/uqm/supermelee/netplay/netmelee.c b/src/uqm/supermelee/netplay/netmelee.c
new file mode 100644
index 0000000..e9368fd
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmelee.c
@@ -0,0 +1,740 @@
+/*
+ * 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 "netmelee.h"
+#include "libs/async.h"
+#include "libs/callback.h"
+#include "libs/log.h"
+#include "libs/net.h"
+#include "netinput.h"
+#include "netmisc.h"
+#include "netsend.h"
+#include "notify.h"
+#include "packetq.h"
+#include "proto/npconfirm.h"
+#include "proto/ready.h"
+#include "proto/reset.h"
+
+#include "../../battlecontrols.h"
+ // for NetworkInputContext
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+ // for NUM_PLAYERS
+#include "../../globdata.h"
+ // for GLOBAL
+
+#include <errno.h>
+#include <stdlib.h>
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+NetConnection *netConnections[NUM_PLAYERS];
+size_t numNetConnections;
+
+void
+addNetConnection(NetConnection *conn, int playerNr) {
+ netConnections[playerNr] = conn;
+ numNetConnections++;
+}
+
+void
+removeNetConnection(int playerNr) {
+ netConnections[playerNr] = NULL;
+ numNetConnections--;
+}
+
+size_t
+getNumNetConnections(void) {
+ return numNetConnections;
+}
+
+// If the callback function returns 'false', the function will immediately
+// return with 'false'. Otherwise it will return 'true' after calling
+// the callback function for each connected player.
+bool
+forEachConnectedPlayer(ForEachConnectionCallback callback, void *arg) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ if (!(*callback)(conn, arg))
+ return false;
+ }
+ return true;
+}
+
+void
+closeAllConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+
+ if (conn != NULL)
+ closePlayerNetworkConnection(player);
+ }
+}
+
+void
+closeDisconnectedConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+
+ if (conn != NULL && !NetConnection_isConnected(conn))
+ closePlayerNetworkConnection(player);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+
+struct melee_state *
+NetMelee_getMeleeState(NetConnection *conn) {
+ if (NetConnection_getState(conn) > NetState_connecting) {
+ BattleStateData *battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ return battleStateData->meleeState;
+ } else {
+ return (struct melee_state *) NetConnection_getExtra(conn);
+ }
+}
+
+struct battlestate_struct *
+NetMelee_getBattleState(NetConnection *conn) {
+ if (NetConnection_getState(conn) > NetState_connecting) {
+ BattleStateData *battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ return battleStateData->battleState;
+ } else {
+ return NULL;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static inline void
+netInputAux(uint32 timeoutMs) {
+ NetManager_process(&timeoutMs);
+ // This may cause more packets to be queued, hence the
+ // flushPacketQueues().
+ Async_process();
+ flushPacketQueues();
+ // During the flush, a disconnect may be noticed, which triggers
+ // another callback. It must be handled immediately, before
+ // another flushPacketQueue() can occur, which would not know
+ // that the socket is no longer valid.
+ // TODO: modify the close handling so this order isn't
+ // necessary.
+ Callback_process();
+}
+
+// Check the network connections for input.
+void
+netInput(void) {
+ netInputAux(0);
+}
+
+void
+netInputBlocking(uint32 timeoutMs) {
+ uint32 nextAsyncMs;
+
+ nextAsyncMs = Async_timeBeforeNextMs();
+ if (nextAsyncMs < timeoutMs)
+ timeoutMs = nextAsyncMs;
+
+ netInputAux(timeoutMs);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+// Send along all pending network packets.
+void
+flushPacketQueues(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ int flushStatus;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ flushStatus = flushPacketQueue(conn);
+ if (flushStatus == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
+ closePlayerNetworkConnection(player);
+ }
+}
+
+void
+confirmConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_confirm(conn);
+ }
+}
+
+void
+cancelConfirmations(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_cancelConfirmation(conn);
+ }
+}
+
+void
+connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_localReady(conn, callback, arg, true);
+ }
+}
+
+bool
+allConnected(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ return false;
+ }
+ return true;
+}
+
+void
+initBattleStateDataConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleStateData *battleStateData;
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ battleStateData->endFrameCount = 0;
+ }
+}
+
+void
+setBattleStateConnections(struct battlestate_struct *bs) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleStateData *battleStateData;
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ battleStateData->battleState = bs;
+ }
+}
+
+BATTLE_INPUT_STATE
+networkBattleInput(NetworkInputContext *context, STARSHIP *StarShipPtr) {
+ BattleInputBuffer *bib = getBattleInputBuffer(context->playerNr);
+ BATTLE_INPUT_STATE result;
+
+ for (;;) {
+ bool ok;
+
+#if 0
+ // This is a useful debugging trick. By enabling this #if
+ // block, this side will always lag the maximum number of frames
+ // behind the other side. When the remote side stops on some event
+ // (a breakpoint or so), this side will stop too, waiting for input
+ // in the loop below, but it won't have processed the frame that
+ // triggered the event yet. If you then jump over this 'if'
+ // statement here, you can walk through the decisive frames
+ // manually. Works best with no input delay.
+ if (bib->size <= getBattleInputDelay() + 1) {
+ ok = false;
+ } else
+#endif
+ ok = BattleInputBuffer_pop(bib, &result);
+ // Get the input from the front of the
+ // buffer.
+ if (ok)
+ break;
+
+ {
+ NetConnection *conn = netConnections[context->playerNr];
+
+ // First try whether there is incoming data, without blocking.
+ // If there isn't any, only then give a warning, and then
+ // block after all.
+ netInput();
+ if (!NetConnection_isConnected(conn))
+ {
+ // Connection aborted.
+ GLOBAL(CurrentActivity) |= CHECK_ABORT;
+ return (BATTLE_INPUT_STATE) 0;
+ }
+
+ if (GLOBAL(CurrentActivity) & CHECK_ABORT)
+ return (BATTLE_INPUT_STATE) 0;
+
+#if 0
+ log_add(log_Warning, "NETPLAY: [%d] stalling for "
+ "network input. Increase the input delay if this "
+ "happens a lot.\n", context->playerNr);
+#endif
+#define MAX_BLOCK_TIME 500
+ netInputBlocking(MAX_BLOCK_TIME);
+ if (!NetConnection_isConnected(conn))
+ {
+ // Connection aborted.
+ GLOBAL(CurrentActivity) |= CHECK_ABORT;
+ return (BATTLE_INPUT_STATE) 0;
+ }
+ }
+ }
+
+ (void) StarShipPtr;
+ return result;
+}
+
+static void
+deleteConnectionCallback(NetConnection *conn) {
+ removeNetConnection(NetConnection_getPlayerNr(conn));
+}
+
+NetConnection *
+openPlayerNetworkConnection(COUNT player, void *extra) {
+ NetConnection *conn;
+
+ assert(netConnections[player] == NULL);
+
+ conn = NetConnection_open(player,
+ &netplayOptions.peer[player], NetMelee_connectCallback,
+ NetMelee_closeCallback, NetMelee_errorCallback,
+ deleteConnectionCallback, extra);
+
+ addNetConnection(conn, player);
+ return conn;
+}
+
+void
+closePlayerNetworkConnection(COUNT player) {
+ assert(netConnections[player] != NULL);
+
+ NetConnection_close(netConnections[player]);
+}
+
+bool
+setupInputDelay(size_t localInputDelay) {
+ COUNT player;
+ bool haveNetworkPlayer = false;
+ // We have at least one network controlled player.
+ size_t inputDelay = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ haveNetworkPlayer = true;
+ if (NetConnection_getInputDelay(conn) > inputDelay)
+ inputDelay = NetConnection_getInputDelay(conn);
+ }
+
+ if (haveNetworkPlayer && inputDelay < localInputDelay)
+ inputDelay = localInputDelay;
+
+ setBattleInputDelay(inputDelay);
+ return true;
+}
+
+static bool
+setStateConnection(NetConnection *conn, void *arg) {
+ const NetState *state = (NetState *) arg;
+ NetConnection_setState(conn, *state);
+ return true;
+}
+
+bool
+setStateConnections(NetState state) {
+ return forEachConnectedPlayer(setStateConnection, &state);
+}
+
+static bool
+sendAbortConnection(NetConnection *conn, void *arg) {
+ const NetplayAbortReason *reason = (NetplayAbortReason *) arg;
+ sendAbort(conn, *reason);
+ return true;
+}
+
+bool
+sendAbortConnections(NetplayAbortReason reason) {
+ return forEachConnectedPlayer(sendAbortConnection, &reason);
+}
+
+static bool
+resetConnection(NetConnection *conn, void *arg) {
+ const NetplayResetReason *reason = (NetplayResetReason *) arg;
+ Netplay_localReset(conn, *reason);
+ return true;
+}
+
+bool
+resetConnections(NetplayResetReason reason) {
+ return forEachConnectedPlayer(resetConnection, &reason);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ NetConnection_ReadyCallback readyCallback;
+ void *readyCallbackArg;
+ bool notifyRemote;
+} LocalReadyConnectionArg;
+
+static bool
+localReadyConnection(NetConnection *conn, void *arg) {
+ LocalReadyConnectionArg *readyArg = (LocalReadyConnectionArg *) arg;
+ Netplay_localReady(conn, readyArg->readyCallback,
+ readyArg->readyCallbackArg, readyArg->notifyRemote);
+ return true;
+}
+
+bool
+localReadyConnections(NetConnection_ReadyCallback readyCallback,
+ void *readyArg, bool notifyRemote) {
+ LocalReadyConnectionArg arg;
+ arg.readyCallback = readyCallback;
+ arg.readyCallbackArg = readyArg;
+ arg.notifyRemote = notifyRemote;
+
+ return forEachConnectedPlayer(localReadyConnection, &arg);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
+
+typedef struct NegotiateReadyState NegotiateReadyState;
+struct NegotiateReadyState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN(*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetState nextState;
+ bool done;
+};
+
+static BOOLEAN
+negotiateReadyInputFunc(NegotiateReadyState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need negotiateReadyInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+// Called when both sides are ready
+static void
+negotiateReadyBothReadyCallback(NetConnection *conn, void *arg) {
+ NegotiateReadyState *state =(NegotiateReadyState *) arg;
+
+ NetConnection_setState(conn, state->nextState);
+ // This has to be done immediately, as more packets in the
+ // receive queue may be handled by the netInput() call that
+ // triggered this callback.
+ // This is the reason for the nextState argument to
+ // negotiateReady(); setting the state after the call to
+ // negotiateReady() would be too late.
+ state->done = true;
+}
+
+bool
+negotiateReady(NetConnection *conn, bool notifyRemote, NetState nextState) {
+ NegotiateReadyState state;
+ state.InputFunc = (BOOLEAN(*)(void *)) negotiateReadyInputFunc;
+ state.conn = conn;
+ state.nextState = nextState;
+ state.done = false;
+
+ Netplay_localReady(conn, negotiateReadyBothReadyCallback,
+ (void *) &state, notifyRemote);
+ flushPacketQueue(conn);
+ if (!state.done)
+ DoInput(&state, FALSE);
+
+ return NetConnection_isConnected(conn);
+}
+
+// Wait for all connections to get ready.
+// XXX: Right now all connections are handled one by one. Handling them all
+// at once would be faster but would require more work, which is
+// not worth it as the time is minimal and this function is not
+// time critical.
+bool
+negotiateReadyConnections(bool notifyRemote, NetState nextState) {
+ COUNT player;
+ size_t numDisconnected = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn)) {
+ numDisconnected++;
+ continue;
+ }
+
+ negotiateReady(conn, notifyRemote, nextState);
+ }
+
+ return numDisconnected == 0;
+}
+
+typedef struct WaitReadyState WaitReadyState;
+struct WaitReadyState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN (*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetConnection_ReadyCallback readyCallback;
+ void *readyCallbackArg;
+ bool done;
+};
+
+static void
+waitReadyCallback(NetConnection *conn, void *arg) {
+ WaitReadyState *state =(WaitReadyState *) arg;
+ state->done = true;
+
+ // Call the original callback.
+ state->readyCallback(conn, state->readyCallbackArg);
+}
+
+static BOOLEAN
+waitReadyInputFunc(WaitReadyState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need negotiateReadyInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+bool
+waitReady(NetConnection *conn) {
+ WaitReadyState state;
+ state.InputFunc =(BOOLEAN(*)(void *)) waitReadyInputFunc;
+ state.conn = conn;
+ state.readyCallback = NetConnection_getReadyCallback(conn);
+ state.readyCallbackArg = NetConnection_getReadyCallbackArg(conn);
+ state.done = false;
+
+ NetConnection_setReadyCallback(conn, waitReadyCallback, (void *) &state);
+
+ DoInput(&state, FALSE);
+
+ return NetConnection_isConnected(conn);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct WaitResetState WaitResetState;
+struct WaitResetState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN(*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetState nextState;
+ bool done;
+};
+
+static BOOLEAN
+waitResetInputFunc(WaitResetState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need waitResetInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+// Called when both sides are reset.
+static void
+waitResetBothResetCallback(NetConnection *conn, void *arg) {
+ WaitResetState *state = (WaitResetState *) arg;
+
+ if (state->nextState != (NetState) -1) {
+ NetConnection_setState(conn, state->nextState);
+ // This has to be done immediately, as more packets in the
+ // receive queue may be handled by the netInput() call that
+ // triggered this callback.
+ // This is the reason for the nextState argument to
+ // waitReset(); setting the state after the call to
+ // waitReset() would be too late.
+ }
+ state->done = true;
+}
+
+bool
+waitReset(NetConnection *conn, NetState nextState) {
+ WaitResetState state;
+ state.InputFunc = (BOOLEAN(*)(void *)) waitResetInputFunc;
+ state.conn = conn;
+ state.nextState = nextState;
+ state.done = false;
+
+ Netplay_setResetCallback(conn, waitResetBothResetCallback,
+ (void *) &state);
+ if (state.done)
+ goto out;
+
+
+ if (!Netplay_isLocalReset(conn)) {
+ Netplay_localReset(conn, ResetReason_manualReset);
+ flushPacketQueue(conn);
+ }
+
+ if (!state.done)
+ DoInput(&state, FALSE);
+
+out:
+ return NetConnection_isConnected(conn);
+}
+
+// Wait until we have received a reset packet from all connections. If we
+// ourselves have not sent a reset packet, one is sent, with reason
+// 'manualReset'.
+// XXX: Right now all connections are handled one by one. Handling them all
+// at once would be faster but would require more work, which is
+// not worth it as the time is minimal and this function is not
+// time critical.
+// Use '(NetState) -1' for nextState to keep the current state.
+bool
+waitResetConnections(NetState nextState) {
+ COUNT player;
+ size_t numDisconnected = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn)) {
+ numDisconnected++;
+ continue;
+ }
+
+ waitReset(conn, nextState);
+ }
+
+ return numDisconnected == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
diff --git a/src/uqm/supermelee/netplay/netmelee.h b/src/uqm/supermelee/netplay/netmelee.h
new file mode 100644
index 0000000..83f3562
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmelee.h
@@ -0,0 +1,90 @@
+/*
+ * 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
+ */
+
+#if !defined(UQM_SUPERMELEE_NETPLAY_NETMELEE_H_) && defined(NETPLAY)
+#define UQM_SUPERMELEE_NETPLAY_NETMELEE_H_
+
+#include "netplay.h"
+#include "netinput.h"
+#include "netconnection.h"
+#include "packetsenders.h"
+
+#include "../../battlecontrols.h"
+ // for NetworkInputContext
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../races.h"
+ // for STARSHIP
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern struct NetConnection *netConnections[];
+
+
+void addNetConnection(NetConnection *conn, int playerNr);
+void removeNetConnection(int playerNr);
+void closeAllConnections(void);
+void closeDisconnectedConnections(void);
+size_t getNumNetConnections(void);
+typedef bool(*ForEachConnectionCallback)(NetConnection *conn, void *arg);
+bool forEachConnectedPlayer(ForEachConnectionCallback callback, void *arg);
+
+struct melee_state *NetMelee_getMeleeState(NetConnection *conn);
+struct battlestate_struct *NetMelee_getBattleState(NetConnection *conn);
+
+void netInput(void);
+void netInputBlocking(uint32 timeoutMs);
+void flushPacketQueues(void);
+
+void confirmConnections(void);
+void cancelConfirmations(void);
+void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg);
+
+bool allConnected(void);
+
+void initBattleStateDataConnections(void);
+void setBattleStateConnections(struct battlestate_struct *bs);
+
+BATTLE_INPUT_STATE networkBattleInput(NetworkInputContext *context,
+ STARSHIP *StarShipPtr);
+
+NetConnection *openPlayerNetworkConnection(COUNT player, void *extra);
+void closePlayerNetworkConnection(COUNT player);
+
+bool setupInputDelay(size_t localInputDelay);
+bool setStateConnections(NetState state);
+bool sendAbortConnections(NetplayAbortReason reason);
+bool resetConnections(NetplayResetReason reason);
+bool localReadyConnections(NetConnection_ReadyCallback readyCallback,
+ void *arg, bool notifyRemote);
+
+bool negotiateReady(NetConnection *conn, bool notifyRemote,
+ NetState nextState);
+bool negotiateReadyConnections(bool notifyRemote, NetState nextState);
+bool waitReady(NetConnection *conn);
+
+bool waitReset(NetConnection *conn, NetState nextState);
+bool waitResetConnections(NetState nextState);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETMELEE_H_ */
diff --git a/src/uqm/supermelee/netplay/netmisc.c b/src/uqm/supermelee/netplay/netmisc.c
new file mode 100644
index 0000000..3ab2f72
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmisc.c
@@ -0,0 +1,134 @@
+/*
+ * 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
+ */
+
+#include "netplay.h"
+#include "netmisc.h"
+
+#include "netmelee.h"
+#include "notifyall.h"
+#include "packetsenders.h"
+#include "proto/ready.h"
+
+#include "../melee.h"
+ // For feedback functions.
+
+#include <stdlib.h>
+
+
+static BattleStateData *BattleStateData_alloc(void);
+static void BattleStateData_free(BattleStateData *battleStateData);
+static inline BattleStateData *BattleStateData_new(
+ struct melee_state *meleeState,
+ struct battlestate_struct *battleState,
+ struct getmelee_struct *getMeleeState);
+static void BattleStateData_delete(BattleStateData *battleStateData);
+
+
+static BattleStateData *
+BattleStateData_alloc(void) {
+ return (BattleStateData *) malloc(sizeof (BattleStateData));
+}
+
+static void
+BattleStateData_free(BattleStateData *battleStateData) {
+ free(battleStateData);
+}
+
+static inline BattleStateData *
+BattleStateData_new(struct melee_state *meleeState,
+ struct battlestate_struct *battleState,
+ struct getmelee_struct *getMeleeState) {
+ BattleStateData *battleStateData = BattleStateData_alloc();
+ battleStateData->releaseFunction =
+ (NetConnectionStateData_ReleaseFunction) BattleStateData_delete;
+ battleStateData->meleeState = meleeState;
+ battleStateData->battleState = battleState;
+ battleStateData->getMeleeState = getMeleeState;
+ return battleStateData;
+}
+
+static void
+BattleStateData_delete(BattleStateData *battleStateData) {
+ BattleStateData_free(battleStateData);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static void NetMelee_enterState_inSetup(NetConnection *conn, void *arg);
+
+// Called when a connection has been established.
+void
+NetMelee_connectCallback(NetConnection *conn) {
+ BattleStateData *battleStateData;
+ struct melee_state *meleeState;
+
+ meleeState = (struct melee_state *) NetConnection_getExtra(conn);
+ battleStateData = BattleStateData_new(meleeState, NULL, NULL);
+ NetConnection_setStateData(conn, (void *) battleStateData);
+ NetConnection_setExtra(conn, NULL);
+
+ // We have sent no teams yet. Initialize the state accordingly.
+ MeleeSetup_resetSentTeams (meleeState->meleeSetup);
+
+ sendInit(conn);
+ Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false);
+}
+
+// Called when a connection is closed.
+void
+NetMelee_closeCallback(NetConnection *conn) {
+ closeFeedback(conn);
+}
+
+// Called when a network error occurs during connect.
+void
+NetMelee_errorCallback(NetConnection *conn,
+ const NetConnectionError *error) {
+ errorFeedback(conn);
+ (void) error;
+}
+
+// Callback function for when both sides have finished initialisation after
+// initial connect.
+static void
+NetMelee_enterState_inSetup(NetConnection *conn, void *arg) {
+ BattleStateData *battleStateData;
+ struct melee_state *meleeState;
+ int player;
+
+ NetConnection_setState(conn, NetState_inSetup);
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ meleeState = battleStateData->meleeState;
+
+ player = NetConnection_getPlayerNr(conn);
+
+ connectedFeedback(conn);
+
+ // Send our team to the remote side.
+ // XXX This only works with 2 players atm.
+ assert (NUM_PLAYERS == 2);
+ Melee_bootstrapSyncTeam (meleeState, player);
+
+ flushPacketQueues();
+
+ (void) arg;
+}
+
diff --git a/src/uqm/supermelee/netplay/netmisc.h b/src/uqm/supermelee/netplay/netmisc.h
new file mode 100644
index 0000000..ea14921
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmisc.h
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETMISC_H_
+#define UQM_SUPERMELEE_NETPLAY_NETMISC_H_
+
+typedef struct BattleStateData BattleStateData;
+
+#include "netconnection.h"
+#include "netstate.h"
+#include "types.h"
+
+#include "../../battle.h"
+ // for BattleFrameCounter, BATTLE_FRAME_RATE
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct BattleStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+
+ struct melee_state *meleeState;
+ struct battlestate_struct *battleState;
+ struct getmelee_struct *getMeleeState;
+ BattleFrameCounter endFrameCount;
+};
+
+
+void NetMelee_connectCallback(NetConnection *conn);
+void NetMelee_closeCallback(NetConnection *conn);
+void NetMelee_errorCallback(NetConnection *conn,
+ const NetConnectionError *error);
+
+void NetMelee_reenterState_inSetup(NetConnection *conn);
+
+
+// Returns true iff the connection is in a state where the confirmation
+// handshake is meaningful. Right now this is only when we're in the
+// pre-game setup menu.
+static inline bool
+handshakeMeaningful(NetState state) {
+ return state == NetState_inSetup;
+}
+
+static inline bool
+readyFlagsMeaningful(NetState state) {
+ return state == NetState_init ||
+ state == NetState_preBattle ||
+ state == NetState_selectShip ||
+ state == NetState_interBattle ||
+ state == NetState_inBattle ||
+ state == NetState_endingBattle ||
+ state == NetState_endingBattle2;
+}
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETMISC_H_ */
diff --git a/src/uqm/supermelee/netplay/netoptions.c b/src/uqm/supermelee/netplay/netoptions.c
new file mode 100644
index 0000000..a74e1a3
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netoptions.c
@@ -0,0 +1,39 @@
+/*
+ * 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
+ */
+
+#include "netoptions.h"
+
+NetplayOptions netplayOptions = {
+ /* .metaServer = */ "uqm.stack.nl",
+ /* .metaPort = */ "21836",
+ /* .peer = */ {
+ /* [0] Player 1 (bottom) */ {
+ /* .isServer = */ true,
+ /* .host = */ "localhost",
+ /* .port = */ "21837" /* 0x554d - "UM" */,
+ },
+ /* [1] Player 2 (top) */ {
+ /* .isServer = */ true,
+ /* .host = */ "localhost",
+ /* .port = */ "21837" /* 0x554d - "UM" */,
+ },
+ },
+ /* .inputDelay = */ 2,
+};
+
+
diff --git a/src/uqm/supermelee/netplay/netoptions.h b/src/uqm/supermelee/netplay/netoptions.h
new file mode 100644
index 0000000..77b1637
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netoptions.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_
+#define UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_
+
+#include "types.h"
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define NETPLAY_NUM_PLAYERS 2
+ // Not using NUM_PLAYERS because that would mean we'd have
+ // to include init.h, and all that comes with it.
+ // XXX: Don't use a hardcoded limit.
+
+typedef struct {
+ bool isServer;
+ const char *host;
+ const char *port;
+ // May be given as a service name.
+} NetplayPeerOptions;
+
+typedef struct {
+ const char *metaServer;
+ const char *metaPort;
+ // May be given as a service name.
+ NetplayPeerOptions peer[NETPLAY_NUM_PLAYERS];
+ size_t inputDelay;
+} NetplayOptions;
+extern NetplayOptions netplayOptions;
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_ */
diff --git a/src/uqm/supermelee/netplay/netplay.h b/src/uqm/supermelee/netplay/netplay.h
new file mode 100644
index 0000000..b78c69a
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netplay.h
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+#if !defined(UQM_SUPERMELEE_NETPLAY_NETPLAY_H_) && defined(NETPLAY)
+#define UQM_SUPERMELEE_NETPLAY_NETPLAY_H_
+
+// NETPLAY can either be unset (in which case we will never get here)
+// NETPLAY_FULL, or NETPLAY_IPV4 (disables IPv6)
+#define NETPLAY_IPV4 1
+#define NETPLAY_FULL 2
+
+#define NETPLAY_PROTOCOL_VERSION_MAJOR 0
+#define NETPLAY_PROTOCOL_VERSION_MINOR 4
+
+#define NETPLAY_MIN_UQM_VERSION_MAJOR 0
+#define NETPLAY_MIN_UQM_VERSION_MINOR 6
+#define NETPLAY_MIN_UQM_VERSION_PATCH 9
+
+#undef NETPLAY_DEBUG
+ /* Extra debugging for netplay */
+#undef NETPLAY_DEBUG_FILE
+ /* Dump extra debugging information to file.
+ * Implies NETPLAY_DEBUG.*/
+#define NETPLAY_STATISTICS
+ /* Keep some statistics */
+#define NETPLAY_CHECKSUM
+ /* Send/process checksums to verify that both sides of a network
+ * connection are still in sync.
+ * If not enabled, incoming checksum packets will be ignored.
+ * TODO: make compilation of crc.c and checksum.c conditional. */
+#define NETPLAY_CHECKSUM_INTERVAL 1
+ /* If NETPLAY_CHECKSUM is defined, this define determines
+ * every how many frames a checksum packet is sent. */
+
+#define NETPLAY_READBUFSIZE 2048
+#define NETPLAY_CONNECTTIMEOUT 2000
+ /* Time to wait for a connect() to succeed. In ms. */
+//#define NETPLAY_LISTENTIMEOUT 30000
+// /* Time to wait for a listen() to succeed. In ms. */
+#define NETPLAY_RETRYDELAY 2000
+ /* Time to wait after all addresses of a host have been tried
+ * before starting retrying them all. In ms. */
+#define NETPLAY_LISTEN_BACKLOG 2
+ /* Second argument to listen(). */
+
+
+#ifdef _MSC_VER
+# if _MSC_VER < 1300
+ /* NETPLAY_DEBUG_FILE requires the __VA_ARGS__ macro, which is
+ * not available on MSVC 6.0. */
+# undef NETPLAY_DEBUG_FILE
+# endif
+#endif
+
+#ifdef NETPLAY_DEBUG_FILE
+# define NETPLAY_DEBUG
+# define DUMP_CRC_OPS
+#endif
+
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETPLAY_H_ */
+
diff --git a/src/uqm/supermelee/netplay/netrcv.c b/src/uqm/supermelee/netplay/netrcv.c
new file mode 100644
index 0000000..b9ea5f7
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netrcv.c
@@ -0,0 +1,193 @@
+/*
+ * 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
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "port.h"
+
+#include "netconnection.h"
+#include "netrcv.h"
+#include "packet.h"
+
+#include "types.h"
+#include "libs/log.h"
+
+#include <errno.h>
+#include <string.h>
+
+
+// Try to get a single packet from a stream of data.
+// Returns 0 if the packet was successfully processed, or
+// -1 on an error, in which case the state is unchanged.
+static ssize_t
+dataReceivedSingle(NetConnection *conn, const uint8 *data,
+ size_t dataLen) {
+ uint32 packetLen;
+ PacketType type;
+ int result;
+
+ if (dataLen < sizeof (PacketHeader)) {
+ // Incomplete packet. We'll have to wait for the rest.
+ return 0;
+ }
+
+ packetLen = packetLength((const Packet *) data);
+ type = packetType((const Packet *) data);
+
+ if (!validPacketType(type)) {
+ log_add(log_Warning, "Packet with invalid type %d received.\n", type);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (packetLen < packetTypeData[type].len) {
+ // Bad len field of packet.
+ log_add(log_Warning, "Packet with bad length field received (type="
+ "%s, lenfield=%d.\n", packetTypeData[type].name,
+ packetLen);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (dataLen < packetLen) {
+ // Incomplete packet. We'll have to wait for the rest.
+ return 0;
+ }
+
+#ifdef NETPLAY_STATISTICS
+ NetConnection_getStatistics(conn)->packetsReceived++;
+ NetConnection_getStatistics(conn)->packetTypeReceived[type]++;
+#endif
+
+#ifdef NETPLAY_DEBUG
+ if (type != PACKET_BATTLEINPUT && type != PACKET_CHECKSUM) {
+ // Reporting BattleInput and Checksum would get so spammy that it
+ // would slow down the battle.
+ log_add(log_Debug, "NETPLAY: [%d] <== Received packet of type %s.\n",
+ NetConnection_getPlayerNr(conn), packetTypeData[type].name);
+ }
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] <== Received packet of type %s.\n",
+ NetConnection_getPlayerNr(conn), packetTypeData[type].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+
+ result = packetTypeData[type].handler(conn, data);
+ if (result == -1) {
+ // An error occured. errno is set by the handler.
+ return -1;
+ }
+
+ return packetLen;
+}
+
+// Try to get all the packets from a stream of data.
+// Returns the number of bytes processed.
+static ssize_t
+dataReceivedMulti(NetConnection *conn, const uint8 *data, size_t len) {
+ size_t processed;
+
+ processed = 0;
+ while (len > 0) {
+ ssize_t packetLen = dataReceivedSingle(conn, data, len);
+ if (packetLen == -1) {
+ // Bad packet. Errno is set.
+ return -1;
+ }
+
+ if (packetLen == 0) {
+ // No packet was processed. This means that no complete
+ // packet arrived.
+ break;
+ }
+
+ processed += packetLen;
+ data += packetLen;
+ len -= packetLen;
+ }
+
+ return processed;
+}
+
+void
+dataReadyCallback(NetDescriptor *nd) {
+ NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
+ Socket *socket = NetDescriptor_getSocket(nd);
+
+ for (;;) {
+ ssize_t numRead;
+ ssize_t numProcessed;
+
+ numRead = Socket_recv(socket, conn->readEnd,
+ NETPLAY_READBUFSIZE - (conn->readEnd - conn->readBuf), 0);
+ if (numRead == 0) {
+ // Other side closed the connection.
+ NetDescriptor_close(nd);
+ return;
+ }
+
+ if (numRead == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return; // No more data for now.
+ else if (errno == EINTR)
+ continue; // System call was interrupted. Retry.
+ else
+ {
+ int savedErrno = errno;
+ log_add(log_Error, "recv() failed: %s.\n",
+ strerror(errno));
+ NetConnection_doErrorCallback(conn, savedErrno);
+ NetDescriptor_close(nd);
+ return;
+ }
+ }
+
+ conn->readEnd += numRead;
+
+ numProcessed = dataReceivedMulti(conn, conn->readBuf,
+ conn->readEnd - conn->readBuf);
+ if (numProcessed == -1) {
+ // An error occured during processing.
+ // errno is set.
+ NetConnection_doErrorCallback(conn, errno);
+ NetDescriptor_close(nd);
+ return;
+ }
+ if (numProcessed == 0) {
+ // No packets could be processed. This means we need to receive
+ // more data first.
+ return;
+ }
+
+ // Some packets have been processed.
+ // We more any rest to the front of the buffer, to make room
+ // for more data.
+ // A cyclic buffer would obviate the need for this move,
+ // but it would complicate things a lot.
+ memmove(conn->readBuf, conn->readBuf + numProcessed,
+ (conn->readEnd - conn->readBuf) - numProcessed);
+ conn->readEnd -= numProcessed;
+ }
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/netrcv.h b/src/uqm/supermelee/netplay/netrcv.h
new file mode 100644
index 0000000..a7577d9
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netrcv.h
@@ -0,0 +1,34 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETRCV_H_
+#define UQM_SUPERMELEE_NETPLAY_NETRCV_H_
+
+#include "libs/net.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void dataReadyCallback(NetDescriptor *nd);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETRCV_H_ */
diff --git a/src/uqm/supermelee/netplay/netsend.c b/src/uqm/supermelee/netplay/netsend.c
new file mode 100644
index 0000000..b9f371f
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netsend.c
@@ -0,0 +1,95 @@
+/*
+ * 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
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "port.h"
+
+#include "netsend.h"
+#include "netconnection.h"
+#include "packet.h"
+#include "libs/log.h"
+#include "libs/net.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+
+int
+sendPacket(NetConnection *conn, Packet *packet) {
+ ssize_t sendResult;
+ size_t len;
+ Socket *socket;
+
+ assert(NetConnection_isConnected(conn));
+
+#ifdef NETPLAY_DEBUG
+ //if (packetType(packet) != PACKET_BATTLEINPUT &&
+ // packetType(packet) != PACKET_CHECKSUM) {
+ // // Reporting BattleInput or Checksum would get so spammy that it
+ // // would slow down the battle.
+ // log_add(log_Debug, "NETPLAY: [%d] ==> Sending packet of type %s.\n",
+ // conn->player, packetTypeData[packetType(packet)].name);
+ //}
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] ==> Sending packet of type %s.\n",
+ conn->player, packetTypeData[packetType(packet)].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+
+ socket = NetDescriptor_getSocket(conn->nd);
+
+ len = packetLength(packet);
+ while (len > 0) {
+ sendResult = Socket_send(socket, (void *) packet, len, 0);
+ if (sendResult >= 0) {
+ len -= sendResult;
+ continue;
+ }
+
+ switch (errno) {
+ case EINTR: // System call interrupted, retry;
+ continue;
+ case ECONNRESET: { // Connection reset by peer.
+ // keep errno
+ return -1;
+ }
+ default: {
+ // Should not happen.
+ int savedErrno = errno;
+ log_add(log_Error, "send() failed: %s.\n", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ }
+ }
+
+#ifdef NETPLAY_STATISTICS
+ NetConnection_getStatistics(conn)->packetsSent++;
+ NetConnection_getStatistics(conn)->packetTypeSent[packetType(packet)]++;
+#endif
+
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netsend.h b/src/uqm/supermelee/netplay/netsend.h
new file mode 100644
index 0000000..e005d03
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netsend.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETSEND_H_
+#define UQM_SUPERMELEE_NETPLAY_NETSEND_H_
+
+#include "packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int sendPacket(NetConnection *conn, Packet *packet);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETSEND_H_ */
diff --git a/src/uqm/supermelee/netplay/netstate.c b/src/uqm/supermelee/netplay/netstate.c
new file mode 100644
index 0000000..4382b94
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netstate.c
@@ -0,0 +1,48 @@
+/*
+ * 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
+ */
+
+#include "netplay.h"
+#include "netstate.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+
+#define DEFINE_NETSTATEDATA(stateName) \
+ { \
+ /* .name = */ #stateName, \
+ }
+NetStateData netStateData[] = {
+ DEFINE_NETSTATEDATA(unconnected),
+ DEFINE_NETSTATEDATA(connecting),
+ DEFINE_NETSTATEDATA(init),
+ DEFINE_NETSTATEDATA(inSetup),
+ DEFINE_NETSTATEDATA(preBattle),
+ DEFINE_NETSTATEDATA(interBattle),
+ DEFINE_NETSTATEDATA(selectShip),
+ DEFINE_NETSTATEDATA(inBattle),
+ DEFINE_NETSTATEDATA(endingBattle),
+ DEFINE_NETSTATEDATA(endingBattle2),
+};
+
+void
+NetConnectionStateData_release(NetConnectionStateData *stateData) {
+ assert(stateData->releaseFunction != NULL);
+ stateData->releaseFunction(stateData);
+}
+
diff --git a/src/uqm/supermelee/netplay/netstate.h b/src/uqm/supermelee/netplay/netstate.h
new file mode 100644
index 0000000..1d46d49
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netstate.h
@@ -0,0 +1,83 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETSTATE_H_
+#define UQM_SUPERMELEE_NETPLAY_NETSTATE_H_
+
+#include "port.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct NetConnectionStateData NetConnectionStateData;
+
+// State of a NetConnection.
+typedef enum {
+ NetState_unconnected, /* No connection initiated */
+ NetState_connecting, /* Connection being setup */
+ NetState_init, /* Initialising the connection */
+ NetState_inSetup, /* In the network game setup */
+ NetState_preBattle, /* Pre-battle initialisations */
+ NetState_interBattle, /* Negotiations between battles. */
+ NetState_selectShip, /* Selecting a ship in battle */
+ NetState_inBattle, /* Battle has started */
+ NetState_endingBattle, /* Both sides are prepared to end */
+ NetState_endingBattle2, /* Waiting for the final synchronisation */
+} NetState;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct {
+ const char *name;
+} NetStateData;
+extern NetStateData netStateData[];
+
+typedef void (*NetConnectionStateData_ReleaseFunction)(
+ NetConnectionStateData *stateData);
+
+#define NETCONNECTION_STATE_DATA_COMMON \
+ NetConnectionStateData_ReleaseFunction releaseFunction;
+
+struct
+NetConnectionStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+};
+
+void NetConnectionStateData_release(NetConnectionStateData *stateData);
+
+static inline bool
+NetState_battleActive(NetState state) {
+ return state == NetState_inBattle || state == NetState_endingBattle ||
+ state == NetState_endingBattle2;
+}
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETSTATE_H_ */
diff --git a/src/uqm/supermelee/netplay/notify.c b/src/uqm/supermelee/netplay/notify.c
new file mode 100644
index 0000000..8b35ead
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notify.c
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+// This files contains functions that notify the other side of local
+// changes.
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "notify.h"
+
+#include "packetsenders.h"
+
+
+// Convert a local player number to a side indication relative to this
+// party.
+static inline NetplaySide
+netSide(NetConnection *conn, int side) {
+ if (side == conn->player)
+ return NetplaySide_remote;
+
+ return NetplaySide_local;
+}
+
+void
+Netplay_Notify_shipSelected(NetConnection *conn, FleetShipIndex index) {
+ assert(NetConnection_getState(conn) == NetState_selectShip);
+
+ sendSelectShip(conn, index);
+}
+
+void
+Netplay_Notify_battleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
+ assert(NetConnection_getState(conn) == NetState_inBattle ||
+ NetConnection_getState(conn) == NetState_endingBattle ||
+ NetConnection_getState(conn) == NetState_endingBattle2);
+
+ sendBattleInput(conn, input);
+}
+
+void
+Netplay_Notify_setTeamName(NetConnection *conn, int player,
+ const char *name, size_t len) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendTeamName(conn, netSide(conn, player), name, len);
+}
+
+// On initialisation, or load.
+void
+Netplay_Notify_setFleet(NetConnection *conn, int player,
+ const MeleeShip *fleet, size_t fleetSize) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendFleet(conn, netSide(conn, player), fleet, fleetSize);
+}
+
+void
+Netplay_Notify_setShip(NetConnection *conn, int player,
+ FleetShipIndex index, MeleeShip ship) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendFleetShip(conn, netSide(conn, player), index, ship);
+}
+
+void
+Netplay_Notify_seedRandom(NetConnection *conn, uint32 seed) {
+ assert(NetConnection_getState(conn) == NetState_preBattle);
+
+ sendSeedRandom(conn, seed);
+ conn->stateFlags.agreement.randomSeed = true;
+}
+
+void
+Netplay_Notify_inputDelay(NetConnection *conn, uint32 delay) {
+ assert(NetConnection_getState(conn) == NetState_preBattle);
+
+ sendInputDelay(conn, delay);
+}
+
+void
+Netplay_Notify_frameCount(NetConnection *conn,
+ BattleFrameCounter frameCount) {
+ assert(NetConnection_getState(conn) == NetState_endingBattle);
+
+ sendFrameCount(conn, frameCount);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+Netplay_Notify_checksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ assert(NetState_battleActive(NetConnection_getState(conn)));
+
+ sendChecksum(conn, frameNr, checksum);
+}
+#endif /* NETPLAY_CHECKSUM */
+
+
+
+
diff --git a/src/uqm/supermelee/netplay/notify.h b/src/uqm/supermelee/netplay/notify.h
new file mode 100644
index 0000000..60a127d
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notify.h
@@ -0,0 +1,62 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NOTIFY_H_
+#define UQM_SUPERMELEE_NETPLAY_NOTIFY_H_
+
+#include "netplay.h"
+ // for NETPLAY_CHECKSUM
+#include "netconnection.h"
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#ifdef NETPLAY_CHECKSUM
+# include "checksum.h"
+#endif
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../meleesetup.h"
+ // for FleetShipIndex
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_Notify_shipSelected(NetConnection *conn, FleetShipIndex index);
+void Netplay_Notify_battleInput(NetConnection *conn,
+ BATTLE_INPUT_STATE input);
+void Netplay_Notify_setTeamName(NetConnection *conn, int player,
+ const char *name, size_t len);
+void Netplay_Notify_setFleet(NetConnection *conn, int player,
+ const MeleeShip *fleet, size_t fleetSize);
+void Netplay_Notify_setShip(NetConnection *conn, int player,
+ FleetShipIndex index, MeleeShip ship);
+void Netplay_Notify_seedRandom(NetConnection *conn, uint32 seed);
+void Netplay_Notify_inputDelay(NetConnection *conn, uint32 delay);
+void Netplay_Notify_frameCount(NetConnection *conn,
+ BattleFrameCounter frameCount);
+#ifdef NETPLAY_CHECKSUM
+void Netplay_Notify_checksum(NetConnection *conn,
+ BattleFrameCounter frameCount, Checksum checksum);
+#endif
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NOTIFY_H_ */
diff --git a/src/uqm/supermelee/netplay/notifyall.c b/src/uqm/supermelee/netplay/notifyall.c
new file mode 100644
index 0000000..2d0cc8a
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notifyall.c
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include "notifyall.h"
+
+#include "netmelee.h"
+#include "notify.h"
+
+// Notify the network connections of a team name change.
+void
+Netplay_NotifyAll_setTeamName (MELEE_STATE *pMS, size_t playerNr)
+{
+ const char *name;
+ size_t len;
+ size_t playerI;
+
+ name = MeleeSetup_getTeamName (pMS->meleeSetup, playerNr);
+ len = strlen (name);
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setTeamName (conn, playerNr, name, len);
+ }
+}
+
+// Notify the network connections of the configuration of a fleet.
+void
+Netplay_NotifyAll_setFleet (MELEE_STATE *pMS, size_t playerNr)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+ const MeleeShip *ships = MeleeSetup_getFleet (setup, playerNr);
+ size_t playerI;
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++) {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setFleet (conn, playerNr, ships, MELEE_FLEET_SIZE);
+ }
+}
+
+// Notify the network of a change in the configuration of a fleet.
+void
+Netplay_NotifyAll_setShip (MELEE_STATE *pMS, size_t playerNr, size_t index)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+ MeleeShip ship = MeleeSetup_getShip (setup, playerNr, index);
+
+ size_t playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setShip (conn, playerNr, index, ship);
+ }
+}
+
+static bool
+Netplay_NotifyAll_inputDelayCallback(NetConnection *conn, void *arg) {
+ const size_t *delay = (size_t *) arg;
+ Netplay_Notify_inputDelay(conn, *delay);
+ return true;
+}
+
+bool
+Netplay_NotifyAll_inputDelay(size_t delay) {
+ return forEachConnectedPlayer(Netplay_NotifyAll_inputDelayCallback,
+ &delay);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+Netplay_NotifyAll_checksum(BattleFrameCounter frameNr, Checksum checksum) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_Notify_checksum(conn, frameNr, checksum);
+ }
+}
+#endif /* NETPLAY_CHECKSUM */
+
+void
+Netplay_NotifyAll_battleInput(BATTLE_INPUT_STATE input) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_Notify_battleInput(conn, input);
+ }
+}
+
diff --git a/src/uqm/supermelee/netplay/notifyall.h b/src/uqm/supermelee/netplay/notifyall.h
new file mode 100644
index 0000000..d20ca81
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notifyall.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef NOTIFYALL_H
+#define NOTIFYALL_H
+
+#include "../../battle.h"
+#include "../../battlecontrols.h"
+#include "../melee.h"
+#ifdef NETPLAY_CHECKSUM
+# include "checksum.h"
+#endif /* NETPLAY_CHECKSUM */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_NotifyAll_setTeamName (MELEE_STATE *pMS, size_t playerNr);
+void Netplay_NotifyAll_setFleet (MELEE_STATE *pMS, size_t playerNr);
+void Netplay_NotifyAll_setShip (MELEE_STATE *pMS, size_t playerNr,
+ size_t index);
+
+bool Netplay_NotifyAll_inputDelay(size_t delay);
+#ifdef NETPLAY_CHECKSUM
+void Netplay_NotifyAll_checksum(BattleFrameCounter frameNr,
+ Checksum checksum);
+#endif /* NETPLAY_CHECKSUM */
+void Netplay_NotifyAll_battleInput(BATTLE_INPUT_STATE input);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* NOTIFYALL_H */
+
diff --git a/src/uqm/supermelee/netplay/packet.c b/src/uqm/supermelee/netplay/packet.c
new file mode 100644
index 0000000..442be17
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packet.c
@@ -0,0 +1,263 @@
+/*
+ * 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
+ */
+
+#include "netplay.h"
+#include "packet.h"
+
+#include "uqmversion.h"
+
+#include "netrcv.h"
+#include "packethandlers.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define DEFINE_PACKETDATA(name) \
+ { \
+ /* .len = */ sizeof (Packet_##name), \
+ /* .handler = */ (PacketHandler) PacketHandler_##name, \
+ /* .name = */ #name, \
+ }
+PacketTypeData packetTypeData[PACKET_NUM] = {
+ DEFINE_PACKETDATA(Init),
+ DEFINE_PACKETDATA(Ping),
+ DEFINE_PACKETDATA(Ack),
+ DEFINE_PACKETDATA(Ready),
+ DEFINE_PACKETDATA(Fleet),
+ DEFINE_PACKETDATA(TeamName),
+ DEFINE_PACKETDATA(Handshake0),
+ DEFINE_PACKETDATA(Handshake1),
+ DEFINE_PACKETDATA(HandshakeCancel),
+ DEFINE_PACKETDATA(HandshakeCancelAck),
+ DEFINE_PACKETDATA(SeedRandom),
+ DEFINE_PACKETDATA(InputDelay),
+ DEFINE_PACKETDATA(SelectShip),
+ DEFINE_PACKETDATA(BattleInput),
+ DEFINE_PACKETDATA(FrameCount),
+ DEFINE_PACKETDATA(Checksum),
+ DEFINE_PACKETDATA(Abort),
+ DEFINE_PACKETDATA(Reset),
+};
+
+static inline void *
+Packet_alloc(size_t size) {
+ return malloc(size);
+}
+
+static Packet *
+Packet_create(PacketType type, size_t extraSize) {
+ Packet *result;
+ size_t len;
+
+ // Alignment requirement.
+ assert(extraSize % 4 == 0);
+
+ len = packetTypeData[type].len + extraSize;
+ result = Packet_alloc(len);
+ result->header.len = hton16((uint16) len);
+ result->header.type = hton16((uint16) type);
+ return result;
+}
+
+void
+Packet_delete(Packet *packet) {
+ free(packet);
+}
+
+Packet_Init *
+Packet_Init_create(void) {
+ Packet_Init *packet = (Packet_Init *) Packet_create(PACKET_INIT, 0);
+
+ packet->protoVersion.major = NETPLAY_PROTOCOL_VERSION_MAJOR;
+ packet->protoVersion.minor = NETPLAY_PROTOCOL_VERSION_MINOR;
+ packet->padding0 = 0;
+ packet->uqmVersion.major = UQM_MAJOR_VERSION;
+ packet->uqmVersion.minor = UQM_MINOR_VERSION;
+ packet->uqmVersion.patch = UQM_PATCH_VERSION;
+ packet->padding1 = 0;
+ return packet;
+}
+
+Packet_Ping *
+Packet_Ping_create(uint32 id) {
+ Packet_Ping *packet = (Packet_Ping *) Packet_create(PACKET_PING, 0);
+
+ packet->id = hton32(id);
+ return packet;
+}
+
+Packet_Ack *
+Packet_Ack_create(uint32 id) {
+ Packet_Ack *packet = (Packet_Ack *) Packet_create(PACKET_ACK, 0);
+
+ packet->id = hton32(id);
+ return packet;
+}
+
+Packet_Ready *
+Packet_Ready_create(void) {
+ Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
+
+ return packet;
+}
+
+// The fleet itself still needs to be filled by the caller.
+// This function takes care of the necessary padding; it is allocated,
+// and filled with zero chars.
+Packet_Fleet *
+Packet_Fleet_create(NetplaySide side, size_t numShips) {
+ Packet_Fleet *packet;
+ size_t fleetSize;
+ size_t extraSize;
+
+ fleetSize = numShips * sizeof (FleetEntry);
+ extraSize = (fleetSize + 3) & ~0x03;
+ packet = (Packet_Fleet *) Packet_create(PACKET_FLEET, extraSize);
+ packet->side = (uint8) side;
+ packet->padding = 0;
+ packet->numShips = hton16((uint16) numShips);
+ memset((char *) packet + sizeof (Packet_Fleet) + fleetSize,
+ '\0', extraSize - fleetSize);
+
+ return packet;
+}
+
+// 'size' is the number of bytes (not characters) in 'name', excluding
+// a possible terminating '\0'. A '\0' will be included in the packet though.
+// This function takes care of the required padding.
+Packet_TeamName *
+Packet_TeamName_create(NetplaySide side, const char *name, size_t size) {
+ Packet_TeamName *packet;
+ size_t extraSize;
+
+ extraSize = ((size + 1) + 3) & ~0x03;
+ // The +1 is for the '\0'.
+ packet = (Packet_TeamName *) Packet_create(PACKET_TEAMNAME, extraSize);
+ packet->side = (uint8) side;
+ packet->padding = 0;
+ memcpy(packet->name, name, size);
+ memset((char *) packet + sizeof (Packet_TeamName) + size, '\0',
+ extraSize - size);
+ // This takes care of the terminating '\0', as well as the
+ // padding.
+
+ return packet;
+}
+
+Packet_Handshake0 *
+Packet_Handshake0_create(void) {
+ Packet_Handshake0 *packet =
+ (Packet_Handshake0 *) Packet_create(PACKET_HANDSHAKE0, 0);
+ return packet;
+}
+
+Packet_Handshake1 *
+Packet_Handshake1_create(void) {
+ Packet_Handshake1 *packet =
+ (Packet_Handshake1 *) Packet_create(PACKET_HANDSHAKE1, 0);
+ return packet;
+}
+
+Packet_HandshakeCancel *
+Packet_HandshakeCancel_create(void) {
+ Packet_HandshakeCancel *packet =
+ (Packet_HandshakeCancel *) Packet_create(
+ PACKET_HANDSHAKECANCEL, 0);
+ return packet;
+}
+
+Packet_HandshakeCancelAck *
+Packet_HandshakeCancelAck_create(void) {
+ Packet_HandshakeCancelAck *packet =
+ (Packet_HandshakeCancelAck *) Packet_create(
+ PACKET_HANDSHAKECANCELACK, 0);
+ return packet;
+}
+
+Packet_SeedRandom *
+Packet_SeedRandom_create(uint32 seed) {
+ Packet_SeedRandom *packet =
+ (Packet_SeedRandom *) Packet_create(PACKET_SEEDRANDOM, 0);
+
+ packet->seed = hton32(seed);
+ return packet;
+}
+
+Packet_InputDelay *
+Packet_InputDelay_create(uint32 delay) {
+ Packet_InputDelay *packet =
+ (Packet_InputDelay *) Packet_create(PACKET_INPUTDELAY, 0);
+
+ packet->delay = hton32(delay);
+ return packet;
+}
+
+Packet_SelectShip *
+Packet_SelectShip_create(uint16 ship) {
+ Packet_SelectShip *packet =
+ (Packet_SelectShip *) Packet_create(PACKET_SELECTSHIP, 0);
+ packet->ship = hton16(ship);
+ packet->padding = 0;
+ return packet;
+}
+
+Packet_BattleInput *
+Packet_BattleInput_create(uint8 state) {
+ Packet_BattleInput *packet =
+ (Packet_BattleInput *) Packet_create(PACKET_BATTLEINPUT, 0);
+ packet->state = (uint8) state;
+ packet->padding0 = 0;
+ packet->padding1 = 0;
+ return packet;
+}
+
+Packet_FrameCount *
+Packet_FrameCount_create(uint32 frameCount) {
+ Packet_FrameCount *packet =
+ (Packet_FrameCount *) Packet_create(PACKET_FRAMECOUNT, 0);
+ packet->frameCount = hton32(frameCount);
+ return packet;
+}
+
+Packet_Checksum *
+Packet_Checksum_create(uint32 frameNr, uint32 checksum) {
+ Packet_Checksum *packet =
+ (Packet_Checksum *) Packet_create(PACKET_CHECKSUM, 0);
+ packet->frameNr = hton32(frameNr);
+ packet->checksum = hton32(checksum);
+ return packet;
+}
+
+Packet_Abort *
+Packet_Abort_create(uint16 reason) {
+ Packet_Abort *packet = (Packet_Abort *) Packet_create(PACKET_ABORT, 0);
+ packet->reason = hton16(reason);
+ return packet;
+}
+
+Packet_Reset *
+Packet_Reset_create(uint16 reason) {
+ Packet_Reset *packet = (Packet_Reset *) Packet_create(PACKET_RESET, 0);
+ packet->reason = hton16(reason);
+ return packet;
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/packet.h b/src/uqm/supermelee/netplay/packet.h
new file mode 100644
index 0000000..f8c9682
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packet.h
@@ -0,0 +1,299 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKET_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKET_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct Packet Packet;
+
+typedef enum PacketType {
+ PACKET_INIT,
+ PACKET_PING,
+ PACKET_ACK,
+ PACKET_READY,
+ PACKET_FLEET,
+ PACKET_TEAMNAME,
+ PACKET_HANDSHAKE0,
+ PACKET_HANDSHAKE1,
+ PACKET_HANDSHAKECANCEL,
+ PACKET_HANDSHAKECANCELACK,
+ PACKET_SEEDRANDOM,
+ PACKET_INPUTDELAY,
+ PACKET_SELECTSHIP,
+ PACKET_BATTLEINPUT,
+ PACKET_FRAMECOUNT,
+ PACKET_CHECKSUM,
+ PACKET_ABORT,
+ PACKET_RESET,
+
+ PACKET_NUM, /* Number of packet types */
+} PacketType;
+
+// Sent before aborting the connection.
+typedef enum NetplayAbortReason {
+ AbortReason_unspecified,
+ AbortReason_versionMismatch,
+ AbortReason_invalidHash,
+ AbortReason_protocolError,
+ // Network is in an inconsistent state.
+} NetplayAbortReason;
+
+// Sent before resetting the connection. A game in progress is terminated.
+typedef enum NetplayResetReason {
+ ResetReason_unspecified,
+ ResetReason_syncLoss,
+ ResetReason_manualReset,
+} NetplayResetReason;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifndef PACKET_H_STANDALONE
+#include "netconnection.h"
+
+#include "types.h"
+#include "libs/network/bytesex.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* NB: These handlers are expected not to modify the state if an
+ * error occurs.
+ * When a handler is called, it has already been validated that the
+ * a complete packet has arrived.
+ */
+typedef int (*PacketHandler)(NetConnection *conn, const void *packet);
+
+typedef struct {
+ size_t len; /* Minimal length of a packet of this type */
+ PacketHandler handler;
+ const char *name;
+} PacketTypeData;
+
+extern PacketTypeData packetTypeData[];
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// When adding new packets, be sure to have all the fields properly aligned,
+// and that the size of a packet is a multiple of 4 bytes in length.
+// Fields should be no longer than 4 bytes in themselves, as larger
+// fields may require a larger alignment.
+
+typedef struct {
+ uint16 len;
+ uint16 type; /* Actually of type PacketType */
+} PacketHeader;
+
+// "Base class" for all packets.
+struct Packet {
+ PacketHeader header;
+};
+
+static inline size_t
+packetLength(const Packet *packet) {
+ return (size_t) ntoh16(packet->header.len);
+}
+
+static inline PacketType
+packetType(const Packet *packet) {
+ return (PacketType) (int) ntoh16(packet->header.type);
+}
+
+static inline bool
+validPacketType(PacketType type) {
+ return type < PACKET_NUM;
+}
+
+typedef struct {
+ PacketHeader header;
+ struct {
+ uint8 major;
+ uint8 minor;
+ } protoVersion; /* Protocol version */
+ uint16 padding0; /* Set to 0 */
+ struct {
+ uint8 major;
+ uint8 minor;
+ uint8 patch;
+ } uqmVersion; /* Protocol version */
+ uint8 padding1; /* Set to 0 */
+} Packet_Init;
+
+typedef struct {
+ PacketHeader header;
+ uint32 id;
+} Packet_Ping;
+
+// Acknowledgement of a Ping.
+typedef struct {
+ PacketHeader header;
+ uint32 id;
+} Packet_Ack;
+
+// Used to signal that a party is ready to continue.
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Ready;
+
+typedef struct {
+ PacketHeader header;
+ uint32 seed;
+} Packet_SeedRandom;
+
+typedef struct {
+ PacketHeader header;
+ uint32 delay;
+} Packet_InputDelay;
+
+// This enum is used to indicate that a packet containing it relates to
+// either the local or the remote player, from the perspective of the
+// sender of the message;
+typedef enum {
+ NetplaySide_local,
+ NetplaySide_remote
+} NetplaySide;
+
+typedef struct {
+ uint8 index; /* Position in the fleet */
+ uint8 ship; /* Ship type index; actually MeleeShip */
+} FleetEntry;
+// Structure describing an update to a player's fleet.
+// TODO: use strings as ship identifiers, instead of numbers,
+// so that adding of new ships doesn't break this.
+typedef struct {
+ PacketHeader header;
+ uint8 side;
+ uint8 padding;
+ uint16 numShips;
+ FleetEntry ships[];
+ // Be sure to add padding to this structure to make it a multiple of
+ // 4 bytes in length.
+} Packet_Fleet;
+
+typedef struct {
+ PacketHeader header;
+ uint8 side;
+ uint8 padding;
+ uint8 name[];
+ // '\0' terminated.
+ // Be sure to add padding to this structure to make it a multiple of
+ // 4 bytes in length.
+} Packet_TeamName;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Handshake0;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Handshake1;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_HandshakeCancel;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_HandshakeCancelAck;
+
+typedef struct {
+ PacketHeader header;
+ uint16 ship;
+ // The value '(uint16) ~0' indicates random selection.
+ uint16 padding;
+} Packet_SelectShip;
+
+typedef struct {
+ PacketHeader header;
+ uint8 state; /* Actually BATTLE_INPUT_STATE */
+ uint8 padding0;
+ uint16 padding1;
+} Packet_BattleInput;
+
+typedef struct {
+ PacketHeader header;
+ uint32 frameCount; /* Actually BattleFrameCounter */
+} Packet_FrameCount;
+
+typedef struct {
+ PacketHeader header;
+ uint32 frameNr; /* Actually BattleFrameCounter */
+ uint32 checksum; /* Actually Checksum */
+} Packet_Checksum;
+
+typedef struct {
+ PacketHeader header;
+ uint16 reason; /* Actually NetplayAbortReason */
+ uint16 padding0;
+} Packet_Abort;
+
+typedef struct {
+ PacketHeader header;
+ uint16 reason; /* Actually NetplayResetReason */
+ uint16 padding0;
+} Packet_Reset;
+
+
+#ifndef PACKET_H_STANDALONE
+void Packet_delete(Packet *packet);
+Packet_Init *Packet_Init_create(void);
+Packet_Ping *Packet_Ping_create(uint32 id);
+Packet_Ack *Packet_Ack_create(uint32 id);
+Packet_Ready *Packet_Ready_create(void);
+Packet_Handshake0 *Packet_Handshake0_create(void);
+Packet_Handshake1 *Packet_Handshake1_create(void);
+Packet_HandshakeCancel *Packet_HandshakeCancel_create(void);
+Packet_HandshakeCancelAck *Packet_HandshakeCancelAck_create(void);
+Packet_SeedRandom *Packet_SeedRandom_create(uint32 seed);
+Packet_InputDelay *Packet_InputDelay_create(uint32 delay);
+Packet_Fleet *Packet_Fleet_create(NetplaySide side, size_t numShips);
+Packet_TeamName *Packet_TeamName_create(NetplaySide side, const char *name,
+ size_t size);
+Packet_SelectShip *Packet_SelectShip_create(uint16 ship);
+Packet_BattleInput *Packet_BattleInput_create(uint8 state);
+Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount);
+Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum);
+Packet_Abort *Packet_Abort_create(uint16 reason);
+Packet_Reset *Packet_Reset_create(uint16 reason);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKET_H_ */
+
diff --git a/src/uqm/supermelee/netplay/packethandlers.c b/src/uqm/supermelee/netplay/packethandlers.c
new file mode 100644
index 0000000..5d2d8f4
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packethandlers.c
@@ -0,0 +1,649 @@
+/*
+ * 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"
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "packethandlers.h"
+
+#include "netinput.h"
+#include "netmisc.h"
+#include "packetsenders.h"
+#include "proto/npconfirm.h"
+#include "proto/ready.h"
+#include "proto/reset.h"
+#include "libs/log.h"
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+ // for NUM_PLAYERS
+#include "../../globdata.h"
+ // for GLOBAL
+#include "../melee.h"
+ // for various update functions.
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../pickmele.h"
+ // for various update functions.
+#include "libs/mathlib.h"
+ // for TFB_SeedRandom
+
+#include <errno.h>
+
+
+static bool
+testNetState(bool condition, PacketType type) {
+ if (!condition) {
+ log_add(log_Error, "Packet of type '%s' received from wrong "
+ "state.", packetTypeData[type].name);
+ errno = EBADMSG;
+ }
+ return condition;
+}
+
+static int
+versionCompare(int major1, int minor1, int patch1,
+ int major2, int minor2, int patch2) {
+ if (major1 < major2)
+ return -1;
+ if (major1 > major2)
+ return 1;
+
+ if (minor1 < minor2)
+ return -1;
+ if (minor1 > minor2)
+ return 1;
+
+ if (patch1 < patch2)
+ return -1;
+ if (patch1 > patch2)
+ return 1;
+
+ return 0;
+}
+
+int
+PacketHandler_Init(NetConnection *conn, const Packet_Init *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_init &&
+ !conn->stateFlags.ready.remoteReady, PACKET_INIT))
+ return -1; // errno is set
+
+ if (packet->protoVersion.major != NETPLAY_PROTOCOL_VERSION_MAJOR ||
+ packet->protoVersion.minor != NETPLAY_PROTOCOL_VERSION_MINOR) {
+ sendAbort (conn, AbortReason_versionMismatch);
+ abortFeedback(conn, AbortReason_versionMismatch);
+ log_add(log_Error, "Protocol version %d.%d not supported.",
+ packet->protoVersion.major, packet->protoVersion.minor);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (versionCompare(packet->uqmVersion.major, packet->uqmVersion.minor,
+ packet->uqmVersion.patch, NETPLAY_MIN_UQM_VERSION_MAJOR,
+ NETPLAY_MIN_UQM_VERSION_MINOR, NETPLAY_MIN_UQM_VERSION_PATCH)
+ < 0) {
+ sendAbort (conn, AbortReason_versionMismatch);
+ abortFeedback(conn, AbortReason_versionMismatch);
+ log_add(log_Error, "Remote side is running a version of UQM that "
+ "is too old (%d.%d.%d; %d.%d.%d is required).",
+ packet->uqmVersion.major, packet->uqmVersion.minor,
+ packet->uqmVersion.patch, NETPLAY_MIN_UQM_VERSION_MAJOR,
+ NETPLAY_MIN_UQM_VERSION_MINOR, NETPLAY_MIN_UQM_VERSION_PATCH);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ Netplay_remoteReady(conn);
+
+ return 0;
+}
+
+int
+PacketHandler_Ping(NetConnection *conn, const Packet_Ping *packet) {
+ if (!testNetState(conn->state > NetState_init, PACKET_PING))
+ return -1; // errno is set
+
+ sendAck(conn, packet->id);
+ return 0;
+}
+
+int
+PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
+ if (!testNetState(conn->state > NetState_init, PACKET_ACK))
+ return -1; // errno is set
+
+ (void) conn;
+ (void) packet;
+ return 0;
+}
+
+// Convert the side indication relative to a remote party to
+// a local player number.
+static inline int
+localSide(NetConnection *conn, NetplaySide side) {
+ if (side == NetplaySide_local) {
+ // "local" relative to the remote party.
+ return conn->player;
+ }
+
+ return 1 - conn->player;
+}
+
+int
+PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(readyFlagsMeaningful(conn->state) &&
+ !conn->stateFlags.ready.remoteReady, PACKET_READY))
+ return -1; // errno is set
+
+ Netplay_remoteReady(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
+ uint16 numShips = ntoh16(packet->numShips);
+ size_t i;
+ size_t len;
+ int player;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
+ return -1; // errno is set
+
+ player = localSide(conn, (NetplaySide) packet->side);
+
+ len = packetLength((const Packet *) packet);
+ if (sizeof packet + numShips * sizeof(packet->ships[0]) > len) {
+ // There is not enough room in the packet to contain all
+ // the ships it says it contains.
+ log_add(log_Warning, "Invalid fleet size. Specified size is %d, "
+ "actual size = %d",
+ numShips, (int) ((len - sizeof packet) / sizeof(packet->ships[0])));
+ errno = EBADMSG;
+ return -1;
+ }
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+ if (conn->stateFlags.handshake.localOk) {
+ Netplay_cancelConfirmation(conn);
+ confirmationCancelled(battleStateData->meleeState, conn->player);
+ }
+
+ for (i = 0; i < numShips; i++) {
+ MeleeShip ship = (MeleeShip) packet->ships[i].ship;
+ FleetShipIndex index = (FleetShipIndex) packet->ships[i].index;
+
+ if (!MeleeShip_valid(ship)) {
+ log_add (log_Warning, "Invalid ship type number %d (max = %d).\n",
+ ship, NUM_MELEE_SHIPS - 1);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (index >= MELEE_FLEET_SIZE)
+ {
+ log_add (log_Warning, "Invalid ship position number %d "
+ "(max = %d).\n", index, MELEE_FLEET_SIZE - 1);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ Melee_RemoteChange_ship (battleStateData->meleeState, conn,
+ player, index, ship);
+ }
+
+ // Padding data may follow; it is ignored.
+ return 0;
+}
+
+int
+PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
+ size_t nameLen;
+ int side;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+ if (conn->stateFlags.handshake.localOk) {
+ Netplay_cancelConfirmation(conn);
+ confirmationCancelled(battleStateData->meleeState, conn->player);
+ }
+
+ side = localSide(conn, (NetplaySide) packet->side);
+ nameLen = packetLength((const Packet *) packet)
+ - sizeof (Packet_TeamName) - 1;
+ // The -1 is for not counting the terminating '\0'.
+
+ {
+ char buf[MAX_TEAM_CHARS + 1];
+
+ if (nameLen > MAX_TEAM_CHARS)
+ nameLen = MAX_TEAM_CHARS;
+ memcpy (buf, (const char *) packet->name, nameLen);
+ buf[nameLen] = '\0';
+
+ Melee_RemoteChange_teamName(battleStateData->meleeState, conn,
+ side, buf);
+ }
+
+ // Padding data may follow; it is ignored.
+ return 0;
+}
+
+static void
+handshakeComplete(NetConnection *conn) {
+ assert(!conn->stateFlags.handshake.localOk);
+ assert(!conn->stateFlags.handshake.remoteOk);
+ assert(!conn->stateFlags.handshake.canceling);
+
+ assert(conn->state == NetState_inSetup);
+ NetConnection_setState(conn, NetState_preBattle);
+}
+
+int
+PacketHandler_Handshake0(NetConnection *conn,
+ const Packet_Handshake0 *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.remoteOk = true;
+ if (conn->stateFlags.handshake.localOk &&
+ !conn->stateFlags.handshake.canceling)
+ sendHandshake1(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_Handshake1(NetConnection *conn,
+ const Packet_Handshake1 *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state) &&
+ (conn->stateFlags.handshake.localOk ||
+ conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1))
+ return -1; // errno is set
+
+ if (conn->stateFlags.handshake.canceling) {
+ conn->stateFlags.handshake.remoteOk = true;
+ } else {
+ bool remoteWasOk = conn->stateFlags.handshake.remoteOk;
+
+ conn->stateFlags.handshake.localOk = false;
+ conn->stateFlags.handshake.remoteOk = false;
+
+ if (!remoteWasOk) {
+ // Received Handshake1 without prior Handshake0.
+ // A Handshake0 is implied, but we still need to confirm
+ // it with a Handshake1.
+ sendHandshake1(conn);
+ }
+
+ handshakeComplete(conn);
+ }
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_HandshakeCancel(NetConnection *conn,
+ const Packet_HandshakeCancel *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.remoteOk = false;
+ sendHandshakeCancelAck(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_HandshakeCancelAck(NetConnection *conn,
+ const Packet_HandshakeCancelAck *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && conn->stateFlags.handshake.canceling,
+ PACKET_HANDSHAKECANCELACK))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.canceling = false;
+ if (conn->stateFlags.handshake.localOk) {
+ if (conn->stateFlags.handshake.remoteOk) {
+ sendHandshake1(conn);
+ } else
+ sendHandshake0(conn);
+ }
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_SeedRandom(NetConnection *conn,
+ const Packet_SeedRandom *packet) {
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_preBattle &&
+ !conn->stateFlags.discriminant, PACKET_SEEDRANDOM))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ updateRandomSeed (battleStateData->meleeState, conn->player,
+ ntoh32(packet->seed));
+
+ conn->stateFlags.agreement.randomSeed = true;
+ return 0;
+}
+
+int
+PacketHandler_InputDelay(NetConnection *conn,
+ const Packet_InputDelay *packet) {
+ BattleStateData *battleStateData;
+ uint32 delay;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_preBattle, PACKET_INPUTDELAY))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ delay = ntoh32(packet->delay);
+ if (delay > BATTLE_FRAME_RATE) {
+ log_add(log_Error, "NETPLAY: [%d] Received absurdly large "
+ "input delay value (%d).", conn->player, delay);
+ return -1;
+ }
+ conn->stateFlags.inputDelay = delay;
+
+ return 0;
+}
+
+int
+PacketHandler_SelectShip(NetConnection *conn,
+ const Packet_SelectShip *packet) {
+ bool updateResult;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_selectShip, PACKET_SELECTSHIP))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ updateResult = updateMeleeSelection(battleStateData->getMeleeState,
+ conn->player, ntoh16(packet->ship));
+ if (!updateResult)
+ {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PacketHandler_BattleInput(NetConnection *conn,
+ const Packet_BattleInput *packet) {
+ BATTLE_INPUT_STATE input;
+ BattleInputBuffer *bib;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inBattle ||
+ conn->state == NetState_endingBattle ||
+ conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT))
+ return -1; // errno is set
+
+ input = (BATTLE_INPUT_STATE) packet->state;
+ bib = getBattleInputBuffer(conn->player);
+ if (!BattleInputBuffer_push(bib, input)) {
+ // errno is set
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PacketHandler_FrameCount(NetConnection *conn,
+ const Packet_FrameCount *packet) {
+ BattleStateData *battleStateData;
+ BattleFrameCounter frameCount;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_endingBattle,
+ PACKET_FRAMECOUNT))
+ return -1; // errno is set
+
+ frameCount = (BattleFrameCounter) ntoh32(packet->frameCount);
+#ifdef NETPLAY_DEBUG
+ log_add(log_Debug, "NETPLAY: [%d] <== Received battleFrameCount %u.",
+ conn->player, (unsigned int) frameCount);
+#endif
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ if (frameCount > battleStateData->endFrameCount)
+ battleStateData->endFrameCount = frameCount;
+ Netplay_remoteReady(conn);
+
+ return 0;
+}
+
+int
+PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
+#ifdef NETPLAY_CHECKSUM
+ uint32 frameNr;
+ uint32 checksum;
+ size_t delay;
+ size_t interval;
+#endif
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(NetState_battleActive(conn->state), PACKET_CHECKSUM))
+ return -1; // errno is set
+
+#ifdef NETPLAY_CHECKSUM
+ frameNr = ntoh32(packet->frameNr);
+ checksum = ntoh32(packet->checksum);
+ interval = NetConnection_getChecksumInterval(conn);
+ delay = getBattleInputDelay();
+
+ if (frameNr % interval != 0) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for frame %u, while we only expect checksums on frames "
+ "divisable by %u -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) interval);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ // The checksum is sent at the beginning of a frame.
+ // If we're in frame n and have sent our input already,
+ // the remote side has got enough input to progress delay + 1 frames from
+ // frame n. The next frame is then n + delay + 1, for which we can
+ // receive a checksum.
+ if (frameNr > battleFrameCount + delay + 1) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for a frame too far in the future (frame %u, current "
+ "is %u, input delay is %u) -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) battleFrameCount, (unsigned int) delay);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ // We can progress delay more frames after the last frame for which we
+ // received input. If we call that frame n, we can complete frames
+ // n through n + delay - 1. While we are waiting for the next input,
+ // in frame n + delay, we will first receive the checksum that the
+ // remote side sent at the start of frame n + 1.
+ // In this situation frameNr is n + 1, and battleFrameCount is
+ // n + delay.
+ if (frameNr + delay < battleFrameCount) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for a frame too far in the past (frame %u, current "
+ "is %u, input delay is %u) -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) battleFrameCount, (unsigned int) delay);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ addRemoteChecksum(conn, frameNr, checksum);
+#endif
+
+#ifndef NETPLAY_CHECKSUM
+ (void) packet;
+#endif
+ return 0;
+}
+
+int
+PacketHandler_Abort(NetConnection *conn, const Packet_Abort *packet) {
+ abortFeedback(conn, packet->reason);
+
+ return -1;
+ // Close connection.
+}
+
+int
+PacketHandler_Reset(NetConnection *conn, const Packet_Reset *packet) {
+ NetplayResetReason reason;
+
+ if (!testNetState(!conn->stateFlags.reset.remoteReset, PACKET_RESET))
+ return -1; // errno is set
+
+ reason = ntoh16(packet->reason);
+
+ Netplay_remoteReset(conn, reason);
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/packethandlers.h b/src/uqm/supermelee/netplay/packethandlers.h
new file mode 100644
index 0000000..7bd686e
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packethandlers.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_
+
+#include "packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DECLARE_PACKETHANDLER(type) \
+ int PacketHandler_##type(NetConnection *conn, \
+ const Packet_##type *packet)
+
+DECLARE_PACKETHANDLER(Init);
+DECLARE_PACKETHANDLER(Ping);
+DECLARE_PACKETHANDLER(Ack);
+DECLARE_PACKETHANDLER(Ready);
+DECLARE_PACKETHANDLER(Fleet);
+DECLARE_PACKETHANDLER(TeamName);
+DECLARE_PACKETHANDLER(Handshake0);
+DECLARE_PACKETHANDLER(Handshake1);
+DECLARE_PACKETHANDLER(HandshakeCancel);
+DECLARE_PACKETHANDLER(HandshakeCancelAck);
+DECLARE_PACKETHANDLER(SeedRandom);
+DECLARE_PACKETHANDLER(InputDelay);
+DECLARE_PACKETHANDLER(SelectShip);
+DECLARE_PACKETHANDLER(BattleInput);
+DECLARE_PACKETHANDLER(FrameCount);
+DECLARE_PACKETHANDLER(Checksum);
+DECLARE_PACKETHANDLER(Abort);
+DECLARE_PACKETHANDLER(Reset);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_ */
diff --git a/src/uqm/supermelee/netplay/packetq.c b/src/uqm/supermelee/netplay/packetq.c
new file mode 100644
index 0000000..ee8ec01
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetq.c
@@ -0,0 +1,149 @@
+/*
+ * 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 NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "netconnection.h"
+#include "packetq.h"
+#include "netsend.h"
+#include "packetsenders.h"
+#ifdef NETPLAY_DEBUG
+# include "libs/log.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline PacketQueueLink *
+PacketQueueLink_alloc(void) {
+ // XXX: perhaps keep a pool of links?
+ return malloc(sizeof (PacketQueueLink));
+}
+
+static inline void
+PacketQueueLink_delete(PacketQueueLink *link) {
+ free(link);
+}
+
+// 'maxSize' should at least be 1
+void
+PacketQueue_init(PacketQueue *queue) {
+ queue->size = 0;
+ queue->first = NULL;
+ queue->end = &queue->first;
+}
+
+static void
+PacketQueue_deleteLinks(PacketQueueLink *link) {
+ while (link != NULL) {
+ PacketQueueLink *next = link->next;
+ Packet_delete(link->packet);
+ PacketQueueLink_delete(link);
+ link = next;
+ }
+}
+
+void
+PacketQueue_uninit(PacketQueue *queue) {
+ PacketQueue_deleteLinks(queue->first);
+}
+
+void
+queuePacket(NetConnection *conn, Packet *packet) {
+ PacketQueue *queue;
+ PacketQueueLink *link;
+
+ assert(NetConnection_isConnected(conn));
+
+ queue = &conn->queue;
+
+ link = PacketQueueLink_alloc();
+ link->packet = packet;
+ link->next = NULL;
+ *queue->end = link;
+ queue->end = &link->next;
+
+ queue->size++;
+ // XXX: perhaps check that this queue isn't getting too large?
+
+#ifdef NETPLAY_DEBUG
+ if (packetType(packet) != PACKET_BATTLEINPUT &&
+ packetType(packet) != PACKET_CHECKSUM) {
+ // Reporting BattleInput or Checksum would get so spammy that it
+ // would slow down the battle.
+ log_add(log_Debug, "NETPLAY: [%d] ==> Queueing packet of type %s.\n",
+ NetConnection_getPlayerNr(conn),
+ packetTypeData[packetType(packet)].name);
+ }
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] ==> Queueing packet of type %s.\n",
+ NetConnection_getPlayerNr(conn),
+ packetTypeData[packetType(packet)].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+}
+
+// If an error occurs during sending, we leave the unsent packets in
+// the queue, and let the caller decide what to do with them.
+// This function may return -1 with errno EAGAIN or EWOULDBLOCK
+// if we're waiting for the other party to act first.
+static int
+flushPacketQueueLinks(NetConnection *conn, PacketQueueLink **first) {
+ PacketQueueLink *link;
+ PacketQueueLink *next;
+ PacketQueue *queue = &conn->queue;
+
+ for (link = *first; link != NULL; link = next) {
+ if (sendPacket(conn, link->packet) == -1) {
+ // Errno is set.
+ *first = link;
+ return -1;
+ }
+
+ next = link->next;
+ Packet_delete(link->packet);
+ PacketQueueLink_delete(link);
+ queue->size--;
+ }
+
+ *first = link;
+ return 0;
+}
+
+int
+flushPacketQueue(NetConnection *conn) {
+ int flushResult;
+ PacketQueue *queue = &conn->queue;
+
+ assert(NetConnection_isConnected(conn));
+
+ flushResult = flushPacketQueueLinks(conn, &queue->first);
+ if (queue->first == NULL)
+ queue->end = &queue->first;
+ if (flushResult == -1) {
+ // errno is set
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/supermelee/netplay/packetq.h b/src/uqm/supermelee/netplay/packetq.h
new file mode 100644
index 0000000..71f2347
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetq.h
@@ -0,0 +1,59 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETQ_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETQ_H_
+
+typedef struct PacketQueue PacketQueue;
+
+#include "packet.h"
+#include "types.h"
+
+#include <sys/types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct PacketQueueLink PacketQueueLink;
+struct PacketQueueLink {
+ PacketQueueLink *next;
+ Packet *packet;
+};
+
+struct PacketQueue {
+ size_t size;
+ PacketQueueLink *first;
+ PacketQueueLink **end;
+
+ // first points to the first entry in the queue
+ // end points to the location where the next message should be inserted.
+};
+
+void PacketQueue_init(PacketQueue *queue);
+void PacketQueue_uninit(PacketQueue *queue);
+void queuePacket(NetConnection *conn, Packet *packet);
+int flushPacketQueue(NetConnection *conn);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/src/uqm/supermelee/netplay/packetsenders.c b/src/uqm/supermelee/netplay/packetsenders.c
new file mode 100644
index 0000000..fb9f232
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetsenders.c
@@ -0,0 +1,197 @@
+/*
+ * 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
+ */
+
+#include "netplay.h"
+#include "packetsenders.h"
+
+#include "packet.h"
+#include "packetq.h"
+#include "netsend.h"
+
+
+void
+sendInit(NetConnection *conn) {
+ Packet_Init *packet;
+
+ packet = Packet_Init_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendPing(NetConnection *conn, uint32 id) {
+ Packet_Ping *packet;
+
+ packet = Packet_Ping_create(id);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendAck(NetConnection *conn, uint32 id) {
+ Packet_Ack *packet;
+
+ packet = Packet_Ack_create(id);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendReady(NetConnection *conn) {
+ Packet_Ready *packet;
+
+ packet = Packet_Ready_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshake0(NetConnection *conn) {
+ Packet_Handshake0 *packet;
+
+ packet = Packet_Handshake0_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshake1(NetConnection *conn) {
+ Packet_Handshake1 *packet;
+
+ packet = Packet_Handshake1_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshakeCancel(NetConnection *conn) {
+ Packet_HandshakeCancel *packet;
+
+ packet = Packet_HandshakeCancel_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshakeCancelAck(NetConnection *conn) {
+ Packet_HandshakeCancelAck *packet;
+
+ packet = Packet_HandshakeCancelAck_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendTeamName(NetConnection *conn, NetplaySide side, const char *name,
+ size_t len) {
+ Packet_TeamName *packet;
+
+ packet = Packet_TeamName_create(side, name, len);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFleet(NetConnection *conn, NetplaySide side, const MeleeShip *ships,
+ size_t shipCount) {
+ size_t i;
+ Packet_Fleet *packet;
+
+ packet = Packet_Fleet_create(side, shipCount);
+
+ for (i = 0; i < shipCount; i++) {
+ packet->ships[i].index = (uint8) i;
+ packet->ships[i].ship = (uint8) ships[i];
+ }
+
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFleetShip(NetConnection *conn, NetplaySide side,
+ FleetShipIndex shipIndex, MeleeShip ship) {
+ Packet_Fleet *packet;
+
+ packet = Packet_Fleet_create(side, 1);
+
+ packet->ships[0].index = (uint8) shipIndex;
+ packet->ships[0].ship = (uint8) ship;
+
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendSeedRandom(NetConnection *conn, uint32 seed) {
+ Packet_SeedRandom *packet;
+
+ packet = Packet_SeedRandom_create(seed);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendInputDelay(NetConnection *conn, uint32 delay) {
+ Packet_InputDelay *packet;
+
+ packet = Packet_InputDelay_create(delay);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendSelectShip(NetConnection *conn, FleetShipIndex index) {
+ Packet_SelectShip *packet;
+
+ packet = Packet_SelectShip_create((uint16) index);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
+ Packet_BattleInput *packet;
+
+ packet = Packet_BattleInput_create((uint8) input);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
+ Packet_FrameCount *packet;
+
+ packet = Packet_FrameCount_create((uint32) frameCount);
+ queuePacket(conn, (Packet *) packet);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ Packet_Checksum *packet;
+
+ packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
+ queuePacket(conn, (Packet *) packet);
+}
+#endif
+
+void
+sendAbort(NetConnection *conn, NetplayAbortReason reason) {
+ Packet_Abort *packet;
+
+ packet = Packet_Abort_create((uint16) reason);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendReset(NetConnection *conn, NetplayResetReason reason) {
+ Packet_Reset *packet;
+
+ packet = Packet_Reset_create((uint16) reason);
+ queuePacket(conn, (Packet *) packet);
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/packetsenders.h b/src/uqm/supermelee/netplay/packetsenders.h
new file mode 100644
index 0000000..de0bc6d
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetsenders.h
@@ -0,0 +1,67 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_
+
+#include "types.h"
+
+#include "netconnection.h"
+#include "packet.h"
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../meleesetup.h"
+ // for FleetShipIndex
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void sendInit(NetConnection *conn);
+void sendPing(NetConnection *conn, uint32 id);
+void sendAck(NetConnection *conn, uint32 id);
+void sendReady(NetConnection *conn);
+void sendHandshake0(NetConnection *conn);
+void sendHandshake1(NetConnection *conn);
+void sendHandshakeCancel(NetConnection *conn);
+void sendHandshakeCancelAck(NetConnection *conn);
+void sendTeamName(NetConnection *conn, NetplaySide side,
+ const char *name, size_t len);
+void sendFleet(NetConnection *conn, NetplaySide side,
+ const MeleeShip *ships, size_t numShips);
+void sendFleetShip(NetConnection *conn, NetplaySide player,
+ FleetShipIndex shipIndex, MeleeShip ship);
+void sendSeedRandom(NetConnection *conn, uint32 seed);
+void sendInputDelay(NetConnection *conn, uint32 delay);
+void sendSelectShip(NetConnection *conn, FleetShipIndex index);
+void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
+void sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount);
+void sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum);
+void sendAbort(NetConnection *conn, NetplayAbortReason reason);
+void sendReset(NetConnection *conn, NetplayResetReason reason);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/Makeinfo b/src/uqm/supermelee/netplay/proto/Makeinfo
new file mode 100644
index 0000000..1d9739c
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="npconfirm.c ready.c reset.c"
+uqm_HFILES="npconfirm.h ready.h reset.h"
diff --git a/src/uqm/supermelee/netplay/proto/npconfirm.c b/src/uqm/supermelee/netplay/proto/npconfirm.c
new file mode 100644
index 0000000..6929219
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/npconfirm.c
@@ -0,0 +1,81 @@
+/*
+ * 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 NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "npconfirm.h"
+
+#include "types.h"
+#include "../netmisc.h"
+#include "../packetsenders.h"
+
+#include <assert.h>
+#include <errno.h>
+
+int
+Netplay_confirm(NetConnection *conn) {
+ assert(handshakeMeaningful(NetConnection_getState(conn)));
+
+ if (conn->stateFlags.handshake.localOk) {
+ // Already confirmed
+ errno = EINVAL;
+ return -1;
+ }
+
+ conn->stateFlags.handshake.localOk = true;
+
+ if (conn->stateFlags.handshake.canceling) {
+ // If a previous confirmation was cancelled, but the cancel
+ // is not acknowledged yet, we don't have to send anything yet.
+ // The handshake0 packet will be sent when the acknowledgement
+ // arrives.
+ } else if (conn->stateFlags.handshake.remoteOk) {
+ // A Handshake0 is implied by the following Handshake1.
+ sendHandshake1(conn);
+ } else {
+ sendHandshake0(conn);
+ }
+
+ return 0;
+}
+
+int
+Netplay_cancelConfirmation(NetConnection *conn) {
+ assert(handshakeMeaningful(NetConnection_getState(conn)));
+
+ if (!conn->stateFlags.handshake.localOk) {
+ // Not confirmed, or already canceling.
+ errno = EINVAL;
+ return -1;
+ }
+
+ conn->stateFlags.handshake.localOk = false;
+ if (conn->stateFlags.handshake.canceling) {
+ // If previous cancellation is still waiting to be acknowledged,
+ // the confirmation we are cancelling here, has not actually been
+ // sent yet. By setting the localOk flag to false, it is
+ // cancelled, without the need for any packets to be sent.
+ } else {
+ conn->stateFlags.handshake.canceling = true;
+ sendHandshakeCancel(conn);
+ }
+
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/proto/npconfirm.h b/src/uqm/supermelee/netplay/proto/npconfirm.h
new file mode 100644
index 0000000..1ae58f5
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/npconfirm.h
@@ -0,0 +1,36 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_
+
+#include "../netplay.h"
+#include "../netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int Netplay_confirm(NetConnection *conn);
+int Netplay_cancelConfirmation(NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/ready.c b/src/uqm/supermelee/netplay/proto/ready.c
new file mode 100644
index 0000000..e9f8c58
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/ready.c
@@ -0,0 +1,106 @@
+/*
+ * 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 NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "ready.h"
+
+#include "types.h"
+#include "../netmisc.h"
+#include "../packetsenders.h"
+
+#include <assert.h>
+
+static void
+Netplay_bothReady(NetConnection *conn) {
+ NetConnection_ReadyCallback callback;
+ void *readyArg;
+
+ assert(conn->readyCallback != NULL);
+
+ callback = conn->readyCallback;
+ readyArg = conn->readyCallbackArg;
+
+ NetConnection_setReadyCallback(conn, NULL, NULL);
+ // Clear the readyCallback field before performing the callback,
+ // so that it can be set again from inside the callback
+ // function.
+
+ callback(conn, readyArg);
+}
+
+// If notifyRemote is set, a 'Ready' message will be sent to the other side.
+// returns true iff both sides are ready.
+// Inside the callback function, ready flags may be set for a possible
+// next Ready communication.
+bool
+Netplay_localReady(NetConnection *conn, NetConnection_ReadyCallback callback,
+ void *readyArg, bool notifyRemote) {
+ assert(readyFlagsMeaningful(NetConnection_getState(conn)));
+ assert(!conn->stateFlags.ready.localReady);
+ assert(callback != NULL);
+
+ NetConnection_setReadyCallback(conn, callback, readyArg);
+
+ if (notifyRemote)
+ sendReady(conn);
+ if (!conn->stateFlags.ready.remoteReady) {
+ conn->stateFlags.ready.localReady = true;
+ return false;
+ }
+
+ // Reset ready flags:
+ conn->stateFlags.ready.remoteReady = false;
+
+ // Trigger the callback.
+ Netplay_bothReady(conn);
+ return true;
+}
+
+// returns true iff both sides are ready.
+bool
+Netplay_remoteReady(NetConnection *conn) {
+ assert(readyFlagsMeaningful(NetConnection_getState(conn)));
+ // This is supposed to be already verified by the calling
+ // function.
+ assert(!conn->stateFlags.ready.remoteReady);
+
+ if (!conn->stateFlags.ready.localReady) {
+ conn->stateFlags.ready.remoteReady = true;
+ return false;
+ }
+
+ // Reset ready flags:
+ conn->stateFlags.ready.localReady = false;
+
+ // Trigger the callback.
+ Netplay_bothReady(conn);
+ return true;
+}
+
+bool
+Netplay_isLocalReady(const NetConnection *conn) {
+ return conn->stateFlags.ready.localReady;
+}
+
+bool
+Netplay_isRemoteReady(const NetConnection *conn) {
+ return conn->stateFlags.ready.remoteReady;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/proto/ready.h b/src/uqm/supermelee/netplay/proto/ready.h
new file mode 100644
index 0000000..3521557
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/ready.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_
+
+#include "../netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool Netplay_localReady(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg, bool notifyRemote);
+bool Netplay_remoteReady(NetConnection *conn);
+bool Netplay_isLocalReady(const NetConnection *conn);
+bool Netplay_isRemoteReady(const NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/reset.c b/src/uqm/supermelee/netplay/proto/reset.c
new file mode 100644
index 0000000..82483b1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/reset.c
@@ -0,0 +1,166 @@
+/*
+ * 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
+ */
+
+// See doc/devel/netplay/protocol
+
+#define NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "reset.h"
+
+#include "types.h"
+#include "../packetsenders.h"
+#include "../../melee.h"
+ // For resetFeedback.
+
+#include <assert.h>
+
+// Reset packets are sent to indicate that a game is to be reset.
+// i.e. the game is to return to the SuperMelee fleet setup menu.
+// The reset will occur when a reset packet has both been sent and
+// received. When a reset packet is received and the local side had not
+// sent a reset packet itself, the local side will confirm the reset.
+// When both sides initiate a reset simultaneously, the reset packets
+// of each side will act as a confirmation for the other side.
+//
+// When a reset packet has been sent, no further gameplay packets should be
+// sent until the game has been reset. Non-gameplay packets such as 'ping'
+// are allowed.
+// When a reset packet has been received, all further incoming gameplay
+// packets are ignored until the game has been reset.
+//
+// conn->stateFlags.reset.localReset is set when a reset packet is sent.
+// conn->stateFlags.reset.remoteReset is set when a reset packet is
+// received.
+//
+// When either localReset or remoteReset gets set and the other flag isn't
+// set, Netplay_connectionReset() gets called.
+//
+// As soon as the following three conditions are met, the reset callback is
+// called and the localReset and remoteReset flags are cleared.
+// - conn->stateFlags.reset.localReset is set
+// - conn->stateFlags.reset.remoteReset is set
+// - the reset callback is non-NULL.
+//
+// Elsewhere in the UQM source:
+// When the local side causes a reset, it calls Netplay_localReset().
+// When a remote reset packet is received, Netplay_remoteReset() is called
+// (which will sent a reset packet back as confirmation, as required).
+// At the end of melee, the reset callback is set (for each connection),
+// and the game will wait until the reset callback for each connection has
+// been called (when the forementioned conditions have become true)
+// (or until the connection is terminated).
+
+
+// This function is called when one side initiates a reset.
+static void
+Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
+ bool byRemote) {
+ switch (NetConnection_getState(conn)) {
+ case NetState_unconnected:
+ case NetState_connecting:
+ case NetState_init:
+ case NetState_inSetup:
+ break;
+ case NetState_preBattle:
+ case NetState_interBattle:
+ case NetState_selectShip:
+ case NetState_inBattle:
+ case NetState_endingBattle:
+ case NetState_endingBattle2:
+ resetFeedback(conn, reason, byRemote);
+ break;
+ }
+}
+
+static void
+Netplay_doConnectionResetCallback(NetConnection *conn) {
+ NetConnection_ResetCallback callback;
+ void *resetArg;
+
+ callback = conn->resetCallback;
+ resetArg = conn->resetCallbackArg;
+
+ NetConnection_setResetCallback(conn, NULL, NULL);
+ // Clear the resetCallback field before performing the callback,
+ // so that it can be set again from inside the callback
+ // function.
+ callback(conn, resetArg);
+}
+
+static void
+Netplay_resetConditionTriggered(NetConnection *conn) {
+ if (conn->resetCallback == NULL)
+ return;
+
+ if (!conn->stateFlags.reset.localReset ||
+ !conn->stateFlags.reset.remoteReset)
+ return;
+
+ conn->stateFlags.reset.localReset = false;
+ conn->stateFlags.reset.remoteReset = false;
+
+ Netplay_doConnectionResetCallback(conn);
+}
+
+void
+Netplay_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *resetArg) {
+ NetConnection_setResetCallback(conn, callback, resetArg);
+
+ Netplay_resetConditionTriggered(conn);
+}
+
+void
+Netplay_localReset(NetConnection *conn, NetplayResetReason reason) {
+ assert(!conn->stateFlags.reset.localReset);
+
+ conn->stateFlags.reset.localReset = true;
+ if (conn->stateFlags.reset.remoteReset) {
+ // Both sides have initiated/confirmed the reset.
+ Netplay_resetConditionTriggered(conn);
+ } else {
+ sendReset(conn, reason);
+ Netplay_connectionReset(conn, reason, false);
+ }
+}
+
+void
+Netplay_remoteReset(NetConnection *conn, NetplayResetReason reason) {
+ assert(!conn->stateFlags.reset.remoteReset);
+ // Should already be checked when the packet arrives.
+
+ conn->stateFlags.reset.remoteReset = true;
+ if (!conn->stateFlags.reset.localReset) {
+ sendReset(conn, reason);
+ conn->stateFlags.reset.localReset = true;
+ Netplay_connectionReset(conn, reason, true);
+ }
+
+ Netplay_resetConditionTriggered(conn);
+}
+
+bool
+Netplay_isLocalReset(const NetConnection *conn) {
+ return conn->stateFlags.reset.localReset;
+}
+
+bool
+Netplay_isRemoteReset(const NetConnection *conn) {
+ return conn->stateFlags.reset.remoteReset;
+}
+
diff --git a/src/uqm/supermelee/netplay/proto/reset.h b/src/uqm/supermelee/netplay/proto/reset.h
new file mode 100644
index 0000000..e16b1d1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/reset.h
@@ -0,0 +1,41 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_
+
+#include "../netconnection.h"
+#include "../packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *resetArg);
+void Netplay_localReset(NetConnection *conn, NetplayResetReason reason);
+void Netplay_remoteReset(NetConnection *conn, NetplayResetReason reason);
+bool Netplay_isLocalReset(const NetConnection *conn);
+bool Netplay_isRemoteReset(const NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_ */
+