about summary refs log tree commit diff
path: root/src/engine/client/ec_srvbrowse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/client/ec_srvbrowse.c')
-rw-r--r--src/engine/client/ec_srvbrowse.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c
new file mode 100644
index 00000000..9fc5ec02
--- /dev/null
+++ b/src/engine/client/ec_srvbrowse.c
@@ -0,0 +1,404 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <engine/e_system.h>
+#include <engine/e_network.h>
+#include <engine/e_interface.h>
+#include <engine/e_config.h>
+#include <engine/e_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();
+}