newnet: Fix the host mixing up broadcast messages with messsages to the host, by making NetQueue even more message-oriented. Use -Wall -Werror on C++ files too, and fix the corresponding werrors. Don't use gameRand() from scripts, since the scripts are apparently only executed on the host. Add syncDebug() log function, which is only printed, if it would print different things on different clients.

master
Cyp 2010-02-16 23:02:27 +01:00
parent 5298844797
commit f978943eb6
14 changed files with 434 additions and 188 deletions

View File

@ -204,7 +204,7 @@ if test "x$enable_debug" = "xyes" ; then
WZ_CFLAGS="${WZ_CFLAGS} -O0 -g -Wall -Werror -Wno-unused-label -Wno-pointer-to-int-cast -Wmissing-field-initializers -Wcast-align -Wwrite-strings"
WZ_CFLAGS="${WZ_CFLAGS} -Wmissing-declarations -Wstrict-prototypes -Wpointer-arith -Wno-format-security -Wdeclaration-after-statement"
WZ_C99FLAGS="${WZ_C99FLAGS} -Wno-declaration-after-statement"
WZ_CPPFLAGS="${WZ_CPPFLAGS} -DDEBUG"
WZ_CPPFLAGS="${WZ_CPPFLAGS} -Wall -Werror -DDEBUG"
fi
elif test "x$enable_debug" = "xrelaxed" ; then
WZ_CFLAGS="${WZ_CFLAGS} -g -O0 -Wall -Wextra -Wwrite-strings"

View File

