From c2e5771a15c35987b9e88057d579cfaeb0c36f8b Mon Sep 17 00:00:00 2001 From: oy Date: Sun, 4 Mar 2012 12:46:32 +0100 Subject: [PATCH] improved map download. Closes #712 --- src/base/system.c | 24 +++++++----- src/engine/client/client.cpp | 54 ++++++++++++--------------- src/engine/client/client.h | 2 + src/engine/server/server.cpp | 56 +++++++++++++++------------- src/engine/server/server.h | 7 ++++ src/engine/shared/config_variables.h | 1 + src/engine/shared/network.cpp | 31 ++++++++------- src/engine/shared/network.h | 4 +- src/engine/shared/network_client.cpp | 18 ++++++--- src/engine/shared/network_server.cpp | 18 ++++++--- 10 files changed, 122 insertions(+), 93 deletions(-) diff --git a/src/base/system.c b/src/base/system.c index ed0f41ec45..d357a7c29a 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -903,6 +903,7 @@ NETSOCKET net_udp_create(NETADDR bindaddr) NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; int broadcast = 1; + int recvsize = 65536; if(bindaddr.type&NETTYPE_IPV4) { @@ -917,13 +918,13 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { sock.type |= NETTYPE_IPV4; sock.ipv4sock = socket; - } - /* set non-blocking */ - net_set_non_blocking(sock); + /* set broadcast */ + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - /* set boardcast */ - setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + /* set receive buffer size */ + setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); + } } if(bindaddr.type&NETTYPE_IPV6) @@ -939,15 +940,18 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { sock.type |= NETTYPE_IPV6; sock.ipv6sock = socket; - } - /* set non-blocking */ - net_set_non_blocking(sock); + /* set broadcast */ + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - /* set boardcast */ - setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + /* set receive buffer size */ + setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); + } } + /* set non-blocking */ + net_set_non_blocking(sock); + /* return */ return sock; } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index d5da647bfe..f4e1f7c029 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1028,6 +1028,8 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) const char *pMap = Unpacker.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES); int MapCrc = Unpacker.GetInt(); int MapSize = Unpacker.GetInt(); + int MapChunkNum = Unpacker.GetInt(); + int MapChunkSize = Unpacker.GetInt(); const char *pError = 0; if(Unpacker.Error()) @@ -1037,13 +1039,14 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(!m_MapChecker.IsMapValid(pMap, MapCrc, MapSize)) pError = "invalid standard map"; - for(int i = 0; pMap[i]; i++) // protect the player from nasty map names + // protect the player from nasty map names + for(int i = 0; pMap[i]; i++) { if(pMap[i] == '/' || pMap[i] == '\\') pError = "strange character in map name"; } - if(MapSize < 0) + if(MapSize <= 0) pError = "invalid map size"; if(pError) @@ -1059,52 +1062,50 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) } else { + // start map download str_format(m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename), "downloadedmaps/%s_%08x.map", pMap, MapCrc); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "starting to download map to '%s'", m_aMapdownloadFilename); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", aBuf); - m_MapdownloadChunk = 0; str_copy(m_aMapdownloadName, pMap, sizeof(m_aMapdownloadName)); if(m_MapdownloadFile) io_close(m_MapdownloadFile); m_MapdownloadFile = Storage()->OpenFile(m_aMapdownloadFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); + m_MapdownloadChunk = 0; + m_MapdownloadChunkNum = MapChunkNum; + m_MapDownloadChunkSize = MapChunkSize; m_MapdownloadCrc = MapCrc; m_MapdownloadTotalsize = MapSize; m_MapdownloadAmount = 0; + // request first chunk package of map data CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA); - Msg.AddInt(m_MapdownloadChunk); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); if(g_Config.m_Debug) - { - str_format(aBuf, sizeof(aBuf), "requested chunk %d", m_MapdownloadChunk); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client/network", aBuf); - } + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client/network", "requested first chunk package"); } } } else if(Msg == NETMSG_MAP_DATA) { - int Last = Unpacker.GetInt(); - int MapCRC = Unpacker.GetInt(); - int Chunk = Unpacker.GetInt(); - int Size = Unpacker.GetInt(); - const unsigned char *pData = Unpacker.GetRaw(Size); - - // check fior errors - if(Unpacker.Error() || Size <= 0 || MapCRC != m_MapdownloadCrc || Chunk != m_MapdownloadChunk || !m_MapdownloadFile) + if(!m_MapdownloadFile) return; + int Size = min(m_MapDownloadChunkSize, m_MapdownloadTotalsize-m_MapdownloadAmount); + const unsigned char *pData = Unpacker.GetRaw(Size); + if(Unpacker.Error()) + return; + io_write(m_MapdownloadFile, pData, Size); - + ++m_MapdownloadChunk; m_MapdownloadAmount += Size; - if(Last) + if(m_MapdownloadAmount == m_MapdownloadTotalsize) { - const char *pError; + // map download complete m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "download complete, loading map"); if(m_MapdownloadFile) @@ -1114,7 +1115,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) m_MapdownloadTotalsize = -1; // load map - pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadCrc); + const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadCrc); if(!pError) { m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done"); @@ -1123,21 +1124,14 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) else DisconnectWithReason(pError); } - else + else if(m_MapdownloadChunk%m_MapdownloadChunkNum == 0) { - // request new chunk - m_MapdownloadChunk++; - + // request next chunk package of map data CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA); - Msg.AddInt(m_MapdownloadChunk); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); if(g_Config.m_Debug) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "requested chunk %d", m_MapdownloadChunk); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client/network", aBuf); - } + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client/network", "requested next chunk package"); } } else if(Msg == NETMSG_CON_READY) diff --git a/src/engine/client/client.h b/src/engine/client/client.h index d958b49aec..16fd8ab2c7 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -121,6 +121,8 @@ class CClient : public IClient, public CDemoPlayer::IListner char m_aMapdownloadName[256]; IOHANDLE m_MapdownloadFile; int m_MapdownloadChunk; + int m_MapdownloadChunkNum; + int m_MapDownloadChunkSize; int m_MapdownloadCrc; int m_MapdownloadAmount; int m_MapdownloadTotalsize; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index a231d1e8d7..5e88180cba 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -288,6 +288,7 @@ void CServer::CClient::Reset() m_LastInputTick = -1; m_SnapRate = CClient::SNAPRATE_INIT; m_Score = 0; + m_MapChunk = 0; } CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) @@ -745,6 +746,8 @@ void CServer::SendMap(int ClientID) Msg.AddString(GetMapName(), 0); Msg.AddInt(m_CurrentMapCrc); Msg.AddInt(m_CurrentMapSize); + Msg.AddInt(m_MapChunksPerRequest); + Msg.AddInt(MAP_CHUNK_SIZE); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true); } @@ -855,36 +858,36 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } else if(Msg == NETMSG_REQUEST_MAP_DATA) { - int Chunk = Unpacker.GetInt(); - int ChunkSize = 1024-128; - int Offset = Chunk * ChunkSize; - int Last = 0; + if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING) + { + int ChunkSize = MAP_CHUNK_SIZE; - // drop faulty map data requests - if(Chunk < 0 || Offset > m_CurrentMapSize) - return; + // send map chunks + for(int i = 0; i < m_MapChunksPerRequest && m_aClients[ClientID].m_MapChunk >= 0; ++i) + { + int Chunk = m_aClients[ClientID].m_MapChunk; + int Offset = Chunk * ChunkSize; - if(Offset+ChunkSize >= m_CurrentMapSize) - { - ChunkSize = m_CurrentMapSize-Offset; - if(ChunkSize < 0) - ChunkSize = 0; - Last = 1; - } + // check for last part + if(Offset+ChunkSize >= m_CurrentMapSize) + { + ChunkSize = m_CurrentMapSize-Offset; + m_aClients[ClientID].m_MapChunk = -1; + } + else + m_aClients[ClientID].m_MapChunk++; - CMsgPacker Msg(NETMSG_MAP_DATA); - Msg.AddInt(Last); - Msg.AddInt(m_CurrentMapCrc); - Msg.AddInt(Chunk); - Msg.AddInt(ChunkSize); - Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); - SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true); + CMsgPacker Msg(NETMSG_MAP_DATA); + Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true); - if(g_Config.m_Debug) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize); - Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); + if(g_Config.m_Debug) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); + } + } } } else if(Msg == NETMSG_READY) @@ -1268,6 +1271,7 @@ int CServer::Run() dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap); return -1; } + m_MapChunksPerRequest = g_Config.m_SvMapDownloadSpeed; // start server NETADDR BindAddr; diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 696b472da7..9803a93941 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -122,6 +122,7 @@ class CServer : public IServer int m_Authed; int m_AuthTries; + int m_MapChunk; const IConsole::CCommandInfo *m_pRconCmdToSend; void Reset(); @@ -149,10 +150,16 @@ class CServer : public IServer int64 m_Lastheartbeat; //static NETADDR4 master_server; + // map + enum + { + MAP_CHUNK_SIZE=NET_MAX_PAYLOAD-NET_MAX_CHUNKHEADERSIZE-4, // msg type + }; char m_aCurrentMap[64]; unsigned m_CurrentMapCrc; unsigned char *m_pCurrentMapData; int m_CurrentMapSize; + int m_MapChunksPerRequest; CDemoRecorder m_DemoRecorder; CRegister m_Register; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index ac913162a4..22b0baf602 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -84,6 +84,7 @@ MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "Ext MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server") MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server") MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server") +MACRO_CONFIG_INT(SvMapDownloadSpeed, sv_map_download_speed, 1, 2, 16, CFGFLAG_SERVER, "Number of map data packages a client gets on each request") MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only") MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing") MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)") diff --git a/src/engine/shared/network.cpp b/src/engine/shared/network.cpp index ada4b18abf..be719e6e1f 100644 --- a/src/engine/shared/network.cpp +++ b/src/engine/shared/network.cpp @@ -161,7 +161,7 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct // check the size if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE) { - dbg_msg("", "packet too small, %d", Size); + dbg_msg("network", "packet too small, %d", Size); return -1; } @@ -177,15 +177,11 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct // read the packet pPacket->m_Flags = pBuffer[0]>>4; - pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1]; - pPacket->m_NumChunks = pBuffer[2]; - pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE; - if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS) { if(Size < 6) { - dbg_msg("", "connection less packet too small, %d", Size); + dbg_msg("network", "connection less packet too small, %d", Size); return -1; } @@ -197,6 +193,15 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct } else { + if(Size-NET_PACKETHEADERSIZE > NET_MAX_PAYLOAD) + { + dbg_msg("network", "packet too big, %d", Size); + return -1; + } + + pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1]; + pPacket->m_NumChunks = pBuffer[2]; + pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE; if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); else @@ -244,12 +249,12 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con unsigned char *CNetChunkHeader::Pack(unsigned char *pData) { - pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f); - pData[1] = (m_Size&0xf); + pData[0] = ((m_Flags&0x03)<<6) | ((m_Size>>6)&0x3F); + pData[1] = (m_Size&0x3F); if(m_Flags&NET_CHUNKFLAG_VITAL) { - pData[1] |= (m_Sequence>>2)&0xf0; - pData[2] = m_Sequence&0xff; + pData[1] |= (m_Sequence>>2)&0xC0; + pData[2] = m_Sequence&0xFF; return pData + 3; } return pData + 2; @@ -257,12 +262,12 @@ unsigned char *CNetChunkHeader::Pack(unsigned char *pData) unsigned char *CNetChunkHeader::Unpack(unsigned char *pData) { - m_Flags = (pData[0]>>6)&3; - m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf); + m_Flags = (pData[0]>>6)&0x03; + m_Size = ((pData[0]&0x3F)<<6) | (pData[1]&0x3F); m_Sequence = -1; if(m_Flags&NET_CHUNKFLAG_VITAL) { - m_Sequence = ((pData[1]&0xf0)<<2) | pData[2]; + m_Sequence = ((pData[1]&0xC0)<<2) | pData[2]; return pData + 3; } return pData + 2; diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index dd43389ecb..562fa8d3d2 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -20,7 +20,7 @@ chunk header: 2-3 bytes unsigned char flags_size; // 2bit flags, 6 bit size - unsigned char size_seq; // 4bit size, 4bit seq + unsigned char size_seq; // 6bit size, 2bit seq (unsigned char seq;) // 8bit seq, if vital flag is set */ @@ -46,7 +46,7 @@ enum NET_MAX_PACKETSIZE = 1400, NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE-6, - NET_MAX_CHUNKHEADERSIZE = 5, + NET_MAX_CHUNKHEADERSIZE = 3, NET_PACKETHEADERSIZE = 3, NET_MAX_CLIENTS = 16, NET_MAX_CONSOLE_CLIENTS = 4, diff --git a/src/engine/shared/network_client.cpp b/src/engine/shared/network_client.cpp index 2c0356061b..5b665a4cf2 100644 --- a/src/engine/shared/network_client.cpp +++ b/src/engine/shared/network_client.cpp @@ -93,19 +93,25 @@ int CNetClient::Recv(CNetChunk *pChunk) int CNetClient::Send(CNetChunk *pChunk) { - if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) - { - dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); - return -1; - } - if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) { + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize); + return -1; + } + // send connectionless packet CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else { + if(pChunk->m_DataSize+NET_MAX_CHUNKHEADERSIZE >= NET_MAX_PAYLOAD) + { + dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); + return -1; + } + int Flags = 0; dbg_assert(pChunk->m_ClientID == 0, "errornous client id"); diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp index 1264a4a5aa..9b4725edd9 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -205,19 +205,25 @@ int CNetServer::Recv(CNetChunk *pChunk) int CNetServer::Send(CNetChunk *pChunk) { - if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) - { - dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize); - return -1; - } - if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) { + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize); + return -1; + } + // send connectionless packet CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else { + if(pChunk->m_DataSize+NET_MAX_CHUNKHEADERSIZE >= NET_MAX_PAYLOAD) + { + dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); + return -1; + } + int Flags = 0; dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id");