mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-11 02:28:18 +00:00
405 lines
9.9 KiB
C
405 lines
9.9 KiB
C
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
|
#include <engine/system.h>
|
|
#include <engine/network.h>
|
|
#include <engine/interface.h>
|
|
#include <engine/config.h>
|
|
#include <engine/memheap.h>
|
|
|
|
#include <mastersrv/mastersrv.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
extern NETCLIENT *net;
|
|
|
|
|
|
/* ------ server browse ---- */
|
|
/* TODO: move all this to a separate file */
|
|
|
|
typedef struct SERVERENTRY_t SERVERENTRY;
|
|
struct SERVERENTRY_t
|
|
{
|
|
NETADDR4 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;
|
|
|
|
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 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 int serverlist_lan = 1;
|
|
|
|
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];
|
|
return a->info.latency > b->info.latency;
|
|
}
|
|
|
|
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 a->info.game_type > b->info.game_type;
|
|
}
|
|
|
|
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];
|
|
return a->info.progression > b->info.progression;
|
|
}
|
|
|
|
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];
|
|
return a->info.num_players > b->info.num_players;
|
|
}
|
|
|
|
static void client_serverbrowse_filter()
|
|
{
|
|
int i = 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 = 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&1)
|
|
filtered = 1;
|
|
else if(config.b_filter_string[0] != 0)
|
|
{
|
|
if(strstr(serverlist[i]->info.name, config.b_filter_string) == 0)
|
|
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;
|
|
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);
|
|
|
|
/* set indexes */
|
|
for(i = 0; i < num_sorted_servers; i++)
|
|
serverlist[sorted_serverlist[i]]->info.sorted_index = i;
|
|
|
|
strncpy(filterstring, config.b_filter_string, sizeof(filterstring)-1);
|
|
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--;
|
|
}
|
|
}
|
|
|
|
void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info)
|
|
{
|
|
int hash = addr->ip[0];
|
|
SERVERENTRY *entry = serverlist_ip[hash];
|
|
while(entry)
|
|
{
|
|
if(net_addr4_cmp(&entry->addr, addr) == 0)
|
|
{
|
|
/* update the server that we already have */
|
|
entry->info = *info;
|
|
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();
|
|
return;
|
|
}
|
|
entry = entry->next_ip;
|
|
}
|
|
|
|
/* create new entry */
|
|
entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY));
|
|
mem_zero(entry, sizeof(SERVERENTRY));
|
|
|
|
/* set the info */
|
|
entry->addr = *addr;
|
|
entry->info = *info;
|
|
|
|
/* 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 = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1);
|
|
memcpy(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++;
|
|
|
|
/* */
|
|
if(request)
|
|
{
|
|
/* 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++;
|
|
}
|
|
|
|
client_serverbrowse_sort();
|
|
}
|
|
|
|
void client_serverbrowse_refresh(int lan)
|
|
{
|
|
/* 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;
|
|
|
|
|
|
/* */
|
|
serverlist_lan = lan;
|
|
|
|
if(serverlist_lan)
|
|
{
|
|
NETPACKET packet;
|
|
packet.client_id = -1;
|
|
mem_zero(&packet, sizeof(packet));
|
|
packet.address.ip[0] = 255;
|
|
packet.address.ip[1] = 255;
|
|
packet.address.ip[2] = 255;
|
|
packet.address.ip[3] = 255;
|
|
packet.address.port = 8303;
|
|
packet.flags = PACKETFLAG_CONNLESS;
|
|
packet.data_size = sizeof(SERVERBROWSE_GETINFO);
|
|
packet.data = SERVERBROWSE_GETINFO;
|
|
netclient_send(net, &packet);
|
|
|
|
if(config.debug)
|
|
dbg_msg("client", "broadcasting for servers");
|
|
}
|
|
else
|
|
{
|
|
NETADDR4 master_server;
|
|
NETPACKET p;
|
|
|
|
net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server);
|
|
|
|
mem_zero(&p, sizeof(p));
|
|
p.client_id = -1;
|
|
p.address = master_server;
|
|
p.flags = PACKETFLAG_CONNLESS;
|
|
p.data_size = sizeof(SERVERBROWSE_GETLIST);
|
|
p.data = SERVERBROWSE_GETLIST;
|
|
netclient_send(net, &p);
|
|
|
|
if(config.debug)
|
|
dbg_msg("client", "requesting server list");
|
|
}
|
|
}
|
|
|
|
static void client_serverbrowse_request(SERVERENTRY *entry)
|
|
{
|
|
NETPACKET p;
|
|
|
|
if(config.debug)
|
|
{
|
|
dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
|
|
entry->addr.ip[0], entry->addr.ip[1], entry->addr.ip[2],
|
|
entry->addr.ip[3], entry->addr.port);
|
|
}
|
|
|
|
p.client_id = -1;
|
|
p.address = entry->addr;
|
|
p.flags = PACKETFLAG_CONNLESS;
|
|
p.data_size = sizeof(SERVERBROWSE_GETINFO);
|
|
p.data = SERVERBROWSE_GETINFO;
|
|
netclient_send(net, &p);
|
|
entry->request_time = time_get();
|
|
}
|
|
|
|
void client_serverbrowse_update()
|
|
{
|
|
int64 timeout = time_freq();
|
|
int64 now = time_get();
|
|
int count;
|
|
SERVERENTRY *entry, *next;
|
|
|
|
/* 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(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)
|
|
client_serverbrowse_sort();
|
|
}
|