@ -297,6 +297,8 @@ void sendPlayerGameTime()
unsigned player;
uint32_t time = gameTime + latency;
sendDebugSync(true);
for (player = 0; player < MAX_PLAYERS; ++player)
{
if (!myResponsibility(player))

View File

@ -2062,7 +2062,7 @@ UDWORD NETgetPacketsRecvd(void)
// ////////////////////////////////////////////////////////////////////////
// Send a message to a player, option to guarantee message
BOOL NETsend(uint8_t player, const uint8_t *rawData, ssize_t rawLen)
BOOL NETsend(uint8_t player, NETMESSAGE message)
{
ssize_t result = 0;
@ -2082,7 +2082,11 @@ BOOL NETsend(uint8_t player, const uint8_t *rawData, ssize_t rawLen)
// We are the host, send directly to player.
if (connected_bsocket[player] != NULL)
{
uint8_t *rawData = NETmessageRawData(message);
size_t rawLen = NETmessageRawSize(message);
result = writeAll(connected_bsocket[player], rawData, rawLen);
NETmessageDestroyRawData(rawData); // Done with the data.
if (result == rawLen)
{
nStats.bytesSent += rawLen;
@ -2101,39 +2105,42 @@ BOOL NETsend(uint8_t player, const uint8_t *rawData, ssize_t rawLen)
else if (player == NetPlay.hostPlayer)
{
// We are a client, send directly to player, who happens to be the host.
if (tcp_socket && (result = writeAll(tcp_socket, rawData, rawLen) == rawLen))
if (tcp_socket)
{
return true;
}
else if (result == SOCKET_ERROR)
{
// Write error, most likely client disconnect.
debug(LOG_ERROR, "Failed to send message: %s", strSockError(getSockErr()));
debug(LOG_WARNING, "Host connection was broken?");
socketClose(tcp_socket);
tcp_socket = NULL;
NetPlay.players[NetPlay.hostPlayer].heartbeat = false; // mark host as dead
//Game is pretty much over --should just end everything when HOST dies.
NetPlay.isHostAlive = false;
uint8_t *rawData = NETmessageRawData(message);
size_t rawLen = NETmessageRawSize(message);
result = writeAll(tcp_socket, rawData, rawLen);
NETmessageDestroyRawData(rawData); // Done with the data.
if (result == rawLen)
{
nStats.bytesSent += rawLen;
nStats.packetsSent += 1;
}
else if (result == SOCKET_ERROR)
{
// Write error, most likely client disconnect.
debug(LOG_ERROR, "Failed to send message: %s", strSockError(getSockErr()));
debug(LOG_WARNING, "Host connection was broken?");
socketClose(tcp_socket);
tcp_socket = NULL;
NetPlay.players[NetPlay.hostPlayer].heartbeat = false; // mark host as dead
//Game is pretty much over --should just end everything when HOST dies.
NetPlay.isHostAlive = false;
}
return result == rawLen;
}
}
else
{
// We are a client and can't send the data directly, ask the host to send the data to the player.
uint8_t sender = selectedPlayer;
uint16_t len;
while (rawLen > 0)
{
len = MIN(rawLen, 10000);
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_SEND_TO_PLAYER);
NETuint8_t(&sender);
NETuint8_t(&player);
NETuint16_t(&len); // Send length.
NETbin((uint8_t *)rawData, len); // Sends length again. Cast away const.
NETend();
rawData += len;
rawLen -= len;
}
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_SEND_TO_PLAYER);
NETuint8_t(&sender);
NETuint8_t(&player);
NETNETMESSAGE(&message);
NETend();
}
return false;
@ -2150,16 +2157,15 @@ static BOOL NETprocessSystemMessage(NETQUEUE playerQueue, uint8_t type)
{
uint8_t sender;
uint8_t receiver;
uint8_t data[10000];
uint16_t len = sizeof(data);
NETMESSAGE message = NULL;
NETbeginDecode(playerQueue, NET_SEND_TO_PLAYER);
NETuint8_t(&sender);
NETuint8_t(&receiver);
NETuint16_t(&len); // Get length.
NETbin(data, len); // Gets and discards length.
NETNETMESSAGE(&message); // Must destroy message later.
if (!NETend())
{
debug(LOG_ERROR, "Incomplete NET_SEND_TO_PLAYER.");
NETdestroyNETMESSAGE(message);
break;
}
if ((receiver == selectedPlayer || receiver == NET_ALL_PLAYERS) && playerQueue.index == NetPlay.hostPlayer)
@ -2167,7 +2173,7 @@ static BOOL NETprocessSystemMessage(NETQUEUE playerQueue, uint8_t type)
// Message was sent to us via the host.
if (sender != selectedPlayer) // TODO Tell host not to send us our own broadcast messages.
{
NETinsertRawData(NETnetQueue(sender), data, len);
NETinsertMessageFromNet(NETnetQueue(sender), message);
}
}
else if (NetPlay.isHost && sender == playerQueue.index)
@ -2176,31 +2182,31 @@ static BOOL NETprocessSystemMessage(NETQUEUE playerQueue, uint8_t type)
NETbeginEncode(NETnetQueue(receiver), NET_SEND_TO_PLAYER);
NETuint8_t(&sender);
NETuint8_t(&receiver);
NETuint16_t(&len); // Send length.
NETbin(data, len); // Sends length again.
NETNETMESSAGE(&message);
NETend();
if (receiver == NET_ALL_PLAYERS)
{
NETinsertRawData(NETnetQueue(sender), data, len); // Message is also for the host.
NETinsertMessageFromNet(NETnetQueue(sender), message); // Message is also for the host.
}
}
else
{
debug(LOG_ERROR, "Player %d sent us a NET_SEND_TO_PLAYER addressed to %d from %d. We are %d.", playerQueue.index, receiver, sender, selectedPlayer);
}
NETdestroyNETMESSAGE(message);
break;
}
case NET_SHARE_GAME_QUEUE:
{
uint8_t player;
uint32_t size;
uint8_t buffer[65535];
NETMESSAGE message = NULL;
// Encoded in NETprocessSystemMessage in nettypes.cpp.
NETbeginDecode(playerQueue, NET_SHARE_GAME_QUEUE);
NETuint8_t(&player);
NETuint32_t(&size);
NETbin(buffer, sizeof(buffer));
NETNETMESSAGE(&message);
if (!NETend() || player > MAX_PLAYERS)
{
debug(LOG_ERROR, "Bad NET_SHARE_GAME_QUEUE message.");
@ -2208,7 +2214,9 @@ static BOOL NETprocessSystemMessage(NETQUEUE playerQueue, uint8_t type)
}
// TODO Check that playerQueue is actually responsible for this game queue.
NETinsertRawData(NETgameQueue(player), buffer, size);
NETinsertMessageFromNet(NETgameQueue(player), message);
NETdestroyNETMESSAGE(message);
break;
}
case NET_PLAYER_STATS:
@ -2459,7 +2467,7 @@ checkMessages:
*queue = NETnetQueue(current);
while (NETisMessageReady(*queue))
{
*type = NETmessageType(*queue);
*type = NETmessageType(NETgetMessage(*queue));
if (!NETprocessSystemMessage(*queue, *type))
{
return true; // We couldn't process the message, let the caller deal with it..
@ -2480,7 +2488,7 @@ BOOL NETrecvGame(NETQUEUE *queue, uint8_t *type)
*queue = NETgameQueue(current);
while (!checkPlayerGameTime(current) && NETisMessageReady(*queue)) // Check for any messages that are scheduled to be read now.
{
*type = NETmessageType(*queue);
*type = NETmessageType(NETgetMessage(*queue));
if (*type == GAME_GAME_TIME)
{
@ -2994,7 +3002,7 @@ static void NETallowJoining(void)
NETinsertRawData(NETnetTmpQueue(i), buffer, size);
if (NETisMessageReady(NETnetTmpQueue(i)) && NETmessageType(NETnetTmpQueue(i)) == NET_JOIN)
if (NETisMessageReady(NETnetTmpQueue(i)) && NETmessageType(NETgetMessage(NETnetTmpQueue(i))) == NET_JOIN)
{
uint8_t j;
int8_t index;
@ -3615,6 +3623,130 @@ unsigned int NETgetGameserverPort()
return gameserver_port;
}
#define MAX_LEN_LOG_LINE 512 // From debug.c - no use printing something longer.
#define MAX_SYNC_MESSAGES 1000
#define MAX_SYNC_HISTORY 100
static unsigned syncDebugNext = 0;
static uint32_t syncDebugNum[MAX_SYNC_HISTORY];
static uint32_t syncDebugGameTime[MAX_SYNC_HISTORY];
static char *syncDebugFunctions[MAX_SYNC_HISTORY][MAX_SYNC_MESSAGES];
static char *syncDebugStrings[MAX_SYNC_HISTORY][MAX_SYNC_MESSAGES];
void _syncDebug(const char *function, const char *str, ...)
{
va_list ap;
char outputBuffer[MAX_LEN_LOG_LINE];
va_start(ap, str);
vssprintf(outputBuffer, str, ap);
va_end(ap);
if (syncDebugNum[syncDebugNext] < MAX_SYNC_MESSAGES)
{
syncDebugFunctions[syncDebugNext][syncDebugNum[syncDebugNext]] = strdup(function);
syncDebugStrings[syncDebugNext][syncDebugNum[syncDebugNext]] = strdup(outputBuffer);
++syncDebugNum[syncDebugNext];
}
}
void sendDebugSync(bool sendEvenIfEmpty)
{
unsigned i;
if (syncDebugNum[syncDebugNext] == 0 && !sendEvenIfEmpty)
{
return; // Nothing to send.
}
syncDebugGameTime[syncDebugNext] = gameTime;
NETbeginEncode(NETgameQueue(selectedPlayer), GAME_SYNC_DEBUG_STRING);
NETuint32_t(&syncDebugNum[syncDebugNext]);
NETuint32_t(&syncDebugGameTime[syncDebugNext]);
for (i = 0; i < syncDebugNum[syncDebugNext]; ++i)
{
NETstring(syncDebugFunctions[syncDebugNext][i], MAX_LEN_LOG_LINE);
NETstring(syncDebugStrings[syncDebugNext][i], MAX_LEN_LOG_LINE);
}
NETend();
// Go to next position, and free it ready for use.
syncDebugNext = (syncDebugNext + 1)%MAX_SYNC_HISTORY;
for (unsigned i = 0; i != syncDebugNum[syncDebugNext]; ++i)
{
free(syncDebugFunctions[syncDebugNext][i]);
free(syncDebugStrings[syncDebugNext][i]);
}
syncDebugNum[syncDebugNext] = 0;
syncDebugGameTime[syncDebugNext] = 0;
}
void recvDebugSync(NETQUEUE queue)
{
bool outOfSynch = false;
unsigned rsyncDebugNext;
uint32_t rsyncDebugNum;
uint32_t rsyncDebugGameTime;
char rsyncDebugFunction[MAX_LEN_LOG_LINE];
char rsyncDebugString[MAX_LEN_LOG_LINE];
NETbeginDecode(queue, GAME_SYNC_DEBUG_STRING);
NETuint32_t(&rsyncDebugNum);
NETuint32_t(&rsyncDebugGameTime);
if (rsyncDebugGameTime == syncDebugGameTime[syncDebugNext])
{
debug(LOG_ERROR, "Huh? We aren't done yet...");
NETend();
return;
}
for (rsyncDebugNext = 0; rsyncDebugNext != MAX_SYNC_HISTORY; ++rsyncDebugNext)
{
if (syncDebugGameTime[rsyncDebugNext] == rsyncDebugGameTime)
{
unsigned i;
if (syncDebugNum[rsyncDebugNext] != rsyncDebugNum)
{
outOfSynch = true;
}
for (i = 0; i < syncDebugNum[rsyncDebugNext] && !outOfSynch; ++i)
{
NETstring(rsyncDebugFunction, MAX_LEN_LOG_LINE);
NETstring(rsyncDebugString, MAX_LEN_LOG_LINE);
if (strcmp(syncDebugFunctions[rsyncDebugNext][i], rsyncDebugFunction) != 0 ||
strcmp(syncDebugStrings[rsyncDebugNext][i], rsyncDebugString) != 0)
{
outOfSynch = true;
}
}
break;
}
}
NETend();
if (outOfSynch)
{
unsigned i;
debug(LOG_ERROR, "Inconsistent GAME_SYNC_DEBUG_STRING at gameTime %u. My version is: (%u lines)", syncDebugGameTime[rsyncDebugNext], syncDebugNum[rsyncDebugNext]);
for (i = 0; i < syncDebugNum[rsyncDebugNext]; ++i)
{
_debug(LOG_SYNC, syncDebugFunctions[rsyncDebugNext][i], "%s", syncDebugStrings[rsyncDebugNext][i]);
free(syncDebugFunctions[rsyncDebugNext][i]);
free(syncDebugStrings[rsyncDebugNext][i]);
}
syncDebugNum[rsyncDebugNext] = 0;
syncDebugGameTime[rsyncDebugNext] = 0;
}
}
const char *messageTypeToString(unsigned messageType_)
{
MESSAGE_TYPES messageType = (MESSAGE_TYPES)messageType_; // Cast to enum, so switch gives a warning if new message types are added without updating the switch.
@ -3622,69 +3754,73 @@ const char *messageTypeToString(unsigned messageType_)
switch (messageType)
{
// Search: \s*([\w_]+).*
// Replace: case \1: return "\1";
// Replace: case \1: return "\1";
// Search: (case ...............................) *(return "[\w_]+";)
// Replace: \t\t\1\2
case NET_MIN_TYPE: return "NET_MIN_TYPE";
case NET_PING: return "NET_PING";
case NET_PLAYER_STATS: return "NET_PLAYER_STATS";
case NET_TEXTMSG: return "NET_TEXTMSG";
case NET_PLAYERRESPONDING: return "NET_PLAYERRESPONDING";
case NET_OPTIONS: return "NET_OPTIONS";
case NET_KICK: return "NET_KICK";
case NET_FIREUP: return "NET_FIREUP";
case NET_COLOURREQUEST: return "NET_COLOURREQUEST";
case NET_SCORESUBMIT: return "NET_SCORESUBMIT";
case NET_AITEXTMSG: return "NET_AITEXTMSG";
case NET_BEACONMSG: return "NET_BEACONMSG";
case NET_TEAMREQUEST: return "NET_TEAMREQUEST";
case NET_JOIN: return "NET_JOIN";
case NET_ACCEPTED: return "NET_ACCEPTED";
case NET_PLAYER_INFO: return "NET_PLAYER_INFO";
case NET_PLAYER_JOINED: return "NET_PLAYER_JOINED";
case NET_PLAYER_LEAVING: return "NET_PLAYER_LEAVING";
case NET_PLAYER_DROPPED: return "NET_PLAYER_DROPPED";
case NET_GAME_FLAGS: return "NET_GAME_FLAGS";
case NET_READY_REQUEST: return "NET_READY_REQUEST";
case NET_REJECTED: return "NET_REJECTED";
case NET_POSITIONREQUEST: return "NET_POSITIONREQUEST";
case NET_DATA_CHECK: return "NET_DATA_CHECK";
case NET_HOST_DROPPED: return "NET_HOST_DROPPED";
case NET_SEND_TO_PLAYER: return "NET_SEND_TO_PLAYER";
case NET_SHARE_GAME_QUEUE: return "NET_SHARE_GAME_QUEUE";
case NET_FILE_REQUESTED: return "NET_FILE_REQUESTED";
case NET_FILE_CANCELLED: return "NET_FILE_CANCELLED";
case NET_FILE_PAYLOAD: return "NET_FILE_PAYLOAD";
case NET_MAX_TYPE: return "NET_MAX_TYPE";
case GAME_MIN_TYPE: return "GAME_MIN_TYPE";
case GAME_DROID: return "GAME_DROID";
case GAME_DROIDINFO: return "GAME_DROIDINFO";
case GAME_DROIDDEST: return "GAME_DROIDDEST";
case GAME_DROIDMOVE: return "GAME_DROIDMOVE";
case GAME_GROUPORDER: return "GAME_GROUPORDER";
case GAME_TEMPLATE: return "GAME_TEMPLATE";
case GAME_TEMPLATEDEST: return "GAME_TEMPLATEDEST";
case GAME_FEATUREDEST: return "GAME_FEATUREDEST";
case GAME_CHECK_DROID: return "GAME_CHECK_DROID";
case GAME_CHECK_STRUCT: return "GAME_CHECK_STRUCT";
case GAME_CHECK_POWER: return "GAME_CHECK_POWER";
case GAME_BUILD: return "GAME_BUILD";
case GAME_STRUCTDEST: return "GAME_STRUCTDEST";
case GAME_BUILDFINISHED: return "GAME_BUILDFINISHED";
case GAME_RESEARCH: return "GAME_RESEARCH";
case GAME_FEATURES: return "GAME_FEATURES";
case GAME_SECONDARY: return "GAME_SECONDARY";
case GAME_ALLIANCE: return "GAME_ALLIANCE";
case GAME_GIFT: return "GAME_GIFT";
case GAME_DEMOLISH: return "GAME_DEMOLISH";
case GAME_ARTIFACTS: return "GAME_ARTIFACTS";
case GAME_VTOL: return "GAME_VTOL";
case GAME_SECONDARY_ALL: return "GAME_SECONDARY_ALL";
case GAME_DROIDEMBARK: return "GAME_DROIDEMBARK";
case GAME_DROIDDISEMBARK: return "GAME_DROIDDISEMBARK";
case GAME_RESEARCHSTATUS: return "GAME_RESEARCHSTATUS";
case GAME_LASSAT: return "GAME_LASSAT";
case GAME_GAME_TIME: return "GAME_GAME_TIME";
case GAME_MAX_TYPE: return "GAME_MAX_TYPE";
case NET_MIN_TYPE: return "NET_MIN_TYPE";
case NET_PING: return "NET_PING";
case NET_PLAYER_STATS: return "NET_PLAYER_STATS";
case NET_TEXTMSG: return "NET_TEXTMSG";
case NET_PLAYERRESPONDING: return "NET_PLAYERRESPONDING";
case NET_OPTIONS: return "NET_OPTIONS";
case NET_KICK: return "NET_KICK";
case NET_FIREUP: return "NET_FIREUP";
case NET_COLOURREQUEST: return "NET_COLOURREQUEST";
case NET_SCORESUBMIT: return "NET_SCORESUBMIT";
case NET_AITEXTMSG: return "NET_AITEXTMSG";
case NET_BEACONMSG: return "NET_BEACONMSG";
case NET_TEAMREQUEST: return "NET_TEAMREQUEST";
case NET_JOIN: return "NET_JOIN";
case NET_ACCEPTED: return "NET_ACCEPTED";
case NET_PLAYER_INFO: return "NET_PLAYER_INFO";
case NET_PLAYER_JOINED: return "NET_PLAYER_JOINED";
case NET_PLAYER_LEAVING: return "NET_PLAYER_LEAVING";
case NET_PLAYER_DROPPED: return "NET_PLAYER_DROPPED";
case NET_GAME_FLAGS: return "NET_GAME_FLAGS";
case NET_READY_REQUEST: return "NET_READY_REQUEST";
case NET_REJECTED: return "NET_REJECTED";
case NET_POSITIONREQUEST: return "NET_POSITIONREQUEST";
case NET_DATA_CHECK: return "NET_DATA_CHECK";
case NET_HOST_DROPPED: return "NET_HOST_DROPPED";
case NET_SEND_TO_PLAYER: return "NET_SEND_TO_PLAYER";
case NET_SHARE_GAME_QUEUE: return "NET_SHARE_GAME_QUEUE";
case NET_FILE_REQUESTED: return "NET_FILE_REQUESTED";
case NET_FILE_CANCELLED: return "NET_FILE_CANCELLED";
case NET_FILE_PAYLOAD: return "NET_FILE_PAYLOAD";
case NET_MAX_TYPE: return "NET_MAX_TYPE";
case GAME_MIN_TYPE: return "GAME_MIN_TYPE";
case GAME_DROID: return "GAME_DROID";
case GAME_DROIDINFO: return "GAME_DROIDINFO";
case GAME_DROIDDEST: return "GAME_DROIDDEST";
case GAME_DROIDMOVE: return "GAME_DROIDMOVE";
case GAME_GROUPORDER: return "GAME_GROUPORDER";
case GAME_TEMPLATE: return "GAME_TEMPLATE";
case GAME_TEMPLATEDEST: return "GAME_TEMPLATEDEST";
case GAME_FEATUREDEST: return "GAME_FEATUREDEST";
case GAME_CHECK_DROID: return "GAME_CHECK_DROID";
case GAME_CHECK_STRUCT: return "GAME_CHECK_STRUCT";
case GAME_CHECK_POWER: return "GAME_CHECK_POWER";
case GAME_BUILD: return "GAME_BUILD";
case GAME_STRUCTDEST: return "GAME_STRUCTDEST";
case GAME_BUILDFINISHED: return "GAME_BUILDFINISHED";
case GAME_RESEARCH: return "GAME_RESEARCH";
case GAME_FEATURES: return "GAME_FEATURES";
case GAME_SECONDARY: return "GAME_SECONDARY";
case GAME_ALLIANCE: return "GAME_ALLIANCE";
case GAME_GIFT: return "GAME_GIFT";
case GAME_DEMOLISH: return "GAME_DEMOLISH";
case GAME_ARTIFACTS: return "GAME_ARTIFACTS";
case GAME_VTOL: return "GAME_VTOL";
case GAME_SECONDARY_ALL: return "GAME_SECONDARY_ALL";
case GAME_DROIDEMBARK: return "GAME_DROIDEMBARK";
case GAME_DROIDDISEMBARK: return "GAME_DROIDDISEMBARK";
case GAME_RESEARCHSTATUS: return "GAME_RESEARCHSTATUS";
case GAME_LASSAT: return "GAME_LASSAT";
case GAME_GAME_TIME: return "GAME_GAME_TIME";
case GAME_SYNC_DEBUG_STRING: return "GAME_SYNC_DEBUG_STRING";
case GAME_MAX_TYPE: return "GAME_MAX_TYPE";
}
return "(INVALID MESSAGE TYPE)";
}

View File

@ -114,6 +114,7 @@ typedef enum
GAME_RESEARCHSTATUS, ///< research state.
GAME_LASSAT, ///< lassat firing.
GAME_GAME_TIME, ///< Game time. Used for synchronising, so that all messages are executed at the same gameTime on all clients.
GAME_SYNC_DEBUG_STRING, ///< For debugging purposes only. For checking if all clients do the same syncDebug() calls.
GAME_MAX_TYPE ///< Maximum+1 valid GAME_ type, *MUST* be last.
} MESSAGE_TYPES;
@ -265,7 +266,7 @@ extern int mapDownloadProgress;
// ////////////////////////////////////////////////////////////////////////
// functions available to you.
extern int NETinit(BOOL bFirstCall); // init
BOOL NETsend(uint8_t player, const uint8_t *rawData, ssize_t rawLen); ///< send to player, or broadcast if player == NET_ALL_PLAYERS.
BOOL NETsend(uint8_t player, NETMESSAGE message); ///< send to player, or broadcast if player == NET_ALL_PLAYERS.
extern BOOL NETrecvNet(NETQUEUE *queue, uint8_t *type); ///< recv a message from the net queues if possible.
extern BOOL NETrecvGame(NETQUEUE *queue, uint8_t *type); ///< recv a message from the game queues which is sceduled to execute by time, if possible.
@ -318,6 +319,13 @@ extern void NET_InitPlayers(void);
const char *messageTypeToString(unsigned messageType);
/// Sync debugging. Only prints anything, if different players do different calls. Doesn't print anything, if all players that do the same calls get the same result, even if some players don't do any calls.
#define syncDebug(...) do { _syncDebug(__FUNCTION__, __VA_ARGS__); } while(0)
void _syncDebug(const char *function, const char *str, ...)
WZ_DECL_FORMAT(printf, 2, 3);
void sendDebugSync(bool sendEvenIfEmpty);
void recvDebugSync(NETQUEUE queue);
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -3,8 +3,25 @@
// See comments in netqueue.h.
uint8_t *NetMessage::rawDataDup() const
{
uint8_t *ret = new uint8_t[5 + data.size()];
ret[0] = type;
ret[1] = data.size()>>24;
ret[2] = data.size()>>16;
ret[3] = data.size()>> 8;
ret[4] = data.size();
std::copy(data.begin(), data.end(), ret + 5);
return ret;
}
size_t NetMessage::rawLen() const
{
return 5 + data.size();
}
NetQueue::NetQueue()
: canReadRawData(true)
: canGetMessagesForNet(true)
, canGetMessages(true)
{
dataPos = messages.end();
@ -22,8 +39,9 @@ void NetQueue::writeRawData(const uint8_t *netData, size_t netLen)
// Extract the messages.
while (buffer.size() - used >= 5)
{
uint32_t len = buffer[used]<<24 | buffer[used + 1]<<16 | buffer[used + 2]<<8 | buffer[used + 3];
uint8_t type = buffer[used + 4];
uint8_t type = buffer[used];
uint32_t len = buffer[used + 1]<<24 | buffer[used + 2]<<16 | buffer[used + 3]<<8 | buffer[used + 4];
ASSERT(len < 40000000, "Trying to writing a very large packet (%u bytes) to the queue.", len);
if (buffer.size() - used - 5 < len)
{
break;
@ -37,53 +55,37 @@ void NetQueue::writeRawData(const uint8_t *netData, size_t netLen)
buffer.erase(buffer.begin(), buffer.begin() + used);
}
void NetQueue::setWillNeverReadRawData()
void NetQueue::setWillNeverGetMessagesForNet()
{
canReadRawData = false;
canGetMessagesForNet = false;
}
bool NetQueue::checkCanReadRawData() const
bool NetQueue::checkCanGetMessagesForNet() const
{
return canReadRawData;
return canGetMessagesForNet;
}
void NetQueue::readRawData(const uint8_t **netData, size_t *netLen)
const NetMessage &NetQueue::getMessageForNet() const
{
ASSERT(canReadRawData, "Wrong NetQueue type for readRawData.");
ASSERT(canGetMessagesForNet, "Wrong NetQueue type for getMessageForNet.");
ASSERT(dataPos != messages.begin(), "No message to get!");
std::vector<uint8_t> &buffer = unsentMessageData; // Short alias.
// Return the message.
List::iterator i = dataPos;
--i;
return *i;
}
// Turn the messages into raw data.
while (dataPos != messages.begin())
{
--dataPos;
buffer.push_back(dataPos->data.size()>>24);
buffer.push_back(dataPos->data.size()>>16);
buffer.push_back(dataPos->data.size()>> 8);
buffer.push_back(dataPos->data.size() );
buffer.push_back(dataPos->type);
buffer.insert(buffer.end(), dataPos->data.begin(), dataPos->data.end());
}
void NetQueue::popMessageForNet()
{
ASSERT(canGetMessagesForNet, "Wrong NetQueue type for popMessageForNet.");
ASSERT(dataPos != messages.begin(), "No message to pop!");
// Pop the message.
--dataPos;
// Recycle old data.
popOldMessages();
// Return the data.
*netData = &buffer[0];
*netLen = buffer.size();
}
void NetQueue::popRawData(size_t netLen)
{
ASSERT(canReadRawData, "Wrong NetQueue type for popRawData.");
if (netLen > unsentMessageData.size())
{
debug(LOG_WARNING, "Popped too much data (popped %zu, had %zu). Someone probably just disconnected.", netLen, unsentMessageData.size());
netLen = unsentMessageData.size();
}
// Pop the data.
unsentMessageData.erase(unsentMessageData.begin(), unsentMessageData.begin() + netLen);
}
void NetQueue::pushMessage(const NetMessage &message)
@ -127,7 +129,7 @@ void NetQueue::popMessage()
void NetQueue::popOldMessages()
{
if (!canReadRawData)
if (!canGetMessagesForNet)
{
dataPos = messages.begin();
}

View File

@ -23,6 +23,8 @@ class NetMessage
{
public:
NetMessage(uint8_t type_ = 0xFF) : type(type_) {}
uint8_t *rawDataDup() const; ///< Returns data compatible with NetQueue::writeRawData(). Must be delete[]d.
size_t rawLen() const; ///< Returns the length of the return value of rawDataDup().
uint8_t type;
std::vector<uint8_t> data;
};
@ -36,6 +38,7 @@ public:
MessageWriter(NetMessage *m = NULL) : message(m) {}
MessageWriter(NetMessage &m) : message(&m) {}
void byte(uint8_t v) const { message->data.push_back(v); }
bool valid() const { return true; }
NetMessage *message;
};
/// MessageReader is used for deserialising, using the same interface as MessageWriter.
@ -61,10 +64,10 @@ public:
// Network related, receiving
void writeRawData(const uint8_t *netData, size_t netLen); ///< Inserts data from the network into the NetQueue.
// Network related, sending
void setWillNeverReadRawData(); ///< Marks that we will not be sending this data over the network.
bool checkCanReadRawData() const; ///< Checks if we marked that we will not be sending this data over the network.
void readRawData(const uint8_t **netData, size_t *netLen); ///< Extracts data from the NetQueue to send over the network.
void popRawData(size_t netLen); ///< Pops the extracted data, so that future readRawData calls do not return that data.
void setWillNeverGetMessagesForNet(); ///< Marks that we will not be sending this data over the network.
bool checkCanGetMessagesForNet() const; ///< Checks that we didn't mark that we will not be sending this data over the network.
const NetMessage &getMessageForNet() const; ///< Extracts data from the NetQueue to send over the network.
void popMessageForNet(); ///< Pops the extracted data, so that future getMessageForNet calls do not return that data.
// All game clients should check game messages from all queues, including their own, and only the net messages sent to them.
// Message related, storing.
@ -82,7 +85,7 @@ private:
NetQueue(const NetQueue &); // TODO When switching to C++0x, use "= delete" notation.
void operator =(const NetQueue &); // TODO When switching to C++0x, use "= delete" notation.
bool canReadRawData; ///< True if we will send the messages over the network, false if we don't.
bool canGetMessagesForNet; ///< True if we will send the messages over the network, false if we don't.
bool canGetMessages; ///< True if we will get the messages, false if we don't use them ourselves.
typedef std::list<NetMessage> List;
@ -90,7 +93,6 @@ private:
List::iterator messagePos; ///< Last message which was popped.
List messages; ///< List of messages. Messages are added to the front and read from the back.
std::vector<uint8_t> incompleteReceivedMessageData; ///< Data from network which has not yet formed an entire message.
std::vector<uint8_t> unsentMessageData; ///< Data for network which has been requested but not yet popped.
};
/// A NetQueuePair is used for talking to a socket. We insert NetMessages in the send NetQueue, which converts the messages into a stream of bytes for the
@ -98,7 +100,7 @@ private:
class NetQueuePair
{
public:
NetQueuePair() { send.setWillNeverGetMessages(); receive.setWillNeverReadRawData(); }
NetQueuePair() { send.setWillNeverGetMessages(); receive.setWillNeverGetMessagesForNet(); }
NetQueue send;
NetQueue receive;

View File

@ -193,6 +193,38 @@ static void queue(const Q &q, Vector3uw &v)
queue(q, v.z);
}
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)
{
@ -285,21 +317,47 @@ 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();
}
uint8_t NETmessageType(NETQUEUE queue)
NETMESSAGE NETgetMessage(NETQUEUE queue)
{
return receiveQueue(queue)->getMessage().type;
return &receiveQueue(queue)->getMessage();
}
uint32_t NETmessageSize(NETQUEUE queue)
uint8_t NETmessageType(NETMESSAGE message)
{
return receiveQueue(queue)->getMessage().data.size();
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
*/
@ -328,7 +386,7 @@ void NETinitQueue(NETQUEUE queue)
void NETsetNoSendOverNetwork(NETQUEUE queue)
{
sendQueue(queue)->setWillNeverReadRawData(); // Will not be sending over net.
sendQueue(queue)->setWillNeverGetMessagesForNet(); // Will not be sending over net.
}
void NETmoveQueue(NETQUEUE src, NETQUEUE dst)
@ -381,31 +439,21 @@ BOOL NETend()
if (queueInfo.queueType == QUEUE_NET || queueInfo.queueType == QUEUE_BROADCAST)
{
const uint8_t *data;
size_t dataLen;
queue->readRawData(&data, &dataLen);
NETsend(queueInfo.index, data, dataLen);
queue->popRawData(dataLen);
NETsend(queueInfo.index, &queue->getMessageForNet());
queue->popMessageForNet();
}
else if (queueInfo.queueType == QUEUE_GAME && queue->checkCanReadRawData())
else if (queueInfo.queueType == QUEUE_GAME && queue->checkCanGetMessagesForNet())
{
uint8_t player = queueInfo.index; // queueInfo.index is changed by NETbeginEncode.
const uint8_t *data;
size_t dataLen;
queue->readRawData(&data, &dataLen);
// 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);
uint32_t size = dataLen;
NETuint32_t(&size);
NETbin(const_cast<uint8_t *>(data), dataLen); // const_cast is safe since we are encoding, not decoding.
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->popRawData(dataLen);
queue->popMessageForNet();
}
// We have ended the serialisation, so mark the direction invalid
@ -418,8 +466,6 @@ BOOL NETend()
{
bool ret = reader.valid();
//receiveQueue(queueInfo)->popMessage(); // Moved to NETpop(), since some messages call NETbeginEncode but not NETend, and others call neither.
// We have ended the deserialisation, so mark the direction invalid
NETsetPacketDir(PACKET_INVALID);
@ -530,15 +576,15 @@ void NETstring(char *str, uint16_t maxlen)
}
}
void NETbin(uint8_t *str, uint16_t maxlen)
void NETbin(uint8_t *str, uint32_t maxlen)
{
/*
* Bins sent over the network are prefixed with their length, sent as an
* unsigned 16-bit integer.
* unsigned 32-bit integer.
*/
// Work out the length of the bin if we are encoding
uint16_t len = NETgetPacketDir() == PACKET_ENCODE ? maxlen : 0;
uint32_t len = NETgetPacketDir() == PACKET_ENCODE ? maxlen : 0;
queueAuto(len);
// Truncate length if necessary
@ -584,6 +630,27 @@ void NETPACKAGED_CHECK(PACKAGED_CHECK *v)
}
}
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,
@ -626,7 +693,9 @@ void NETtest()
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");

View File

@ -56,15 +56,22 @@ typedef struct _netqueue
uint8_t queueType;
} NETQUEUE;
typedef const struct NetMessage *NETMESSAGE;
NETQUEUE NETnetTmpQueue(unsigned tmpPlayer); ///< One of the temp queues from before a client has joined the game. (See comments on tmpQueues in nettypes.cpp.)
NETQUEUE NETnetQueue(unsigned player); ///< The queue pair used for sending and receiving data directly from another client. (See comments on netQueues in nettypes.cpp.)
NETQUEUE NETgameQueue(unsigned player); ///< The game action queue. (See comments on gameQueues in nettypes.cpp.)
NETQUEUE NETbroadcastQueue(void); ///< The queue for sending data directly to the netQueues of all clients, not just a specific one. (See comments on broadcastQueue in nettypes.cpp.)
void NETinsertRawData(NETQUEUE queue, uint8_t *data, size_t dataLen); /// Dump raw data from sockets and raw data sent via host here.
void NETinsertRawData(NETQUEUE queue, uint8_t *data, size_t dataLen); ///< Dump raw data from sockets and raw data sent via host here.
void NETinsertMessageFromNet(NETQUEUE queue, NETMESSAGE message); ///< Dump whole NetMessages into the queue.
BOOL NETisMessageReady(NETQUEUE queue); ///< Returns true if there is a complete message ready to deserialise in this queue.
uint8_t NETmessageType(NETQUEUE queue); ///< Returns the type of the message in this queue which is ready to be deserialised.
uint32_t NETmessageSize(NETQUEUE queue); ///< Returns the size of the message data in this queue which is ready to be deserialised.
NETMESSAGE NETgetMessage(NETQUEUE queue); ///< Returns the current message in the queue which is ready to be deserialised. Do not delete the message.
uint8_t NETmessageType(NETMESSAGE message); ///< Returns the type of the message.
uint32_t NETmessageSize(NETMESSAGE message); ///< Returns the size of the message data.
uint8_t *NETmessageRawData(NETMESSAGE message);///<Returns the raw data, must be deleted again with NETmessageDestroyRawData().
void NETmessageDestroyRawData(uint8_t *data); ///< Destroys the data returned by NETmessageRawData().
size_t NETmessageRawSize(NETMESSAGE message); ///< Returns the size of the message, including headers.
void NETinitQueue(NETQUEUE queue); ///< Allocates the queue. Deletes the old queue, if there was one. Avoids a crash on NULL pointer deference when trying to use the queue.
void NETsetNoSendOverNetwork(NETQUEUE queue); ///< Used to mark that a game queue should not be sent over the network (for example, if it is being sent to us, instead).
@ -85,7 +92,7 @@ void NETfloat(float* fp);
void NETbool(BOOL *bp);
void NETnull(void);
void NETstring(char *str, uint16_t maxlen);
void NETbin(uint8_t *str, uint16_t maxlen);
void NETbin(uint8_t *str, uint32_t maxlen);
PACKETDIR NETgetPacketDir(void);
@ -142,6 +149,8 @@ typedef struct PackagedCheck
uint16_t orderY; ///< Defined iff order == DORDER_MOVE.
} PACKAGED_CHECK;
void NETPACKAGED_CHECK(PACKAGED_CHECK *v);
void NETNETMESSAGE(NETMESSAGE *message); ///< If decoding, must destroy the NETMESSAGE.
void NETdestroyNETMESSAGE(NETMESSAGE message);
void NETtest(void);

