From e18421875ab21fd3d6a00606d0c237df6ec0c47e Mon Sep 17 00:00:00 2001 From: joris <joris.dauphin@gmail.com> Date: Tue, 26 Mar 2013 00:03:43 +0100 Subject: [PATCH] All net messages begin with messageType. --- src/include/net_message.h | 10 +- src/network/net_message.cpp | 193 +++++++++++++++++++++++------------- src/network/netconnect.cpp | 76 +++++++------- src/network/network.cpp | 8 +- 4 files changed, 166 insertions(+), 121 deletions(-) diff --git a/src/include/net_message.h b/src/include/net_message.h index f4f699f9b..f255ae877 100644 --- a/src/include/net_message.h +++ b/src/include/net_message.h @@ -45,7 +45,6 @@ #define MaxNetworkCommands 9 /// Max Commands In A Packet - /** ** Network systems active in current game. */ @@ -171,9 +170,8 @@ public: */ enum _message_type_ { MessageNone, /// When Nothing Is Happening - MessageInitHello, /// Start connection - MessageInitReply, /// Connection reply - MessageInitConfig, /// Setup message configure clients + MessageInit_FromClient, /// Start connection + MessageInit_FromServer, /// Connection reply MessageSync, /// Heart beat MessageSelection, /// Update a Selection from Team Player @@ -313,8 +311,8 @@ public: void Deserialize(const unsigned char *p); static size_t Size() { return 1 + 1 * MaxNetworkCommands; } - uint8_t Cycle; /// Destination game cycle uint8_t Type[MaxNetworkCommands]; /// Commands in packet + uint8_t Cycle; /// Destination game cycle }; /** @@ -325,7 +323,7 @@ public: class CNetworkPacket { public: - unsigned char *Serialize(int numcommands) const; + void Serialize(unsigned char *buf, int numcommands) const; int Deserialize(const unsigned char *p, unsigned int len); static size_t Size(int numcommands) { return CNetworkPacketHeader::Size() + numcommands * CNetworkCommand::Size(); diff --git a/src/network/net_message.cpp b/src/network/net_message.cpp index fe5ada17f..fa12428bd 100644 --- a/src/network/net_message.cpp +++ b/src/network/net_message.cpp @@ -42,6 +42,80 @@ #include "network.h" #include "version.h" +int serialize32(unsigned char *buf, uint32_t data) +{ + if (buf) { + *reinterpret_cast<uint32_t *>(buf) = htonl(data); + } + return sizeof(data); +} +int serialize32(unsigned char *buf, int32_t data) +{ + if (buf) { + *reinterpret_cast<int32_t *>(buf) = htonl(data); + } + return sizeof(data); +} +int serialize16(unsigned char *buf, uint16_t data) +{ + if (buf) { + *reinterpret_cast<uint16_t *>(buf) = htons(data); + } + return sizeof(data); +} +int serialize16(unsigned char *buf, int16_t data) +{ + if (buf) { + *reinterpret_cast<int16_t *>(buf) = htons(data); + } + return sizeof(data); +} +int serialize8(unsigned char *buf, uint8_t data) +{ + if (buf) { + *buf = data; + } + return sizeof(data); +} +int serialize8(unsigned char *buf, int8_t data) +{ + if (buf) { + *buf = data; + } + return sizeof(data); +} + +int deserialize32(const unsigned char *buf, uint32_t *data) +{ + *data = ntohl(*reinterpret_cast<const uint32_t *>(buf)); + return sizeof(*data); +} +int deserialize32(const unsigned char *buf, int32_t *data) +{ + *data = ntohl(*reinterpret_cast<const int32_t *>(buf)); + return sizeof(*data); +} +int deserialize16(const unsigned char *buf, uint16_t *data) +{ + *data = ntohs(*reinterpret_cast<const uint16_t *>(buf)); + return sizeof(*data); +} +int deserialize16(const unsigned char *buf, int16_t *data) +{ + *data = ntohs(*reinterpret_cast<const int16_t *>(buf)); + return sizeof(*data); +} +int deserialize8(const unsigned char *buf, uint8_t *data) +{ + *data = *buf; + return sizeof(*data); +} +int deserialize8(const unsigned char *buf, int8_t *data) +{ + *data = *buf; + return sizeof(*data); +} + // // CNetworkHost // @@ -51,12 +125,9 @@ const unsigned char *CNetworkHost::Serialize() const unsigned char *buf = new unsigned char[CNetworkHost::Size()]; unsigned char *p = buf; - *(uint32_t *)p = htonl(this->Host); - p += 4; - *(uint16_t *)p = htons(this->Port); - p += 2; - *(uint16_t *)p = htons(this->PlyNr); - p += 2; + p += serialize32(p, this->Host); + p += serialize16(p, this->Port); + p += serialize16(p, this->PlyNr); memcpy(p, this->PlyName, sizeof(this->PlyName)); return buf; @@ -64,12 +135,9 @@ const unsigned char *CNetworkHost::Serialize() const void CNetworkHost::Deserialize(const unsigned char *p) { - this->Host = ntohl(*(uint32_t *)p); - p += 4; - this->Port = ntohs(*(uint16_t *)p); - p += 2; - this->PlyNr = ntohs(*(uint16_t *)p); - p += 2; + p += deserialize32(p, &Host); + p += deserialize16(p, &Port); + p += deserialize16(p, &PlyNr); memcpy(this->PlyName, p, sizeof(this->PlyName)); } @@ -95,44 +163,44 @@ const unsigned char *CServerSetup::Serialize() const unsigned char *buf = new unsigned char[CServerSetup::Size()]; unsigned char *p = buf; - *p++ = this->ResourcesOption; - *p++ = this->UnitsOption; - *p++ = this->FogOfWar; - *p++ = this->RevealMap; - *p++ = this->TilesetSelection; - *p++ = this->GameTypeOption; - *p++ = this->Difficulty; - *p++ = this->MapRichness; + p += serialize8(p, this->ResourcesOption); + p += serialize8(p, this->UnitsOption); + p += serialize8(p, this->FogOfWar); + p += serialize8(p, this->RevealMap); + p += serialize8(p, this->TilesetSelection); + p += serialize8(p, this->GameTypeOption); + p += serialize8(p, this->Difficulty); + p += serialize8(p, this->MapRichness); for (int i = 0; i < PlayerMax; ++i) { - *p++ = this->CompOpt[i]; + p += serialize8(p, this->CompOpt[i]); } for (int i = 0; i < PlayerMax; ++i) { - *p++ = this->Ready[i]; + p += serialize8(p, this->Ready[i]); } for (int i = 0; i < PlayerMax; ++i) { - *p++ = this->Race[i]; + p += serialize8(p, this->Race[i]); } return buf; } void CServerSetup::Deserialize(const unsigned char *p) { - this->ResourcesOption = *p++; - this->UnitsOption = *p++; - this->FogOfWar = *p++; - this->RevealMap = *p++; - this->TilesetSelection = *p++; - this->GameTypeOption = *p++; - this->Difficulty = *p++; - this->MapRichness = *p++; + p += deserialize8(p, &this->ResourcesOption); + p += deserialize8(p, &this->UnitsOption); + p += deserialize8(p, &this->FogOfWar); + p += deserialize8(p, &this->RevealMap); + p += deserialize8(p, &this->TilesetSelection); + p += deserialize8(p, &this->GameTypeOption); + p += deserialize8(p, &this->Difficulty); + p += deserialize8(p, &this->MapRichness); for (int i = 0; i < PlayerMax; ++i) { - this->CompOpt[i] = *p++; + p += deserialize8(p, &this->CompOpt[i]); } for (int i = 0; i < PlayerMax; ++i) { - this->Ready[i] = *p++; + p += deserialize8(p, &this->Ready[i]); } for (int i = 0; i < PlayerMax; ++i) { - this->Race[i] = *p++; + p += deserialize8(p, &this->Race[i]); } } @@ -155,20 +223,15 @@ const unsigned char *CInitMessage::Serialize() const unsigned char *buf = new unsigned char[CInitMessage::Size()]; unsigned char *p = buf; - *p++ = this->Type; - *p++ = this->SubType; - *p++ = this->HostsCount; - *p++ = this->padding; - *(int32_t *)p = htonl(this->Stratagus); - p += 4; - *(int32_t *)p = htonl(this->Version); - p += 4; - *(uint32_t *)p = htonl(this->MapUID); - p += 4; - *(int32_t *)p = htonl(this->Lag); - p += 4; - *(int32_t *)p = htonl(this->Updates); - p += 4; + p += serialize8(p, this->Type); + p += serialize8(p, this->SubType); + p += serialize8(p, this->HostsCount); + p += serialize8(p, this->padding); + p += serialize32(p, this->Stratagus); + p += serialize32(p, this->Version); + p += serialize32(p, this->MapUID); + p += serialize32(p, this->Lag); + p += serialize32(p, this->Updates); switch (this->SubType) { case ICMHello: @@ -200,20 +263,15 @@ const unsigned char *CInitMessage::Serialize() const void CInitMessage::Deserialize(const unsigned char *p) { - this->Type = *p++; - this->SubType = *p++; - this->HostsCount = *p++; - this->padding = *p++; - this->Stratagus = ntohl(*(int32_t *)p); - p += 4; - this->Version = ntohl(*(int32_t *)p); - p += 4; - this->MapUID = ntohl(*(uint32_t *)p); - p += 4; - this->Lag = ntohl(*(int32_t *)p); - p += 4; - this->Updates = ntohl(*(int32_t *)p); - p += 4; + p += deserialize8(p, &this->Type); + p += deserialize8(p, &this->SubType); + p += deserialize8(p, &this->HostsCount); + p += deserialize8(p, &this->padding); + p += deserialize32(p, &this->Stratagus); + p += deserialize32(p, &this->Version); + p += deserialize32(p, &this->MapUID); + p += deserialize32(p, &this->Lag); + p += deserialize32(p, &this->Updates); switch (this->SubType) { case ICMHello: @@ -237,7 +295,6 @@ void CInitMessage::Deserialize(const unsigned char *p) } } - // // CNetworkCommand // @@ -266,7 +323,6 @@ void CNetworkCommand::Deserialize(const unsigned char *p) p += 2; } - // // CNetworkExtendedCommand // @@ -321,18 +377,18 @@ void CNetworkChat::Deserialize(const unsigned char *p) void CNetworkPacketHeader::Serialize(unsigned char *p) const { - *p++ = this->Cycle; for (int i = 0; i < MaxNetworkCommands; ++i) { *p++ = this->Type[i]; } + *p++ = this->Cycle; } void CNetworkPacketHeader::Deserialize(const unsigned char *p) { - this->Cycle = *p++; for (int i = 0; i < MaxNetworkCommands; ++i) { this->Type[i] = *p++; } + this->Cycle = *p++; } @@ -340,9 +396,8 @@ void CNetworkPacketHeader::Deserialize(const unsigned char *p) // CNetworkPacket // -unsigned char *CNetworkPacket::Serialize(int numcommands) const +void CNetworkPacket::Serialize(unsigned char *buf, int numcommands) const { - unsigned char *buf = new unsigned char[CNetworkPacket::Size(numcommands)]; unsigned char *p = buf; this->Header.Serialize(p); @@ -358,8 +413,6 @@ unsigned char *CNetworkPacket::Serialize(int numcommands) const } p += CNetworkCommand::Size(); } - - return buf; } int CNetworkPacket::Deserialize(const unsigned char *p, unsigned int len) diff --git a/src/network/netconnect.cpp b/src/network/netconnect.cpp index 471c4e352..a57f07606 100644 --- a/src/network/netconnect.cpp +++ b/src/network/netconnect.cpp @@ -333,7 +333,7 @@ bool CClient::Update_disconnected() Assert(networkState.State == ccs_disconnected); CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMSeeYou; // Spew out 5 and trust in God that they arrive for (int i = 0; i < 5; ++i) { @@ -350,7 +350,7 @@ bool CClient::Update_detaching(unsigned long tick) if (networkState.MsgCnt < 10) { // 10 retries = 1 second CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMGoodBye; NetworkSendRateLimitedClientMessage(message, tick, 100); return true; @@ -368,7 +368,7 @@ bool CClient::Update_connecting(unsigned long tick) if (networkState.MsgCnt < 48) { // 48 retries = 24 seconds CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMHello; message.u.Hosts[0].SetName(Parameters::Instance.LocalPlayerName.c_str()); NetworkSendRateLimitedClientMessage(message, tick, 500); @@ -386,7 +386,7 @@ bool CClient::Update_connected(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMWaiting; NetworkSendRateLimitedClientMessage(message, tick, 650); return true; @@ -407,7 +407,7 @@ bool CClient::Update_synced(unsigned long tick) return Update(tick); } CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMWaiting; NetworkSendRateLimitedClientMessage(message, tick, 850); return true; @@ -420,7 +420,7 @@ bool CClient::Update_changed(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMState; message.u.State = LocalSetupState; message.MapUID = Map.Info.MapUID; @@ -440,7 +440,7 @@ bool CClient::Update_async(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMResync; NetworkSendRateLimitedClientMessage(message, tick, 450); return true; @@ -458,7 +458,7 @@ bool CClient::Update_mapinfo(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMMap; // ICMMapAck.. message.MapUID = Map.Info.MapUID; NetworkSendRateLimitedClientMessage(message, tick, 650); @@ -477,7 +477,7 @@ bool CClient::Update_badmap(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMMapUidMismatch; message.MapUID = Map.Info.MapUID; // MAP Uid doesn't match NetworkSendRateLimitedClientMessage(message, tick, 650); @@ -496,7 +496,7 @@ bool CClient::Update_goahead(unsigned long tick) if (networkState.MsgCnt < 50) { // 50 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMConfig; NetworkSendRateLimitedClientMessage(message, tick, 250); return true; @@ -514,7 +514,7 @@ bool CClient::Update_started(unsigned long tick) if (networkState.MsgCnt < 20) { // 20 retries CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMGo; NetworkSendRateLimitedClientMessage(message, tick, 250); return true; @@ -591,7 +591,7 @@ void CClient::ParseMsgConfig(const CInitMessage &msg, unsigned long host, int po bool CClient::NetworkParseMenuPacket(const CInitMessage &msg, unsigned long host, int port) { - if (msg.Type != MessageInitReply) { + if (msg.Type != MessageInit_FromServer) { return true; } if (msg.SubType == ICMServerQuit) { // Server user canceled, should work in all states @@ -930,7 +930,7 @@ void CClient::ParseAreYouThere() { CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMIAH; NetworkSendICMessage(serverIP, serverPort, message); } @@ -942,7 +942,7 @@ bool CClient::ParseBadMap() { CInitMessage message; - message.Type = MessageInitHello; + message.Type = MessageInit_FromClient; message.SubType = ICMSeeYou; // Spew out 5 and trust in God that they arrive for (int i = 0; i < 5; ++i) { @@ -1000,7 +1000,7 @@ void CServer::Update(unsigned long frameCounter) if (fcd > CLIENT_IS_DEAD) { KickDeadClient(i); } else if (fcd % 5 == 0) { - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMAYT; // Probe for the client message.MapUID = 0L; const int n = NetworkSendICMessage(Hosts[i].Host, Hosts[i].Port, message); @@ -1058,7 +1058,7 @@ int CServer::ParseHello(int h, const CInitMessage &msg, unsigned long host, int } else { CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMGameFull; // Game is full - reject connnection NetworkSendICMessage_Log(host, port, message); return -1; @@ -1067,7 +1067,7 @@ int CServer::ParseHello(int h, const CInitMessage &msg, unsigned long host, int // this code path happens until client sends waiting (= has received this message) CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMWelcome; // Acknowledge: Client is welcome message.u.Hosts[0].PlyNr = h; // Host array slot number message.u.Hosts[0].SetName(Parameters::Instance.LocalPlayerName.c_str()); // Name of server player @@ -1114,7 +1114,7 @@ void CServer::ParseResync(const int h, unsigned long host, int port) // this code path happens until client falls back to ICMWaiting // (indicating Resync has completed) - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMResync; for (int i = 1; i < PlayerMax - 1; ++i) { // Info about other clients if (i != h) { @@ -1160,7 +1160,7 @@ void CServer::ParseWaiting(const int h, unsigned long host, int port) case ccs_connected: { // this code path happens until client acknowledges the map CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMMap; // Send Map info to the client strncpy_s(message.u.MapPath, sizeof(message.u.MapPath), NetworkMapName.c_str(), NetworkMapName.size()); message.MapUID = Map.Info.MapUID; @@ -1194,7 +1194,7 @@ void CServer::ParseWaiting(const int h, unsigned long host, int port) // this code path happens until client acknoledges the state change // by sending ICMResync CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMState; // Send new state info to the client message.u.State = ServerSetupState; message.MapUID = Map.Info.MapUID; @@ -1231,7 +1231,7 @@ void CServer::ParseMap(const int h, unsigned long host, int port) // this code path happens until client acknoledges the state info // by falling back to ICMWaiting with prev. State synced CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMState; // Send State info to the client message.u.State = ServerSetupState; message.MapUID = Map.Info.MapUID; @@ -1284,7 +1284,7 @@ void CServer::ParseState(const int h, const CInitMessage &msg, unsigned long hos // by sending ICMResync CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMState; // Send new state info to the client message.u.State = ServerSetupState; message.MapUID = Map.Info.MapUID; @@ -1322,7 +1322,7 @@ void CServer::ParseGoodBye(const int h, unsigned long host, int port) // this code path happens until client acknoledges the GoodBye // by sending ICMSeeYou; CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMGoodBye; NetworkSendICMessage_Log(host, port, message); @@ -1373,7 +1373,7 @@ static int CheckVersions(const CInitMessage &msg, unsigned long host, int port) NIPQUAD(ntohl(host)), ntohs(port)); CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMEngineMismatch; // Stratagus engine version doesn't match NetworkSendICMessage_Log(host, port, message); return -1; @@ -1389,7 +1389,7 @@ static int CheckVersions(const CInitMessage &msg, unsigned long host, int port) NIPQUAD(ntohl(host)), ntohs(port)); CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMProtocolMismatch; // Network protocol version doesn't match NetworkSendICMessage_Log(host, port, message); return -1; @@ -1459,8 +1459,9 @@ int NetworkParseSetupEvent(const unsigned char *buf, int size, unsigned long hos { Assert(NetConnectRunning != 0); - if (size != (int)CInitMessage::Size()) { - // FIXME: could be a bad packet + const char msgtype = *buf; + if ((msgtype == MessageInit_FromClient && NetConnectRunning != 1) + || (msgtype == MessageInit_FromServer && NetConnectRunning != 2)) { if (NetConnectRunning == 2 && Client.GetNetworkState() == ccs_started) { // Client has acked ready to start and receives first real network packet. // This indicates that we missed the 'Go' in started state and the game @@ -1472,19 +1473,10 @@ int NetworkParseSetupEvent(const unsigned char *buf, int size, unsigned long hos CInitMessage msg; msg.Deserialize(buf); - if (msg.Type > MessageInitConfig) { - if (NetConnectRunning == 2 && Client.GetNetworkState() == ccs_started) { - // Client has acked ready to start and receives first real network packet. - // This indicates that we missed the 'Go' in started state and the game - // has been started by the server, so do the same for the client. - NetConnectRunning = 0; // End the menu.. - } - return 0; - } - DebugPrint("Received %s Init Message %d:%d from %d.%d.%d.%d:%d (%ld)\n" _C_ - icmsgsubtypenames[msg.SubType] _C_ msg.Type _C_ msg.SubType _C_ NIPQUAD(ntohl(host)) _C_ - ntohs(port) _C_ FrameCounter); + DebugPrint("Received %s (%d) from %d.%d.%d.%d:%d\n" _C_ + icmsgsubtypenames[msg.SubType] _C_ msg.SubType _C_ + NIPQUAD(ntohl(host)) _C_ ntohs(port)); if (NetConnectRunning == 2) { // client if (Client.NetworkParseMenuPacket(msg, host, port) == false) { @@ -1696,7 +1688,7 @@ void NetworkServerStartGame() // Prepare the final config message: CInitMessage message; - message.Type = MessageInitReply; + message.Type = MessageInit_FromServer; message.SubType = ICMConfig; message.HostsCount = NetPlayers; message.MapUID = Map.Info.MapUID; @@ -1707,7 +1699,7 @@ void NetworkServerStartGame() // Prepare the final state message: CInitMessage statemsg; - statemsg.Type = MessageInitReply; + statemsg.Type = MessageInit_FromServer; statemsg.SubType = ICMState; statemsg.HostsCount = NetPlayers; statemsg.u.State = ServerSetupState; @@ -1753,7 +1745,7 @@ breakout: CInitMessage msg; msg.Deserialize(buf); - if (msg.Type == MessageInitHello) { + if (msg.Type == MessageInit_FromClient) { switch (msg.SubType) { case ICMConfig: { DebugPrint("Got ack for InitConfig from %d.%d.%d.%d:%d\n" diff --git a/src/network/network.cpp b/src/network/network.cpp index b86796921..d936186dd 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -259,10 +259,10 @@ public: */ struct NetworkSelectionHeader { + unsigned char Type[MaxNetworkCommands]; /// Command unsigned NumberSent : 6; /// New Number Selected unsigned Add : 1; /// Adding to Selection unsigned Remove : 1; /// Removing from Selection - unsigned char Type[MaxNetworkCommands]; /// Command }; //---------------------------------------------------------------------------- @@ -317,7 +317,8 @@ static int NumNCQs; /// Number of NCQs in use */ static void NetworkBroadcast(const CNetworkPacket &packet, int numcommands) { - unsigned char *buf = packet.Serialize(numcommands); + unsigned char *buf = new unsigned char[CNetworkPacket::Size(numcommands)]; + packet.Serialize(buf, numcommands); // Send to all clients. for (int i = 0; i < HostsCount; ++i) { @@ -667,7 +668,8 @@ void NetworkSendSelection(CUnit **units, int count) // Send the Constructed packet to team members const int numcommands = (nosent + 3) / 4; - const unsigned char *buf = packet.Serialize(numcommands); + unsigned char *buf = new unsigned char [CNetworkPacket::Size(numcommands)]; + packet.Serialize(buf, numcommands); const int len = CNetworkPacketHeader::Size() + CNetworkSelection::Size() * numcommands; for (int i = 0; i < numteammates; ++i) {