diff options
Diffstat (limited to 'src/engine/server/es_register.cpp')
| -rw-r--r-- | src/engine/server/es_register.cpp | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/engine/server/es_register.cpp b/src/engine/server/es_register.cpp new file mode 100644 index 00000000..0eb5dba5 --- /dev/null +++ b/src/engine/server/es_register.cpp @@ -0,0 +1,271 @@ +#include <string.h> +#include <base/system.h> +#include <engine/e_network.h> +#include <engine/e_config.h> +#include <engine/e_engine.h> + +#include <mastersrv/mastersrv.h> + +extern CNetServer *m_pNetServer; + +enum +{ + REGISTERSTATE_START=0, + REGISTERSTATE_UPDATE_ADDRS, + REGISTERSTATE_QUERY_COUNT, + REGISTERSTATE_HEARTBEAT, + REGISTERSTATE_REGISTERED, + REGISTERSTATE_ERROR +}; + +static int register_state = REGISTERSTATE_START; +static int64 register_state_start = 0; +static int register_first = 1; +static int register_count = 0; + +static void register_new_state(int state) +{ + register_state = state; + register_state_start = time_get(); +} + +static void register_send_fwcheckresponse(NETADDR *pAddr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); + Packet.m_pData = SERVERBROWSE_FWRESPONSE; + m_pNetServer->Send(&Packet); +} + +static void register_send_heartbeat(NETADDR addr) +{ + static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + unsigned short port = config.sv_port; + CNetChunk Packet; + + mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + Packet.m_ClientID = -1; + Packet.m_Address = addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + Packet.m_pData = &data; + + /* supply the set port that the master can use if it has problems */ + if(config.sv_external_port) + port = config.sv_external_port; + data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; + data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; + m_pNetServer->Send(&Packet); +} + +static void register_send_count_request(NETADDR Addr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); + Packet.m_pData = SERVERBROWSE_GETCOUNT; + m_pNetServer->Send(&Packet); +} + +typedef struct +{ + NETADDR addr; + int count; + int valid; + int64 last_send; +} MASTERSERVER_INFO; + +static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}}; +static int register_registered_server = -1; + +void register_update() +{ + int64 now = time_get(); + int64 freq = time_freq(); + + if(!config.sv_register) + return; + + mastersrv_update(); + + if(register_state == REGISTERSTATE_START) + { + register_count = 0; + register_first = 1; + register_new_state(REGISTERSTATE_UPDATE_ADDRS); + mastersrv_refresh_addresses(); + dbg_msg("register", "refreshing ip addresses"); + } + else if(register_state == REGISTERSTATE_UPDATE_ADDRS) + { + register_registered_server = -1; + + if(!mastersrv_refreshing()) + { + int i; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + NETADDR addr = mastersrv_get(i); + masterserver_info[i].addr = addr; + masterserver_info[i].count = 0; + + if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) + masterserver_info[i].valid = 0; + else + { + masterserver_info[i].valid = 1; + masterserver_info[i].count = -1; + masterserver_info[i].last_send = 0; + } + } + + dbg_msg("register", "fetching server counts"); + register_new_state(REGISTERSTATE_QUERY_COUNT); + } + } + else if(register_state == REGISTERSTATE_QUERY_COUNT) + { + int i; + int left = 0; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + if(!masterserver_info[i].valid) + continue; + + if(masterserver_info[i].count == -1) + { + left++; + if(masterserver_info[i].last_send+freq < now) + { + masterserver_info[i].last_send = now; + register_send_count_request(masterserver_info[i].addr); + } + } + } + + /* check if we are done or timed out */ + if(left == 0 || now > register_state_start+freq*3) + { + /* choose server */ + int best = -1; + int i; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + if(!masterserver_info[i].valid || masterserver_info[i].count == -1) + continue; + + if(best == -1 || masterserver_info[i].count < masterserver_info[best].count) + best = i; + } + + /* server chosen */ + register_registered_server = best; + if(register_registered_server == -1) + { + dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds"); + register_new_state(REGISTERSTATE_ERROR); + } + else + { + dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server)); + masterserver_info[register_registered_server].last_send = 0; + register_new_state(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(register_state == REGISTERSTATE_HEARTBEAT) + { + /* check if we should send heartbeat */ + if(now > masterserver_info[register_registered_server].last_send+freq*15) + { + masterserver_info[register_registered_server].last_send = now; + register_send_heartbeat(masterserver_info[register_registered_server].addr); + } + + if(now > register_state_start+freq*60) + { + dbg_msg("register", "WARNING: Master server is not responding, switching master"); + register_new_state(REGISTERSTATE_START); + } + } + else if(register_state == REGISTERSTATE_REGISTERED) + { + if(register_first) + dbg_msg("register", "server registered"); + + register_first = 0; + + /* check if we should send new heartbeat again */ + if(now > register_state_start+freq) + { + if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */ + register_new_state(REGISTERSTATE_START); + else + { + register_count++; + register_new_state(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(register_state == REGISTERSTATE_ERROR) + { + /* check for restart */ + if(now > register_state_start+freq*60) + register_new_state(REGISTERSTATE_START); + } +} + +static void register_got_count(CNetChunk *pChunk) +{ + unsigned char *pData = (unsigned char *)pChunk->m_pData; + int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1]; + + for(int i = 0; i < MAX_MASTERSERVERS; i++) + { + if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0) + { + masterserver_info[i].count = Count; + break; + } + } +} + +int register_process_packet(CNetChunk *pPacket) +{ + if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + register_send_fwcheckresponse(&pPacket->m_Address); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + { + if(register_first) + dbg_msg("register", "no firewall/nat problems detected"); + register_new_state(REGISTERSTATE_REGISTERED); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) + { + dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); + dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port); + register_new_state(REGISTERSTATE_ERROR); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 && + memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) + { + register_got_count(pPacket); + return 1; + } + + return 0; +} |