855 lines
21 KiB
C++
855 lines
21 KiB
C++
/*
|
|
This file is part of Warzone 2100.
|
|
Copyright (C) 2007-2010 Warzone Resurrection Project
|
|
|
|
Warzone 2100 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.
|
|
|
|
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/**
|
|
* @file nettypes.c
|
|
*
|
|
* Contains the 'new' Network API functions for sending and receiving both
|
|
* low-level primitives and higher-level types.
|
|
*/
|
|
|
|
#include "lib/framework/wzglobal.h"
|
|
#include "lib/framework/string_ext.h"
|
|
#include <string.h>
|
|
|
|
#include <SDL_endian.h>
|
|
|
|
#include "../framework/frame.h"
|
|
#include "netplay.h"
|
|
#include "nettypes.h"
|
|
#include "netqueue.h"
|
|
#include "netlog.h"
|
|
#include "src/order.h"
|
|
#include <cstring>
|
|
|
|
/// There is a game queue representing each player. The game queues are synchronised among all players, so that all players process the same game queue
|
|
/// messages at the same game time. The game queues should be used, even in single-player. Players should write to their own queue, not to other player's
|
|
/// queues, and should read messages from all queues including their own.
|
|
static NetQueue *gameQueues[MAX_PLAYERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
|
|
|
/// There is a bidirectional net queue for communicating with each client or host. Each queue corresponds either to a real socket, or a virtual socket
|
|
/// which routes via the host.
|
|
static NetQueuePair *netQueues[MAX_CONNECTED_PLAYERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
|
|
|
/// These queues are for clients which just connected, but haven't yet been assigned a player number.
|
|
static NetQueuePair *tmpQueues[MAX_TMP_SOCKETS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
|
|
|
/// Sending a message to the broadcast queue is equivalent to sending the message to the net queues of all other players.
|
|
static NetQueue *broadcastQueue = NULL;
|
|
|
|
// Only used between NETbegin{Encode,Decode} and NETend calls.
|
|
static MessageWriter writer; ///< Used when serialising a message.
|
|
static MessageReader reader; ///< Used when deserialising a message.
|
|
static NetMessage message; ///< A message which is being serialised or deserialised.
|
|
static NETQUEUE queueInfo; ///< Indicates which queue is currently being (de)serialised.
|
|
static PACKETDIR NetDir; ///< Indicates whether a message is being serialised (PACKET_ENCODE) or deserialised (PACKET_DECODE), or not doing anything (PACKET_INVALID).
|
|
|
|
static void NETsetPacketDir(PACKETDIR dir)
|
|
{
|
|
NetDir = dir;
|
|
|
|
// Can't put STATIC_ASSERT in global scope, arbitrarily putting it here.
|
|
STATIC_ASSERT(MAX_PLAYERS == MAX_CONNECTED_PLAYERS); // Things might break if each connected player doesn't correspond to a player of the same index.
|
|
}
|
|
|
|
PACKETDIR NETgetPacketDir()
|
|
{
|
|
return NetDir;
|
|
}
|
|
|
|
// The queue(q, v) functions (de)serialise the object v to/from q, depending on whether q is a MessageWriter or MessageReader.
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, uint8_t &v)
|
|
{
|
|
q.byte(v);
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, uint16_t &v)
|
|
{
|
|
uint8_t b[2] = {v>>8, v};
|
|
queue(q, b[0]);
|
|
queue(q, b[1]);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b[0]<<8 | b[1];
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, uint32_t &v)
|
|
{
|
|
uint16_t b[2] = {v>>16, v};
|
|
queue(q, b[0]);
|
|
queue(q, b[1]);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b[0]<<16 | b[1];
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, uint64_t &v)
|
|
{
|
|
uint32_t b[2] = {v>>32, v};
|
|
queue(q, b[0]);
|
|
queue(q, b[1]);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = uint64_t(b[0])<<32 | b[1];
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, char &v)
|
|
{
|
|
uint8_t b = v;
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, int8_t &v)
|
|
{
|
|
uint8_t b = v;
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, int16_t &v)
|
|
{
|
|
uint16_t b = v;
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, int32_t &v)
|
|
{
|
|
uint32_t b = v;
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, int64_t &v)
|
|
{
|
|
uint64_t b = v;
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
void queue(const Q &q, float &v)
|
|
{
|
|
/*
|
|
* NB: Not portable.
|
|
* This routine only works on machines with IEEE754 floating point numbers.
|
|
*/
|
|
|
|
#if !defined(__STDC_IEC_559__) \
|
|
&& !defined(__m68k__) && !defined(__sparc__) && !defined(__i386__) \
|
|
&& !defined(__mips__) && !defined(__ns32k__) && !defined(__alpha__) \
|
|
&& !defined(__arm__) && !defined(__ppc__) && !defined(__ia64__) \
|
|
&& !defined(__arm26__) && !defined(__sparc64__) && !defined(__amd64__) \
|
|
&& !defined(WZ_CC_MSVC) // Assume that all platforms supported by
|
|
// MSVC provide IEEE754 floating point numbers
|
|
# error "this platform hasn't been confirmed to support IEEE754 floating point numbers"
|
|
#endif
|
|
|
|
// IEEE754 floating point numbers can be treated the same as 32-bit integers
|
|
// with regards to endian conversion
|
|
uint32_t b;
|
|
std::memcpy(&b, &v, sizeof(b));
|
|
queue(q, b);
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
std::memcpy(&v, &b, sizeof(b));
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(b) == sizeof(v));
|
|
}
|
|
|
|
template<class Q>
|
|
static void queueSmall(const Q &q, int32_t &v)
|
|
{
|
|
// Non-negative values: value*2
|
|
// Negative values: -value*2 - 1
|
|
// Example: int32_t -5 -4 -3 -2 -1 0 1 2 3 4 5
|
|
// becomes uint32_t 9 7 5 3 1 0 2 4 6 8 10
|
|
|
|
uint32_t b = v<<1 ^ (-((uint32_t)v>>31));
|
|
queueSmall(q, b);
|
|
|
|
if (Q::Direction == Q::Read)
|
|
{
|
|
v = b>>1 ^ -(b&1);
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queueSmall(const Q &q, uint32_t &vOrig)
|
|
{
|
|
// Byte n is the final byte, iff it is less than 256-a[n].
|
|
|
|
// Some possible values of a[n], such that the maximum encodable value is 0xFFFFFFFF, satisfying the assertion below:
|
|
//const unsigned a[5] = {14, 127, 74, 127, 0}; // <242: 1 byte, <2048: 2 bytes, <325644: 3 bytes, <17298432: 4 bytes, <4294967296: 5 bytes
|
|
const unsigned a[5] = {78, 95, 32, 70, 0}; // <178: 1 byte, <12736: 2 bytes, <1672576: 3 bytes, <45776896: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {78, 95, 71, 31, 0}; // <178: 1 byte, <12736: 2 bytes, <1383586: 3 bytes, <119758336: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 19, 119, 0}; // <152: 1 byte, <19392: 2 bytes, <1769400: 3 bytes, <20989952: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 20, 113, 0}; // <152: 1 byte, <19392: 2 bytes, <1762016: 3 bytes, <22880256: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 24, 94, 0}; // <152: 1 byte, <19392: 2 bytes, <1732480: 3 bytes, <30441472: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 30, 75, 0}; // <152: 1 byte, <19392: 2 bytes, <1688176: 3 bytes, <41783296: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 38, 59, 0}; // <152: 1 byte, <19392: 2 bytes, <1629104: 3 bytes, <56905728: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 40, 56, 0}; // <152: 1 byte, <19392: 2 bytes, <1614336: 3 bytes, <60686336: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 57, 39, 0}; // <152: 1 byte, <19392: 2 bytes, <1488808: 3 bytes, <92821504: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 60, 37, 0}; // <152: 1 byte, <19392: 2 bytes, <1466656: 3 bytes, <98492416: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 76, 29, 0}; // <152: 1 byte, <19392: 2 bytes, <1348512: 3 bytes, <128737280: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 95, 23, 0}; // <152: 1 byte, <19392: 2 bytes, <1208216: 3 bytes, <164653056: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 114, 19, 0}; // <152: 1 byte, <19392: 2 bytes, <1067920: 3 bytes, <200568832: 4 bytes, <4294967296: 5 bytes
|
|
//const unsigned a[5] = {104, 71, 120, 18, 0}; // <152: 1 byte, <19392: 2 bytes, <1023616: 3 bytes, <211910656: 4 bytes, <4294967296: 5 bytes
|
|
|
|
ASSERT(0xFFFFFFFF == 255*(1 + a[0]*(1 + a[1]*(1 + a[2]*(1 + a[3]*(1 + a[4]))))), "Maximum encodable value not 0xFFFFFFFF.");
|
|
|
|
if (Q::Direction == Q::Write)
|
|
{
|
|
uint32_t v = vOrig;
|
|
for (int n = 0;; ++n)
|
|
{
|
|
if (v < 256 - a[n])
|
|
{
|
|
uint8_t b = v;
|
|
queue(q, b);
|
|
break; // Last encoded byte.
|
|
}
|
|
else
|
|
{
|
|
v -= 256 - a[n];
|
|
uint8_t b = 255 - v%a[n];
|
|
queue(q, b);
|
|
v /= a[n];
|
|
}
|
|
}
|
|
}
|
|
else if (Q::Direction == Q::Read)
|
|
{
|
|
uint32_t v = 0, m = 1;
|
|
for (int n = 0;; ++n)
|
|
{
|
|
uint8_t b;
|
|
queue(q, b);
|
|
|
|
if (b < 256 - a[n])
|
|
{
|
|
v += b*m;
|
|
break; // Last encoded byte.
|
|
}
|
|
else
|
|
{
|
|
v += (256 - a[n] + 255 - b)*m;
|
|
m *= a[n];
|
|
}
|
|
}
|
|
|
|
vOrig = v;
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, Position &v)
|
|
{
|
|
queueSmall(q, v.x);
|
|
queueSmall(q, v.y);
|
|
queueSmall(q, v.z);
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, Rotation &v)
|
|
{
|
|
queue(q, v.direction);
|
|
queue(q, v.pitch);
|
|
queue(q, v.roll);
|
|
}
|
|
|
|
template<class Q, class T>
|
|
static void queue(const Q &q, std::vector<T> &v)
|
|
{
|
|
uint32_t len = v.size();
|
|
queue(q, len);
|
|
switch (Q::Direction)
|
|
{
|
|
case Q::Write:
|
|
for (unsigned i = 0; i != len; ++i)
|
|
{
|
|
queue(q, v[i]);
|
|
}
|
|
break;
|
|
case Q::Read:
|
|
v.clear();
|
|
for (unsigned i = 0; i != len && q.valid(); ++i)
|
|
{
|
|
T tmp;
|
|
queue(q, tmp);
|
|
v.push_back(tmp);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
template<class Q>
|
|
static void queue(const Q &q, NetMessage &v)
|
|
{
|
|
queue(q, v.type);
|
|
queue(q, v.data);
|
|
}
|
|
|
|
template<class T>
|
|
static void queueAuto(T &v)
|
|
{
|
|
if (NETgetPacketDir() == PACKET_ENCODE)
|
|
{
|
|
queue(writer, v);
|
|
}
|
|
else if (NETgetPacketDir() == PACKET_DECODE)
|
|
{
|
|
queue(reader, v);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
static void queueAutoSmall(T &v)
|
|
{
|
|
if (NETgetPacketDir() == PACKET_ENCODE)
|
|
{
|
|
queueSmall(writer, v);
|
|
}
|
|
else if (NETgetPacketDir() == PACKET_DECODE)
|
|
{
|
|
queueSmall(reader, v);
|
|
}
|
|
}
|
|
|
|
// Queue selection functions
|
|
|
|
/// Gets the &NetQueuePair::send or NetQueue *, corresponding to queue.
|
|
static NetQueue *sendQueue(NETQUEUE queue)
|
|
{
|
|
return queue.isPair ? &(*static_cast<NetQueuePair **>(queue.queue))->send : static_cast<NetQueue *>(queue.queue);
|
|
}
|
|
|
|
/// Gets the &NetQueuePair::receive or NetQueue *, corresponding to queue.
|
|
static NetQueue *receiveQueue(NETQUEUE queue)
|
|
{
|
|
return queue.isPair ? &(*static_cast<NetQueuePair **>(queue.queue))->receive : static_cast<NetQueue *>(queue.queue);
|
|
}
|
|
|
|
/// Gets the &NetQueuePair, corresponding to queue.
|
|
static NetQueuePair *&pairQueue(NETQUEUE queue)
|
|
{
|
|
ASSERT(queue.isPair, "Huh?");
|
|
return *static_cast<NetQueuePair **>(queue.queue);
|
|
}
|
|
|
|
NETQUEUE NETnetTmpQueue(unsigned tmpPlayer)
|
|
{
|
|
NETQUEUE ret;
|
|
ASSERT(tmpPlayer < MAX_TMP_SOCKETS, "Huh?");
|
|
NetQueuePair **queue = &tmpQueues[tmpPlayer];
|
|
ret.queue = queue;
|
|
ret.isPair = true;
|
|
ret.index = tmpPlayer;
|
|
ret.queueType = QUEUE_TMP;
|
|
return ret;
|
|
}
|
|
|
|
NETQUEUE NETnetQueue(unsigned player)
|
|
{
|
|
NETQUEUE ret;
|
|
|
|
if (player == NET_ALL_PLAYERS)
|
|
{
|
|
return NETbroadcastQueue();
|
|
}
|
|
|
|
ASSERT(player < MAX_CONNECTED_PLAYERS, "Huh?");
|
|
NetQueuePair **queue = &netQueues[player];
|
|
ret.queue = queue;
|
|
ret.isPair = true;
|
|
ret.index = player;
|
|
ret.queueType = QUEUE_NET;
|
|
return ret;
|
|
}
|
|
|
|
NETQUEUE NETgameQueue(unsigned player)
|
|
{
|
|
NETQUEUE ret;
|
|
ASSERT(player < MAX_PLAYERS, "Huh?");
|
|
NetQueue *queue = gameQueues[player];
|
|
ret.queue = queue;
|
|
ret.isPair = false;
|
|
ret.index = player;
|
|
ret.queueType = QUEUE_GAME;
|
|
return ret;
|
|
}
|
|
|
|
NETQUEUE NETbroadcastQueue()
|
|
{
|
|
NETQUEUE ret;
|
|
NetQueue *queue = broadcastQueue;
|
|
ret.queue = queue;
|
|
ret.isPair = false;
|
|
ret.index = NET_ALL_PLAYERS;
|
|
ret.queueType = QUEUE_BROADCAST;
|
|
return ret;
|
|
}
|
|
|
|
void NETinsertRawData(NETQUEUE queue, uint8_t *data, size_t dataLen)
|
|
{
|
|
receiveQueue(queue)->writeRawData(data, dataLen);
|
|
}
|
|
|
|
void NETinsertMessageFromNet(NETQUEUE queue, NETMESSAGE message)
|
|
{
|
|
receiveQueue(queue)->pushMessage(*message);
|
|
}
|
|
|
|
BOOL NETisMessageReady(NETQUEUE queue)
|
|
{
|
|
return receiveQueue(queue)->haveMessage();
|
|
}
|
|
|
|
NETMESSAGE NETgetMessage(NETQUEUE queue)
|
|
{
|
|
return &receiveQueue(queue)->getMessage();
|
|
}
|
|
|
|
uint8_t NETmessageType(NETMESSAGE message)
|
|
{
|
|
return message->type;
|
|
}
|
|
|
|
uint32_t NETmessageSize(NETMESSAGE message)
|
|
{
|
|
return message->data.size();
|
|
}
|
|
|
|
uint8_t *NETmessageRawData(NETMESSAGE message)
|
|
{
|
|
return message->rawDataDup();
|
|
}
|
|
|
|
void NETmessageDestroyRawData(uint8_t *data)
|
|
{
|
|
delete[] data;
|
|
}
|
|
|
|
size_t NETmessageRawSize(NETMESSAGE message)
|
|
{
|
|
return message->rawLen();
|
|
}
|
|
|
|
|
|
/*
|
|
* Begin & End functions
|
|
*/
|
|
|
|
void NETinitQueue(NETQUEUE queue)
|
|
{
|
|
if (queue.queueType == QUEUE_BROADCAST)
|
|
{
|
|
delete broadcastQueue;
|
|
broadcastQueue = new NetQueue;
|
|
broadcastQueue->setWillNeverGetMessages();
|
|
return;
|
|
}
|
|
else if (queue.queueType == QUEUE_GAME)
|
|
{
|
|
delete gameQueues[queue.index];
|
|
gameQueues[queue.index] = new NetQueue;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
delete pairQueue(queue);
|
|
pairQueue(queue) = new NetQueuePair;
|
|
}
|
|
}
|
|
|
|
void NETsetNoSendOverNetwork(NETQUEUE queue)
|
|
{
|
|
sendQueue(queue)->setWillNeverGetMessagesForNet(); // Will not be sending over net.
|
|
}
|
|
|
|
void NETmoveQueue(NETQUEUE src, NETQUEUE dst)
|
|
{
|
|
ASSERT(src.isPair, "Huh?");
|
|
ASSERT(dst.isPair, "Huh?");
|
|
delete pairQueue(dst);
|
|
pairQueue(dst) = NULL;
|
|
std::swap(pairQueue(src), pairQueue(dst));
|
|
}
|
|
|
|
void NETbeginEncode(NETQUEUE queue, uint8_t type)
|
|
{
|
|
NETsetPacketDir(PACKET_ENCODE);
|
|
|
|
queueInfo = queue;
|
|
message = type;
|
|
writer = MessageWriter(message);
|
|
}
|
|
|
|
void NETbeginDecode(NETQUEUE queue, uint8_t type)
|
|
{
|
|
NETsetPacketDir(PACKET_DECODE);
|
|
|
|
queueInfo = queue;
|
|
message = receiveQueue(queueInfo)->getMessage();
|
|
reader = MessageReader(message);
|
|
|
|
assert(type == message.type);
|
|
}
|
|
|
|
BOOL NETend()
|
|
{
|
|
// If we are encoding just return true
|
|
if (NETgetPacketDir() == PACKET_ENCODE)
|
|
{
|
|
// Push the message onto the list.
|
|
NetQueue *queue = sendQueue(queueInfo);
|
|
queue->pushMessage(message);
|
|
NETlogPacket(message.type, message.data.size(), false);
|
|
|
|
if (queueInfo.queueType == QUEUE_GAME)
|
|
{
|
|
ASSERT(message.type > GAME_MIN_TYPE && message.type < GAME_MAX_TYPE, "Inserting %s into game queue.", messageTypeToString(message.type));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(message.type > NET_MIN_TYPE && message.type < NET_MAX_TYPE, "Inserting %s into net queue.", messageTypeToString(message.type));
|
|
}
|
|
|
|
if (queueInfo.queueType == QUEUE_NET || queueInfo.queueType == QUEUE_BROADCAST)
|
|
{
|
|
NETsend(queueInfo.index, &queue->getMessageForNet());
|
|
queue->popMessageForNet();
|
|
}
|
|
else if (queueInfo.queueType == QUEUE_GAME && queue->checkCanGetMessagesForNet())
|
|
{
|
|
uint8_t player = queueInfo.index; // queueInfo.index is changed by NETbeginEncode.
|
|
|
|
// Not sure exactly where this belongs, but easy to put here in NETend()...
|
|
// Decoded in NETprocessSystemMessage in netplay.c.
|
|
NETbeginEncode(NETbroadcastQueue(), NET_SHARE_GAME_QUEUE);
|
|
NETuint8_t(&player);
|
|
queueAuto(const_cast<NetMessage &>(queue->getMessageForNet())); // const_cast is safe since we are encoding, not decoding.
|
|
NETend(); // Recursive call, but queueInfo.queueType = QUEUE_BROADCAST now.
|
|
|
|
queue->popMessageForNet();
|
|
}
|
|
|
|
// We have ended the serialisation, so mark the direction invalid
|
|
NETsetPacketDir(PACKET_INVALID);
|
|
|
|
return true; // Serialising never fails.
|
|
}
|
|
|
|
if (NETgetPacketDir() == PACKET_DECODE)
|
|
{
|
|
bool ret = reader.valid();
|
|
|
|
// We have ended the deserialisation, so mark the direction invalid
|
|
NETsetPacketDir(PACKET_INVALID);
|
|
|
|
return ret;
|
|
}
|
|
|
|
assert(false && false && false);
|
|
return false;
|
|
}
|
|
|
|
void NETpop(NETQUEUE queue)
|
|
{
|
|
receiveQueue(queue)->popMessage();
|
|
}
|
|
|
|
void NETint8_t(int8_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETuint8_t(uint8_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETint16_t(int16_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETuint16_t(uint16_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETint32_t(int32_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETint32_tSmall(int32_t *ip)
|
|
{
|
|
queueAutoSmall(*ip);
|
|
}
|
|
|
|
void NETuint32_t(uint32_t *ip)
|
|
{
|
|
queueAuto(*ip);
|
|
}
|
|
|
|
void NETuint32_tSmall(uint32_t *ip)
|
|
{
|
|
queueAutoSmall(*ip);
|
|
}
|
|
|
|
void NETfloat(float *fp)
|
|
{
|
|
queueAuto(*fp);
|
|
}
|
|
|
|
void NETbool(BOOL *bp)
|
|
{
|
|
uint8_t i = !!*bp;
|
|
queueAuto(i);
|
|
*bp = !!i;
|
|
}
|
|
|
|
/*
|
|
* NETnull should be used to either add 4 bytes of padding to a message, or to
|
|
* discard 4 bytes of data from a message.
|
|
*/
|
|
void NETnull()
|
|
{
|
|
uint32_t zero = 0;
|
|
NETuint32_t(&zero);
|
|
}
|
|
|
|
/** Sends or receives a string to or from the current network package.
|
|
* \param str When encoding a packet this is the (NUL-terminated string to
|
|
* be sent in the current network package. When decoding this
|
|
* is the buffer to decode the string from the network package
|
|
* into. When decoding this string is guaranteed to be
|
|
* NUL-terminated provided that this buffer is at least 1 byte
|
|
* large.
|
|
* \param maxlen The buffer size of \c str. For static buffers this means
|
|
* sizeof(\c str), for dynamically allocated buffers this is
|
|
* whatever number you passed to malloc().
|
|
* \note If while decoding \c maxlen is smaller than the actual length of the
|
|
* string being decoded, the resulting string (in \c str) will be
|
|
* truncated.
|
|
*/
|
|
void NETstring(char *str, uint16_t maxlen)
|
|
{
|
|
/*
|
|
* Strings sent over the network are prefixed with their length, sent as an
|
|
* unsigned 16-bit integer, not including \0 termination.
|
|
*/
|
|
|
|
uint16_t len = NETgetPacketDir() == PACKET_ENCODE ? strnlen1(str, maxlen) - 1 : 0;
|
|
queueAuto(len);
|
|
|
|
// Truncate length if necessary
|
|
if (len > maxlen - 1)
|
|
{
|
|
debug(LOG_ERROR, "NETstring: %s packet, length %u truncated at %u", NETgetPacketDir() == PACKET_ENCODE ? "Encoding" : "Decoding", len, maxlen);
|
|
len = maxlen - 1;
|
|
}
|
|
|
|
for (unsigned i = 0; i < len; ++i)
|
|
{
|
|
queueAuto(str[i]);
|
|
}
|
|
|
|
if (NETgetPacketDir() == PACKET_DECODE)
|
|
{
|
|
// NUL-terminate
|
|
str[len] = '\0';
|
|
}
|
|
}
|
|
|
|
void NETbin(uint8_t *str, uint32_t maxlen)
|
|
{
|
|
/*
|
|
* Bins sent over the network are prefixed with their length, sent as an
|
|
* unsigned 32-bit integer.
|
|
*/
|
|
|
|
// Work out the length of the bin if we are encoding
|
|
uint32_t len = NETgetPacketDir() == PACKET_ENCODE ? maxlen : 0;
|
|
queueAuto(len);
|
|
|
|
// Truncate length if necessary
|
|
if (len > maxlen)
|
|
{
|
|
debug(LOG_ERROR, "NETbin: Decoding packet, buffer size %u truncated at %u", len, maxlen);
|
|
len = maxlen;
|
|
}
|
|
|
|
for (unsigned i = 0; i < len; ++i)
|
|
{
|
|
queueAuto(str[i]);
|
|
}
|
|
|
|
// Throw away length...
|
|
//maxlen = len;
|
|
}
|
|
|
|
void NETPosition(Position *vp)
|
|
{
|
|
queueAuto(*vp);
|
|
}
|
|
|
|
void NETRotation(Rotation *vp)
|
|
{
|
|
queueAuto(*vp);
|
|
}
|
|
|
|
void NETPACKAGED_CHECK(PACKAGED_CHECK *v)
|
|
{
|
|
queueAuto(v->player);
|
|
queueAuto(v->droidID);
|
|
queueAuto(v->order);
|
|
queueAuto(v->secondaryOrder);
|
|
queueAuto(v->body);
|
|
queueAuto(v->experience);
|
|
queueAuto(v->pos);
|
|
queueAuto(v->rot);
|
|
queueAuto(v->sMoveX);
|
|
queueAuto(v->sMoveY);
|
|
if (v->order == DORDER_ATTACK)
|
|
{
|
|
queueAuto(v->targetID);
|
|
}
|
|
else if (v->order == DORDER_MOVE)
|
|
{
|
|
queueAuto(v->orderX);
|
|
queueAuto(v->orderY);
|
|
}
|
|
}
|
|
|
|
void NETNETMESSAGE(NETMESSAGE *message)
|
|
{
|
|
if (NETgetPacketDir() == PACKET_ENCODE)
|
|
{
|
|
queueAuto(*const_cast<NetMessage *>(*message)); // Const cast safe when encoding.
|
|
}
|
|
|
|
if (NETgetPacketDir() == PACKET_DECODE)
|
|
{
|
|
NetMessage *m = new NetMessage;
|
|
queueAuto(*m);
|
|
*message = m;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void NETdestroyNETMESSAGE(NETMESSAGE message)
|
|
{
|
|
delete message;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
test_a,
|
|
test_b,
|
|
} test_enum;
|
|
|
|
static void NETcoder(PACKETDIR dir)
|
|
{
|
|
(void)dir;
|
|
/* static const char original[] = "THIS IS A TEST STRING";
|
|
char str[sizeof(original)];
|
|
BOOL b = true;
|
|
uint32_t u32 = 32;
|
|
uint16_t u16 = 16;
|
|
uint8_t u8 = 8;
|
|
int32_t i32 = -32;
|
|
int16_t i16 = -16;
|
|
int8_t i8 = -8;
|
|
test_enum te = test_b;
|
|
|
|
sstrcpy(str, original);
|
|
|
|
if (dir == PACKET_ENCODE)
|
|
NETbeginEncode(0, 0);
|
|
else
|
|
NETbeginDecode(0, 0);
|
|
NETbool(&b); assert(b == true);
|
|
NETuint32_t(&u32); assert(u32 == 32);
|
|
NETuint16_t(&u16); assert(u16 == 16);
|
|
NETuint8_t(&u8); assert(u8 == 8);
|
|
NETint32_t(&i32); assert(i32 == -32);
|
|
NETint16_t(&i16); assert(i16 == -16);
|
|
NETint8_t(&i8); assert(i8 == -8);
|
|
NETstring(str, sizeof(str)); assert(strncmp(str, original, sizeof(str) - 1) == 0);
|
|
NETenum(&te); assert(te == test_b);*/
|
|
}
|
|
|
|
void NETtest()
|
|
{
|
|
/*NETMSG cmp;
|
|
|
|
memset(&cmp, 0, sizeof(cmp));
|
|
memset(&NetMsg, 0, sizeof(NetMsg));
|
|
*/
|
|
NETcoder(PACKET_ENCODE);
|
|
/*
|
|
memcpy(&cmp, &NetMsg, sizeof(cmp));
|
|
NETcoder(PACKET_DECODE);
|
|
ASSERT(memcmp(&cmp, &NetMsg, sizeof(cmp)) == 0, "nettypes unit test failed");
|
|
fprintf(stdout, "\tNETtypes self-test: PASSED\n");*/
|
|
ASSERT(false, "nettypes test disabled, since it doesn't compile anymore.");
|
|
}
|