diff options
Diffstat (limited to 'src/engine/client/ec_srvbrowse.cpp')
| -rw-r--r-- | src/engine/client/ec_srvbrowse.cpp | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/src/engine/client/ec_srvbrowse.cpp b/src/engine/client/ec_srvbrowse.cpp new file mode 100644 index 00000000..1b04937a --- /dev/null +++ b/src/engine/client/ec_srvbrowse.cpp @@ -0,0 +1,736 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <base/system.h> +#include <engine/e_network.h> +#include <engine/e_client_interface.h> +#include <engine/e_config.h> +#include <engine/e_memheap.h> +#include <engine/e_engine.h> + +#include <mastersrv/mastersrv.h> + +#include <string.h> +#include <stdlib.h> + +extern CNetClient m_NetClient; + + +/* ------ server browse ---- */ +/* TODO: move all this to a separate file */ + +typedef struct SERVERENTRY_t SERVERENTRY; +struct SERVERENTRY_t +{ + NETADDR addr; + int64 request_time; + int got_info; + SERVER_INFO info; + + SERVERENTRY *next_ip; /* ip hashed list */ + + SERVERENTRY *prev_req; /* request list */ + SERVERENTRY *next_req; +}; + +static HEAP *serverlist_heap = 0; +static SERVERENTRY **serverlist = 0; +static int *sorted_serverlist = 0; + +enum +{ + MAX_FAVORITES=256 +}; + +static NETADDR favorite_servers[MAX_FAVORITES]; +static int num_favorite_servers = 0; + +static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ + +static SERVERENTRY *first_req_server = 0; /* request list */ +static SERVERENTRY *last_req_server = 0; +static int num_requests = 0; + +static int need_refresh = 0; + +static int num_sorted_servers = 0; +static int num_sorted_servers_capacity = 0; +static int num_servers = 0; +static int num_server_capacity = 0; + +static int sorthash = 0; +static char filterstring[64] = {0}; +static char filtergametypestring[128] = {0}; + +/* the token is to keep server refresh separated from each other */ +static int current_token = 1; + +static int serverlist_type = 0; +static int64 broadcast_time = 0; + +int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; } +int client_serverbrowse_num() { return num_servers; } + +SERVER_INFO *client_serverbrowse_get(int index) +{ + if(index < 0 || index >= num_servers) + return 0; + return &serverlist[index]->info; +} + +int client_serverbrowse_sorted_num() { return num_sorted_servers; } + +SERVER_INFO *client_serverbrowse_sorted_get(int index) +{ + if(index < 0 || index >= num_sorted_servers) + return 0; + return &serverlist[sorted_serverlist[index]]->info; +} + + +int client_serverbrowse_num_requests() +{ + return num_requests; +} + +static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.name, b->info.name); +} + +static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.map, b->info.map); +} + +static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.latency > b->info.latency) return 1; + if(a->info.latency < b->info.latency) return -1; + return 0; +} + +static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.gametype, b->info.gametype); +} + +static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.progression > b->info.progression) return 1; + if(a->info.progression < b->info.progression) return -1; + return 0; +} + +static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.num_players > b->info.num_players) return 1; + if(a->info.num_players < b->info.num_players) return -1; + return 0; +} + +static void client_serverbrowse_filter() +{ + int i = 0, p = 0; + num_sorted_servers = 0; + + /* allocate the sorted list */ + if(num_sorted_servers_capacity < num_servers) + { + if(sorted_serverlist) + mem_free(sorted_serverlist); + num_sorted_servers_capacity = num_servers; + sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); + } + + /* filter the servers */ + for(i = 0; i < num_servers; i++) + { + int filtered = 0; + + if(config.b_filter_empty && serverlist[i]->info.num_players == 0) + filtered = 1; + else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) + filtered = 1; + else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD) + filtered = 1; + else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0)) + filtered = 1; + else if(config.b_filter_pure_map && + !(strcmp(serverlist[i]->info.map, "dm1") == 0 || + strcmp(serverlist[i]->info.map, "dm2") == 0 || + strcmp(serverlist[i]->info.map, "dm6") == 0 || + strcmp(serverlist[i]->info.map, "dm7") == 0 || + strcmp(serverlist[i]->info.map, "dm8") == 0 || + strcmp(serverlist[i]->info.map, "dm9") == 0 || + strcmp(serverlist[i]->info.map, "ctf1") == 0 || + strcmp(serverlist[i]->info.map, "ctf2") == 0 || + strcmp(serverlist[i]->info.map, "ctf3") == 0 || + strcmp(serverlist[i]->info.map, "ctf4") == 0 || + strcmp(serverlist[i]->info.map, "ctf5") == 0) + ) + { + filtered = 1; + } + else if(config.b_filter_ping < serverlist[i]->info.latency) + filtered = 1; + else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0) + filtered = 1; + else + { + if(config.b_filter_string[0] != 0) + { + int matchfound = 0; + + serverlist[i]->info.quicksearch_hit = 0; + + /* match against server name */ + if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME; + } + + /* match against players */ + for(p = 0; p < serverlist[i]->info.num_players; p++) + { + if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME; + break; + } + } + + /* match against map */ + if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME; + } + + if(!matchfound) + filtered = 1; + } + + if(!filtered && config.b_filter_gametype[0] != 0) + { + /* match against game type */ + if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype)) + filtered = 1; + } + } + + if(filtered == 0) + sorted_serverlist[num_sorted_servers++] = i; + } +} + +static int client_serverbrowse_sorthash() +{ + int i = config.b_sort&0xf; + i |= config.b_filter_empty<<4; + i |= config.b_filter_full<<5; + i |= config.b_filter_pw<<6; + i |= config.b_sort_order<<7; + i |= config.b_filter_compatversion<<8; + i |= config.b_filter_pure<<9; + i |= config.b_filter_pure_map<<10; + i |= config.b_filter_ping<<16; + return i; +} + +static void client_serverbrowse_sort() +{ + int i; + + /* create filtered list */ + client_serverbrowse_filter(); + + /* sort */ + if(config.b_sort == BROWSESORT_NAME) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); + else if(config.b_sort == BROWSESORT_PING) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); + else if(config.b_sort == BROWSESORT_MAP) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); + else if(config.b_sort == BROWSESORT_NUMPLAYERS) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); + else if(config.b_sort == BROWSESORT_GAMETYPE) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); + else if(config.b_sort == BROWSESORT_PROGRESSION) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); + + /* invert the list if requested */ + if(config.b_sort_order) + { + for(i = 0; i < num_sorted_servers/2; i++) + { + int temp = sorted_serverlist[i]; + sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1]; + sorted_serverlist[num_sorted_servers-i-1] = temp; + } + } + + /* set indexes */ + for(i = 0; i < num_sorted_servers; i++) + serverlist[sorted_serverlist[i]]->info.sorted_index = i; + + str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring)); + str_copy(filterstring, config.b_filter_string, sizeof(filterstring)); + sorthash = client_serverbrowse_sorthash(); +} + +static void client_serverbrowse_remove_request(SERVERENTRY *entry) +{ + if(entry->prev_req || entry->next_req || first_req_server == entry) + { + if(entry->prev_req) + entry->prev_req->next_req = entry->next_req; + else + first_req_server = entry->next_req; + + if(entry->next_req) + entry->next_req->prev_req = entry->prev_req; + else + last_req_server = entry->prev_req; + + entry->prev_req = 0; + entry->next_req = 0; + num_requests--; + } +} + +static SERVERENTRY *client_serverbrowse_find(NETADDR *addr) +{ + SERVERENTRY *entry = serverlist_ip[addr->ip[0]]; + + for(; entry; entry = entry->next_ip) + { + if(net_addr_comp(&entry->addr, addr) == 0) + return entry; + } + return (SERVERENTRY*)0; +} + +void client_serverbrowse_queuerequest(SERVERENTRY *entry) +{ + /* add it to the list of servers that we should request info from */ + entry->prev_req = last_req_server; + if(last_req_server) + last_req_server->next_req = entry; + else + first_req_server = entry; + last_req_server = entry; + + num_requests++; +} + +void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info) +{ + int fav = entry->info.favorite; + entry->info = *info; + entry->info.favorite = fav; + entry->info.netaddr = entry->addr; + + // all these are just for nice compability + if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype)); + else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype)); + else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype)); + + /*if(!request) + { + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + }*/ + + entry->got_info = 1; + client_serverbrowse_sort(); +} + +SERVERENTRY *client_serverbrowse_add(NETADDR *addr) +{ + int hash = addr->ip[0]; + SERVERENTRY *entry = 0; + int i; + + /* create new entry */ + entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); + mem_zero(entry, sizeof(SERVERENTRY)); + + /* set the info */ + entry->addr = *addr; + entry->info.netaddr = *addr; + + entry->info.latency = 999; + str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d", + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + + /*if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/ + + /* check if it's a favorite */ + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(addr, &favorite_servers[i]) == 0) + entry->info.favorite = 1; + } + + /* add to the hash list */ + entry->next_ip = serverlist_ip[hash]; + serverlist_ip[hash] = entry; + + if(num_servers == num_server_capacity) + { + SERVERENTRY **newlist; + num_server_capacity += 100; + newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); + mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); + mem_free(serverlist); + serverlist = newlist; + } + + /* add to list */ + serverlist[num_servers] = entry; + entry->info.server_index = num_servers; + num_servers++; + + return entry; +} + +void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info) +{ + SERVERENTRY *entry = 0; + if(type == BROWSESET_MASTER_ADD) + { + if(serverlist_type != BROWSETYPE_INTERNET) + return; + + if(!client_serverbrowse_find(addr)) + { + entry = client_serverbrowse_add(addr); + client_serverbrowse_queuerequest(entry); + } + } + else if(type == BROWSESET_FAV_ADD) + { + if(serverlist_type != BROWSETYPE_FAVORITES) + return; + + if(!client_serverbrowse_find(addr)) + { + entry = client_serverbrowse_add(addr); + client_serverbrowse_queuerequest(entry); + } + } + else if(type == BROWSESET_TOKEN) + { + if(token != current_token) + return; + + entry = client_serverbrowse_find(addr); + if(!entry) + entry = client_serverbrowse_add(addr); + if(entry) + { + client_serverbrowse_setinfo(entry, info); + if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); + else + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + } + else if(type == BROWSESET_OLD_INTERNET) + { + entry = client_serverbrowse_find(addr); + if(entry) + { + client_serverbrowse_setinfo(entry, info); + + if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); + else + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + } + else if(type == BROWSESET_OLD_LAN) + { + entry = client_serverbrowse_find(addr); + if(entry) + if(!entry) + entry = client_serverbrowse_add(addr); + if(entry) + client_serverbrowse_setinfo(entry, info); + } + + client_serverbrowse_sort(); +} + +void client_serverbrowse_refresh(int type) +{ + /* clear out everything */ + if(serverlist_heap) + memheap_destroy(serverlist_heap); + serverlist_heap = memheap_create(); + num_servers = 0; + num_sorted_servers = 0; + mem_zero(serverlist_ip, sizeof(serverlist_ip)); + first_req_server = 0; + last_req_server = 0; + num_requests = 0; + + /* next token */ + current_token = (current_token+1)&0xff; + + /* */ + serverlist_type = type; + + if(type == BROWSETYPE_LAN) + { + unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1]; + CNetChunk Packet; + int i; + + mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + Buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; + + Packet.m_ClientID = -1; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_Address.ip[0] = 255; + Packet.m_Address.ip[1] = 255; + Packet.m_Address.ip[2] = 255; + Packet.m_Address.ip[3] = 255; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(Buffer); + Packet.m_pData = Buffer; + broadcast_time = time_get(); + + for(i = 8303; i <= 8310; i++) + { + Packet.m_Address.port = i; + m_NetClient.Send(&Packet); + } + + if(config.debug) + dbg_msg("client", "broadcasting for servers"); + } + else if(type == BROWSETYPE_INTERNET) + need_refresh = 1; + else if(type == BROWSETYPE_FAVORITES) + { + for(int i = 0; i < num_favorite_servers; i++) + client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0); + } +} + +static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) +{ + /*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/ + CNetChunk Packet; + + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + } + + /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ + + Packet.m_ClientID = -1; + Packet.m_Address = *addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + /*p.data_size = sizeof(buffer); + p.data = buffer; + netclient_send(net, &p);*/ + + /* send old requtest style aswell */ + Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO); + Packet.m_pData = SERVERBROWSE_OLD_GETINFO; + m_NetClient.Send(&Packet); + + if(entry) + entry->request_time = time_get(); +} + +void client_serverbrowse_request(NETADDR *addr) +{ + client_serverbrowse_request_impl(addr, 0); +} + + +void client_serverbrowse_update() +{ + int64 timeout = time_freq(); + int64 now = time_get(); + int count; + SERVERENTRY *entry, *next; + + /* do server list requests */ + if(need_refresh && !mastersrv_refreshing()) + { + NETADDR addr; + CNetChunk Packet; + int i; + + need_refresh = 0; + + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); + Packet.m_pData = SERVERBROWSE_GETLIST; + + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + addr = mastersrv_get(i); + if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) + continue; + + Packet.m_Address = addr; + m_NetClient.Send(&Packet); + } + + if(config.debug) + dbg_msg("client", "requesting server list"); + } + + /* do timeouts */ + entry = first_req_server; + while(1) + { + if(!entry) /* no more entries */ + break; + + next = entry->next_req; + + if(entry->request_time && entry->request_time+timeout < now) + { + /* timeout */ + client_serverbrowse_remove_request(entry); + num_requests--; + } + + entry = next; + } + + /* do timeouts */ + entry = first_req_server; + count = 0; + while(1) + { + if(!entry) /* no more entries */ + break; + + /* no more then 10 concurrent requests */ + if(count == config.b_max_requests) + break; + + if(entry->request_time == 0) + client_serverbrowse_request_impl(&entry->addr, entry); + + count++; + entry = entry->next_req; + } + + /* check if we need to resort */ + /* TODO: remove the strcmp */ + if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0) + client_serverbrowse_sort(); +} + + +int client_serverbrowse_isfavorite(NETADDR addr) +{ + /* search for the address */ + int i; + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + return 1; + } + return 0; +} + +void client_serverbrowse_addfavorite(NETADDR addr) +{ + int i; + SERVERENTRY *entry; + + if(num_favorite_servers == MAX_FAVORITES) + return; + + /* make sure that we don't already have the server in our list */ + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + return; + } + + /* add the server to the list */ + favorite_servers[num_favorite_servers++] = addr; + entry = client_serverbrowse_find(&addr); + if(entry) + entry->info.favorite = 1; + dbg_msg("", "added fav, %p", entry); +} + +void client_serverbrowse_removefavorite(NETADDR addr) +{ + int i; + SERVERENTRY *entry; + + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + { + mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1)); + num_favorite_servers--; + + entry = client_serverbrowse_find(&addr); + if(entry) + entry->info.favorite = 0; + + return; + } + } +} + +void client_serverbrowse_save() +{ + int i; + char addrstr[128]; + char buffer[256]; + for(i = 0; i < num_favorite_servers; i++) + { + net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr)); + str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr); + engine_config_write_line(buffer); + } +} + + +int client_serverbrowse_refreshingmasters() +{ + return mastersrv_refreshing(); +} |