View File

@ -439,7 +439,7 @@ extern bool fireOnLocation(unsigned int x, unsigned int y);
* Transitive sensor check for tile. Has to be here rather than
* visibility.h due to header include order issues.
*/
static inline bool hasSensorOnTile(MAPTILE *psTile, int player)
static inline bool hasSensorOnTile(MAPTILE *psTile, unsigned player)
{
return ((player == selectedPlayer && godMode) || (alliancebits[selectedPlayer] & (satuplinkbits | psTile->sensorBits)));
}

View File

@ -826,6 +826,9 @@ BOOL recvMessage(void)
case NET_PLAYER_STATS:
recvMultiStats(queue);
break;
case GAME_SYNC_DEBUG_STRING:
recvDebugSync(queue);
break;
default:
break;
}

View File

@ -42,17 +42,17 @@ void MersenneTwister::generate()
// Loop tripled, to avoid using %624 everywhere.
for (unsigned i = 0; i != 227; ++i)
{
int v = state[i]&0x80000000 | state[i + 1 ]&0x7FFFFFFF;
int v = (state[i]&0x80000000) | (state[i + 1 ]&0x7FFFFFFF);
state[i] = state[i + 397 ] ^ v>>1 ^ ((v&0x00000001)*0x9908B0DF);
}
for (unsigned i = 227; i != 623; ++i)
{
int v = state[i]&0x80000000 | state[i + 1 ]&0x7FFFFFFF;
int v = (state[i]&0x80000000) | (state[i + 1 ]&0x7FFFFFFF);
state[i] = state[i + 397 - 624] ^ v>>1 ^ ((v&0x00000001)*0x9908B0DF);
}
for (unsigned i = 623; i != 624; ++i) // Very short loop.
{
int v = state[i]&0x80000000 | state[i + 1 - 624]&0x7FFFFFFF;
int v = (state[i]&0x80000000) | (state[i + 1 - 624]&0x7FFFFFFF);
state[i] = state[i + 397 - 624] ^ v>>1 ^ ((v&0x00000001)*0x9908B0DF);
}
}
@ -67,7 +67,17 @@ uint32_t gameRandU32()
return gamePseudorandomNumberGenerator.u32();
}
#ifndef SYNC_DEBUG_RANDOM
int32_t gameRand(uint32_t limit)
{
return gamePseudorandomNumberGenerator.u32()%limit;
}
#else //SYNC_DEBUG_RANDOM
#include "lib/netplay/netplay.h"
int32_t _gameRand(uint32_t limit, const char *caller, unsigned line)
{
int32_t ret = gamePseudorandomNumberGenerator.u32()%limit;
syncDebug("%s:%u got %u", caller, line, ret);
return ret;
}
#endif //SYNC_DEBUG_RANDOM

