int CNetClient::Recv(CNetChunk *pChunk, TOKEN *pResponseToken) { while(1) { // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo NETADDR Addr; int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { if(m_Connection.State() != NET_CONNSTATE_OFFLINE && m_Connection.State() != NET_CONNSTATE_ERROR && net_addr_comp(m_Connection.PeerAddress(), &Addr) == 0) { if(m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) { if(!(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)) m_RecvUnpacker.Start(&Addr, &m_Connection, 0); } } else { int Accept = m_TokenManager.ProcessMessage(&Addr, &m_RecvUnpacker.m_Data, true); if(!Accept) continue; if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL) { if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_TOKEN) m_TokenCache.AddToken(&Addr, m_RecvUnpacker.m_Data.m_ResponseToken); } else if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; if(pResponseToken) *pResponseToken = m_RecvUnpacker.m_Data.m_ResponseToken; return 1; } } } } return 0; }
int CNetClient::Recv(CNetChunk *pChunk) { while(1) { // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo NETADDR Addr; unsigned char *pData; int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE, &m_MMSGS, &pData); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data) == 0) { if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_EXTENDED) { pChunk->m_Flags |= NETSENDFLAG_EXTENDED; mem_copy(pChunk->m_aExtraData, m_RecvUnpacker.m_Data.m_aExtraData, sizeof(pChunk->m_aExtraData)); } return 1; } else { if(m_Connection.State() != NET_CONNSTATE_OFFLINE && m_Connection.State() != NET_CONNSTATE_ERROR && net_addr_comp(m_Connection.PeerAddress(), &Addr) == 0 && m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) m_RecvUnpacker.Start(&Addr, &m_Connection, 0); } } } return 0; }
int CNetClient::Recv(CNetChunk *pChunk) { while(1) { // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo NETADDR Addr; int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; return 1; } else { if(m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) m_RecvUnpacker.Start(&Addr, &m_Connection, 0); } } } return 0; }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk) { while(1) { NETADDR Addr; // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { // check if we just should drop the packet char aBuf[128]; if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { // banned, reply with a message CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); continue; } if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; return 1; } else { // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { bool Found = false; // check if we already got this client for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { Found = true; // silent ignore.. we got this client already break; } } // client that wants to connect if(!Found) { // only allow a specific number of players with the same ip NETADDR ThisAddr = Addr, OtherAddr; int FoundAddr = 1; ThisAddr.port = 0; for(int i = 0; i < MaxClients(); ++i) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) continue; OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); OtherAddr.port = 0; if(!net_addr_comp(&ThisAddr, &OtherAddr)) { if(FoundAddr++ >= m_MaxClientsPerIP) { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf)); return 0; } } } for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { Found = true; m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); if(m_pfnNewClient) m_pfnNewClient(i, m_UserPtr); break; } } if(!Found) { const char FullMsg[] = "This server is full"; CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); } } } else { // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) { if(m_RecvUnpacker.m_Data.m_DataSize) m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); } } } } } } } return 0; }
/* TODO: chopp up this function into smaller working parts */ int netserver_recv(NETSERVER *s, NETCHUNK *chunk) { unsigned now = time_timestamp(); while(1) { NETADDR addr; int i, bytes, found; /* check for a chunk */ if(recvinfo_fetch_chunk(&s->recv, chunk)) return 1; /* TODO: empty the recvinfo */ bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE); /* no more packets for now */ if(bytes <= 0) break; if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0) { NETBAN *ban = 0; NETADDR banaddr = addr; int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; found = 0; banaddr.port = 0; /* search a ban */ for(ban = s->bans[iphash]; ban; ban = ban->hashnext) { if(net_addr_comp(&ban->info.addr, &banaddr) == 0) break; } /* check if we just should drop the packet */ if(ban) { // banned, reply with a message char banstr[128]; if(ban->info.expires) { int mins = ((ban->info.expires - now)+59)/60; if(mins == 1) str_format(banstr, sizeof(banstr), "banned for %d minute", mins); else str_format(banstr, sizeof(banstr), "banned for %d minutes", mins); } else str_format(banstr, sizeof(banstr), "banned for life"); send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1); continue; } if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) { chunk->flags = NETSENDFLAG_CONNLESS; chunk->client_id = -1; chunk->address = addr; chunk->data_size = s->recv.data.data_size; chunk->data = s->recv.data.chunk_data; return 1; } else { /* TODO: check size here */ if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) { found = 0; /* check if we already got this client */ for(i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) { found = 1; /* silent ignore.. we got this client already */ break; } } /* client that wants to connect */ if(!found) { for(i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) { found = 1; conn_feed(&s->slots[i].conn, &s->recv.data, &addr); if(s->new_client) s->new_client(i, s->user_ptr); break; } } if(!found) { const char fullmsg[] = "server is full"; send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg)); } } } else { /* normal packet, find matching slot */ for(i = 0; i < s->max_clients; i++) { if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) { if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr)) { if(s->recv.data.data_size) recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i); } } } } } } } return 0; }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk, TOKEN *pResponseToken) { while(1) { NETADDR Addr; // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { // check for bans char aBuf[128]; if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { // banned, reply with a message CNetBase::SendControlMsg(m_Socket, &Addr, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); continue; } bool Found = false; // try to find matching slot for(int i = 0; i < MaxClients(); i++) { if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) { if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) { if(m_RecvUnpacker.m_Data.m_DataSize) { if(!(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)) m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); else { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_Address = *m_aSlots[i].m_Connection.PeerAddress(); pChunk->m_ClientID = i; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; if(pResponseToken) *pResponseToken = NET_TOKEN_NONE; return 1; } } } Found = true; } } if(Found) continue; int Accept = m_TokenManager.ProcessMessage(&Addr, &m_RecvUnpacker.m_Data); if(Accept <= 0) continue; if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL) { if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { bool Found = false; // only allow a specific number of players with the same ip NETADDR ThisAddr = Addr, OtherAddr; int FoundAddr = 1; ThisAddr.port = 0; for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) continue; OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); OtherAddr.port = 0; if(!net_addr_comp(&ThisAddr, &OtherAddr)) { if(FoundAddr++ >= m_MaxClientsPerIP) { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); CNetBase::SendControlMsg(m_Socket, &Addr, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1); return 0; } } } for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { Found = true; m_aSlots[i].m_Connection.SetToken(m_RecvUnpacker.m_Data.m_Token); m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); if(m_pfnNewClient) m_pfnNewClient(i, m_UserPtr); break; } } if(!Found) { const char FullMsg[] = "This server is full"; CNetBase::SendControlMsg(m_Socket, &Addr, m_RecvUnpacker.m_Data.m_ResponseToken, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); } } else if(m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_TOKEN) m_TokenCache.AddToken(&Addr, m_RecvUnpacker.m_Data.m_ResponseToken, NET_TOKENFLAG_RESPONSEONLY); } else if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; if(pResponseToken) *pResponseToken = m_RecvUnpacker.m_Data.m_ResponseToken; return 1; } } } return 0; }
void Run(int Port, NETADDR Dest) { NETADDR Src = {NETTYPE_IPV4, {0,0,0,0}, Port}; NETSOCKET Socket = net_udp_create(Src); char aBuffer[1024*2]; int ID = 0; int Delaycounter = 0; while(1) { static int Lastcfg = 0; int n = ((time_get()/time_freq())/m_ConfigInterval) % m_ConfigNumpingconfs; CPingConfig Ping = m_aConfigPings[n]; if(n != Lastcfg) dbg_msg("crapnet", "cfg = %d", n); Lastcfg = n; // handle incomming packets while(1) { // fetch data int DataTrash = 0; NETADDR From; int Bytes = net_udp_recv(Socket, &From, aBuffer, 1024*2); if(Bytes <= 0) break; if((rand()%100) < Ping.m_Loss) // drop the packet { if(m_ConfigLog) dbg_msg("crapnet", "dropped packet"); continue; } // create new packet CPacket *p = (CPacket *)mem_alloc(sizeof(CPacket)+Bytes, 1); if(net_addr_comp(&From, &Dest) == 0) p->m_SendTo = Src; // from the server else { Src = From; // from the client p->m_SendTo = Dest; } // queue packet p->m_pPrev = m_pLast; p->m_pNext = 0; if(m_pLast) m_pLast->m_pNext = p; else { m_pFirst = p; m_pLast = p; } m_pLast = p; // set data in packet p->m_Timestamp = time_get(); p->m_DataSize = Bytes; p->m_ID = ID++; mem_copy(p->m_aData, aBuffer, Bytes); if(ID > 20 && Bytes > 6 && DataTrash) { p->m_aData[6+(rand()%(Bytes-6))] = rand()&255; // modify a byte if((rand()%10) == 0) { p->m_DataSize -= rand()%32; if(p->m_DataSize < 6) p->m_DataSize = 6; } } if(Delaycounter <= 0) { if(Ping.m_Delay) p->m_Timestamp += (time_freq()*1000)/Ping.m_Delay; Delaycounter = Ping.m_DelayFreq; } Delaycounter--; if(m_ConfigLog) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&From, aAddrStr, sizeof(aAddrStr), true); dbg_msg("crapnet", "<< %08d %s (%d)", p->m_ID, aAddrStr, p->m_DataSize); } } // /*while(1) {*/ CPacket *p = 0; CPacket *pNext = m_pFirst; while(1) { p = pNext; if(!p) break; pNext = p->m_pNext; if((time_get()-p->m_Timestamp) > m_CurrentLatency) { char aFlags[] = " "; if(m_ConfigReorder && (rand()%2) == 0 && p->m_pNext) { aFlags[0] = 'R'; p = m_pFirst->m_pNext; } if(p->m_pNext) p->m_pNext->m_pPrev = p->m_pPrev; else m_pLast = p->m_pPrev; if(p->m_pPrev) p->m_pPrev->m_pNext = p->m_pNext; else m_pFirst = p->m_pNext; /*CPacket *cur = first; while(cur) { dbg_assert(cur != p, "p still in list"); cur = cur->next; }*/ // send and remove packet //if((rand()%20) != 0) // heavy packetloss net_udp_send(Socket, &p->m_SendTo, p->m_aData, p->m_DataSize); // update lag double Flux = rand()/(double)RAND_MAX; int MsSpike = Ping.m_Spike; int MsFlux = Ping.m_Flux; int MsPing = Ping.m_Base; m_CurrentLatency = ((time_freq()*MsPing)/1000) + (int64)(((time_freq()*MsFlux)/1000)*Flux); // 50ms if(MsSpike && (p->m_ID%100) == 0) { m_CurrentLatency += (time_freq()*MsSpike)/1000; aFlags[1] = 'S'; } if(m_ConfigLog) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(&p->m_SendTo, aAddrStr, sizeof(aAddrStr), true); dbg_msg("crapnet", ">> %08d %s (%d) %s", p->m_ID, aAddrStr, p->m_DataSize, aFlags); } mem_free(p); } } thread_sleep(1); } }
/* TODO: chopp up this function into smaller working parts */ int CNetServer::Recv(CNetChunk *pChunk) { unsigned Now = time_timestamp(); while(1) { NETADDR Addr; // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); // no more packets for now if(Bytes <= 0) break; if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) { CBan *pBan = 0; NETADDR BanAddr = Addr; int IpHash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3]+BanAddr.ip[4]+BanAddr.ip[5]+BanAddr.ip[6]+BanAddr.ip[7]+ BanAddr.ip[8]+BanAddr.ip[9]+BanAddr.ip[10]+BanAddr.ip[11]+BanAddr.ip[12]+BanAddr.ip[13]+BanAddr.ip[14]+BanAddr.ip[15])&0xff; int Found = 0; BanAddr.port = 0; // search a ban for(pBan = m_aBans[IpHash]; pBan; pBan = pBan->m_pHashNext) { if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) break; } // check if we just should drop the packet if(pBan) { // banned, reply with a message char BanStr[128]; if(pBan->m_Info.m_Expires > -1) { int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60; if(Mins <= 1) str_format(BanStr, sizeof(BanStr), "Banned for 1 minute (%s)", pBan->m_Info.m_Reason); else str_format(BanStr, sizeof(BanStr), "Banned for %d minutes (%s)", Mins, pBan->m_Info.m_Reason); } else str_format(BanStr, sizeof(BanStr), "Banned for life (%s)", pBan->m_Info.m_Reason); CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1); continue; } if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; pChunk->m_ClientID = -1; pChunk->m_Address = Addr; pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; return 1; } else { // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { Found = 0; // check if we already got this client for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && net_addr_comp(&PeerAddr, &Addr) == 0) { Found = 1; // silent ignore.. we got this client already break; } } // client that wants to connect if(!Found) { // only allow a specific number of players with the same ip NETADDR ThisAddr = Addr, OtherAddr; int FoundAddr = 1; ThisAddr.port = 0; for(int i = 0; i < MaxClients(); ++i) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) continue; OtherAddr = m_aSlots[i].m_Connection.PeerAddress(); OtherAddr.port = 0; if(!net_addr_comp(&ThisAddr, &OtherAddr)) { if(FoundAddr++ >= m_MaxClientsPerIP) { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf)); return 0; } } } for(int i = 0; i < MaxClients(); i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) { Found = 1; m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); if(m_pfnNewClient) m_pfnNewClient(i, m_UserPtr); break; } } if(!Found) { const char FullMsg[] = "This server is full"; CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); } } } else { // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(net_addr_comp(&PeerAddr, &Addr) == 0) { if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) { if(m_RecvUnpacker.m_Data.m_DataSize) m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); } } } } } } } return 0; }