summaryrefslogtreecommitdiff
path: root/src/uqm/supermelee/netplay/netrcv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/supermelee/netplay/netrcv.c')
-rw-r--r--src/uqm/supermelee/netplay/netrcv.c193
1 files changed, 193 insertions, 0 deletions
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;
+ }
+}
+
+
+