View File

@ -34,7 +34,12 @@ uint32_t gameRandU32(void);
/// Generates a random number in the interval [0...limit - 1]. Not equidistributed for large non-powers of 2.
/// Must not be called from graphics routines, only for making game decisions.
#ifndef SYNC_DEBUG_RANDOM
int32_t gameRand(uint32_t limit);
#else //SYNC_DEBUG_RANDOM
#define gameRand(x) _gameRand(x, __FUNCTION__, __LINE__)
int32_t _gameRand(uint32_t limit, const char *caller, unsigned line);
#endif //SYNC_DEBUG_RANDOM
#ifdef __cplusplus
}

View File

@ -4402,7 +4402,7 @@ BOOL scrRandom(void)
}
else
{
iResult = gameRand(abs(range));
iResult = rand()%abs(range);
}
scrFunctionResult.v.ival = iResult;

View File

@ -52,7 +52,7 @@ static std::vector<WavecastTile> generateWavecastTable(unsigned radius)
std::vector<RationalAngle> unsortedAngles;
for (int diamond = 1; diamond*TILE_UNITS < radius*2; ++diamond) // Factor is a bit more than needed to surround the circle with diamonds.
for (int diamond = 1; (unsigned)diamond*TILE_UNITS < radius*2; ++diamond) // Factor is a bit more than needed to surround the circle with diamonds.
{
for (int quadrant = 0; quadrant < 4; ++quadrant)
{