improved map download. Closes #712

This commit is contained in:
oy 2012-03-04 12:46:32 +01:00
parent 42c845be49
commit c2e5771a15
10 changed files with 125 additions and 96 deletions

View file

@ -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 broadcast */
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);
/* set boardcast */
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
}
if(bindaddr.type&NETTYPE_IPV6)
@ -939,15 +940,18 @@ NETSOCKET net_udp_create(NETADDR bindaddr)
{
sock.type |= NETTYPE_IPV6;
sock.ipv6sock = socket;
/* set broadcast */
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);
/* set boardcast */
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
}
/* set non-blocking */
net_set_non_blocking(sock);
/* return */
return sock;
}

View file

@ -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)

View file

@ -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;

View file

@ -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;
// drop faulty map data requests
if(Chunk < 0 || Offset > m_CurrentMapSize)
return;
if(Offset+ChunkSize >= m_CurrentMapSize)
if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
{
ChunkSize = m_CurrentMapSize-Offset;
if(ChunkSize < 0)
ChunkSize = 0;
Last = 1;
}
int ChunkSize = MAP_CHUNK_SIZE;
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);
// 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(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);
// 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.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true);
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;

View file

@ -122,6 +122,7 @@ public:
int m_Authed;
int m_AuthTries;
int m_MapChunk;
const IConsole::CCommandInfo *m_pRconCmdToSend;
void Reset();
@ -149,10 +150,16 @@ public:
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;

View file

@ -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)")

View file

@ -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;

View file

@ -20,7 +20,7 @@ CURRENT:
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,

View file

@ -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");

View file

@ -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");