about summary refs log tree commit diff
path: root/src/engine/server/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/server/server.cpp')
-rw-r--r--src/engine/server/server.cpp571
1 files changed, 193 insertions, 378 deletions
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index d0ae80f0..5632689e 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -1,138 +1,23 @@
-#include <stdio.h>
-#include <string.h>
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
 
 #include <baselib/system.h>
 
 #include <engine/interface.h>
 
-//#include "socket.h"
 #include <engine/packet.h>
 #include <engine/snapshot.h>
 
-#include <engine/lzw.h>
+#include <engine/compression.h>
 #include <engine/versions.h>
 
-namespace baselib {}
-using namespace baselib;
-
-int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
-{
-	if(
-		a->ip[0] != b->ip[0] ||
-		a->ip[1] != b->ip[1] ||
-		a->ip[2] != b->ip[2] ||
-		a->ip[3] != b->ip[3] ||
-		a->port != b->port
-	)
-		return 1;
-	return 0;
-}
-
-// --- string handling (MOVE THESE!!) ---
-void snap_encode_string(const char *src, int *dst, int length, int max_length)
-{
-        const unsigned char *p = (const unsigned char *)src;
-
-        // handle whole int
-        for(int i = 0; i < length/4; i++)
-        {
-                *dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]);
-                p += 4;
-                dst++;
-        }
-
-        // take care of the left overs
-        int left = length%4;
-        if(left)
-        {
-                unsigned last = 0;
-                switch(left)
-                {
-                        case 3: last |= p[2]<<8;
-                        case 2: last |= p[1]<<16;
-                        case 1: last |= p[0]<<24;
-                }
-                *dst = last;
-        }
-}
-
-
-class snapshot_builder
-{
-public:
-	static const int MAX_ITEMS = 512;
-	//static const int MAX_DATA_SIZE=1*1024;
-
-	char data[MAX_SNAPSHOT_SIZE];
-	int data_size;
-
-	int offsets[MAX_ITEMS];
-	int num_items;
-
-	int top_size;
-	int top_items;
-
-	int snapnum;
-
-	snapshot_builder()
-	{
-		top_size = 0;
-		top_items = 0;
-		snapnum = 0;
-	}
-
-	void start()
-	{
-		data_size = 0;
-		num_items = 0;
-	}
+#include <engine/network.h>
+#include <engine/config.h>
 
-	int finish(void *snapdata)
-	{
-		snapnum++;
 
-		// collect some data
-		/*
-		int change = 0;
-		if(data_size > top_size)
-		{
-			change++;
-			top_size = data_size;
-		}
-
-		if(num_items > top_items)
-		{
-			change++;
-			top_items = num_items;
-		}
-
-		if(change)
-		{
-			dbg_msg("snapshot", "new top, items=%d size=%d", top_items, top_size);
-		}*/
-
-		// flattern and make the snapshot
-		snapshot *snap = (snapshot *)snapdata;
-		snap->num_items = num_items;
-		int offset_size = sizeof(int)*num_items;
-		mem_copy(snap->offsets, offsets, offset_size);
-		mem_copy(snap->data_start(), data, data_size);
-		return sizeof(int) + offset_size + data_size;
-	}
-
-	void *new_item(int type, int id, int size)
-	{
-		snapshot::item *obj = (snapshot::item *)(data+data_size);
-		obj->type_and_id = (type<<16)|id;
-		offsets[num_items] = data_size;
-		data_size += sizeof(int) + size;
-		num_items++;
-		dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data");
-		dbg_assert(num_items < MAX_ITEMS, "too many items");
-
-		return &obj->data;
-	}
-};
+namespace baselib {}
+using namespace baselib;
 
 static snapshot_builder builder;
 
@@ -143,7 +28,6 @@ void *snap_new_item(int type, int id, int size)
 	return builder.new_item(type, id, size);
 }
 
-
 //
 class client
 {
@@ -157,34 +41,21 @@ public:
 
 	// connection state info
 	int state;
-
-	// (ticks) if lastactivity > 5 seconds kick him
-	int64 lastactivity;
-	connection conn;
+	
+	int last_acked_snapshot;
+	snapshot_storage snapshots;
 
 	char name[MAX_NAME_LENGTH];
 	char clan[MAX_CLANNAME_LENGTH];
-	/*
-	client()
-	{
-		state = STATE_EMPTY;
-		name[0] = 0;
-		clan[0] = 0;
-	}
-
-	~client()
-	{
-		dbg_assert(state == STATE_EMPTY, "client destoyed while in use");
-	}*/
 
 	bool is_empty() const { return state == STATE_EMPTY; }
 	bool is_ingame() const { return state == STATE_INGAME; }
-	const netaddr4 &address() const { return conn.address(); }
 };
 
 static client clients[MAX_CLIENTS];
 static int current_tick = 0;
 static int send_heartbeats = 1;
+static net_server net;
 
 int server_tick()
 {
@@ -193,7 +64,7 @@ int server_tick()
 
 int server_tickspeed()
 {
-	return 50;
+	return SERVER_TICK_SPEED;
 }
 
 int server_init()
@@ -203,7 +74,7 @@ int server_init()
 		clients[i].state = client::STATE_EMPTY;
 		clients[i].name[0] = 0;
 		clients[i].clan[0] = 0;
-		clients[i].lastactivity = 0;
+		//clients[i].lastactivity = 0;
 	}
 
 	current_tick = 0;
@@ -225,12 +96,27 @@ int server_getclientinfo(int client_id, client_info *info)
 	return 0;
 }
 
-//
+
+int server_send_msg(int client_id)
+{
+	const msg_info *info = msg_get_info();
+	NETPACKET packet;
+	packet.client_id = client_id;
+	packet.data = info->data;
+	packet.data_size = info->size;
+
+	if(info->flags&MSGFLAG_VITAL)	
+		packet.flags = PACKETFLAG_VITAL;
+	
+	net.send(&packet);
+	return 0;
+}
+	
+// TODO: remove this class
 class server
 {
 public:
-
-	socket_udp4 game_socket;
+	//socket_udp4 game_socket;
 
 	const char *map_name;
 	const char *server_name;
@@ -256,14 +142,14 @@ public:
 		}
 
 		// start server
-		if(!game_socket.open(8303))
+		if(!net.open(8303, 0, 0))
 		{
 			dbg_msg("network/server", "couldn't open socket");
 			return false;
 		}
 
-		for(int i = 0; i < MAX_CLIENTS; i++)
-			dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
+		//for(int i = 0; i < MAX_CLIENTS; i++)
+			//dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
 
 		if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0)
 		{
@@ -304,17 +190,6 @@ public:
 					snaptime += time_get()-start;
 				}
 
-				// Check for client timeouts
-				for (int i = 0; i < MAX_CLIENTS; i++)
-				{
-					if (clients[i].state != client::STATE_EMPTY)
-					{
-						// check last activity time
-						if (((lasttick - clients[i].lastactivity) / time_freq()) > SERVER_CLIENT_TIMEOUT)
-							client_timeout(i);
-					}
-				}
-
 				lasttick += time_per_tick;
 			}
 
@@ -332,8 +207,7 @@ public:
 
 						// TODO: fix me
 						netaddr4 me(127, 0, 0, 0, 8303);
-
-						send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
+						//send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
 					}
 
 					lastheartbeat = t+time_per_heartbeat;
@@ -357,6 +231,7 @@ public:
 					(simulationtime+snaptime+networktime)/(float)reportinterval*100.0f);
 
 				unsigned sent_total=0, recv_total=0;
+				/*
 				for (int i = 0; i < MAX_CLIENTS; i++)
 					if (!clients[i].is_empty())
 					{
@@ -366,7 +241,7 @@ public:
 						sent_total += s;
 						recv_total += r;
 					}
-
+				*/
 
 				dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d",
 					biggest_snapshot, sent_total/3, recv_total/3);
@@ -393,6 +268,8 @@ public:
 
 	void snap()
 	{
+		//if(current_tick&1)
+		//	return;
 		mods_presnap();
 
 		for(int i = 0; i < MAX_CLIENTS; i++)
@@ -400,33 +277,86 @@ public:
 			if(clients[i].is_ingame())
 			{
 				char data[MAX_SNAPSHOT_SIZE];
+				char deltadata[MAX_SNAPSHOT_SIZE];
 				char compdata[MAX_SNAPSHOT_SIZE];
+				//char intdata[MAX_SNAPSHOT_SIZE];
 				builder.start();
 				mods_snap(i);
 
 				// finish snapshot
 				int snapshot_size = builder.finish(data);
 
-				// compress it
-				int compsize = lzw_compress(data, snapshot_size, compdata);
-				snapshot_size = compsize;
-
-				if(snapshot_size > biggest_snapshot)
-					biggest_snapshot = snapshot_size;
-
-				const int max_size = MAX_SNAPSHOT_PACKSIZE;
-				int numpackets = (snapshot_size+max_size-1)/max_size;
-				for(int n = 0, left = snapshot_size; left; n++)
+				// remove old snapshos
+				// keep 1 seconds worth of snapshots
+				clients[i].snapshots.purge_until(current_tick-SERVER_TICK_SPEED);
+				
+				// save it the snapshot
+				clients[i].snapshots.add(current_tick, snapshot_size, data);
+				
+				// find snapshot that we can preform delta against
+				static snapshot emptysnap;
+				emptysnap.data_size = 0;
+				emptysnap.num_items = 0;
+				
+				snapshot *deltashot = &emptysnap;
+				int deltashot_size;
+				int delta_tick = -1;
+				{
+					void *delta_data;
+					deltashot_size = clients[i].snapshots.get(clients[i].last_acked_snapshot, (void **)&delta_data);
+					if(deltashot_size >= 0)
+					{
+						delta_tick = clients[i].last_acked_snapshot;
+						deltashot = (snapshot *)delta_data;
+					}
+				}
+				
+				// create delta
+				int deltasize = snapshot_create_delta(deltashot, (snapshot*)data, deltadata);
+				
+				if(deltasize)
 				{
-					int chunk = left < max_size ? left : max_size;
-					left -= chunk;
-
-					packet p(NETMSG_SERVER_SNAP);
-					p.write_int(numpackets);
-					p.write_int(n);
-					p.write_int(chunk);
-					p.write_raw(&compdata[n*max_size], chunk);
-					clients[i].conn.send(&p);
+					// compress it
+					//int intsize = -1;
+					unsigned char intdata[MAX_SNAPSHOT_SIZE];
+					int intsize = intpack_compress(deltadata, deltasize, intdata);
+					
+					int compsize = zerobit_compress(intdata, intsize, compdata);
+					//dbg_msg("compress", "%5d --delta-> %5d --int-> %5d --zero-> %5d %5d",
+						//snapshot_size, deltasize, intsize, compsize, intsize-compsize);
+					snapshot_size = compsize;
+
+					if(snapshot_size > biggest_snapshot)
+						biggest_snapshot = snapshot_size;
+
+					const int max_size = MAX_SNAPSHOT_PACKSIZE;
+					int numpackets = (snapshot_size+max_size-1)/max_size;
+					for(int n = 0, left = snapshot_size; left; n++)
+					{
+						int chunk = left < max_size ? left : max_size;
+						left -= chunk;
+
+						if(numpackets == 1)
+							msg_pack_start(NETMSG_SNAPSMALL, 0);
+						else
+							msg_pack_start(NETMSG_SNAP, 0);
+						msg_pack_int(current_tick);
+						msg_pack_int(current_tick-delta_tick); // compressed with
+						msg_pack_int(chunk);
+						msg_pack_raw(&compdata[n*max_size], chunk);
+						msg_pack_end();
+						//const msg_info *info = msg_get_info();
+						//dbg_msg("server", "size=%d", info->size);
+						server_send_msg(i);
+					}
+				}
+				else
+				{
+					msg_pack_start(NETMSG_SNAPEMPTY, 0);
+					msg_pack_int(current_tick);
+					msg_pack_int(current_tick-delta_tick); // compressed with
+					msg_pack_end();
+					server_send_msg(i);
 				}
 			}
 		}
@@ -434,11 +364,12 @@ public:
 		mods_postsnap();
 	}
 
-	void send_accept(client *client, const char *map)
+	void send_map(int cid)
 	{
-		packet p(NETMSG_SERVER_ACCEPT);
-		p.write_str(map);
-		client->conn.send(&p);
+		msg_pack_start(NETMSG_MAP, MSGFLAG_VITAL);
+		msg_pack_string(map_name, 0);
+		msg_pack_end();
+		server_send_msg(cid);
 	}
 
 	void drop(int cid, const char *reason)
@@ -451,161 +382,47 @@ public:
 		dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name);
 	}
 
-	int find_client(const netaddr4 *addr)
+	void process_client_packet(NETPACKET *packet)
 	{
-		// fetch client
-		for(int i = 0; i < MAX_CLIENTS; i++)
+		int cid = packet->client_id;
+		int msg = msg_unpack_start(packet->data, packet->data_size);
+		if(msg == NETMSG_INFO)
 		{
-			if(!clients[i].is_empty() && clients[i].address() == *addr)
-				return i;
+			strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH);
+			strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
+			const char *password = msg_unpack_string();
+			const char *skin = msg_unpack_string();
+			(void)password; // ignore these variables
+			(void)skin;
+			send_map(cid);
 		}
-		return -1;
-	}
-
-	void client_process_packet(int cid, packet *p)
-	{
-		clients[cid].lastactivity = lasttick;
-		if(p->msg() == NETMSG_CLIENT_DONE)
+		else if(msg == NETMSG_ENTERGAME)
 		{
 			dbg_msg("game", "player as entered the game. cid=%x", cid);
 			clients[cid].state = client::STATE_INGAME;
 			mods_client_enter(cid);
 		}
-		else if(p->msg() == NETMSG_CLIENT_INPUT)
+		else if(msg == NETMSG_INPUT)
 		{
 			int input[MAX_INPUT_SIZE];
-			int size = p->read_int();
+			int size = msg_unpack_int();
 			for(int i = 0; i < size/4; i++)
-				input[i] = p->read_int();
-			if(p->is_good())
-			{
-				//dbg_msg("network/server", "applying input %d %d %d", input[0], input[1], input[2]);
-				mods_client_input(cid, input);
-			}
+				input[i] = msg_unpack_int();
+			mods_client_input(cid, input);
 		}
-		else if(p->msg() == NETMSG_CLIENT_ERROR)
+		else if(msg == NETMSG_SNAPACK)
 		{
-			const char *reason = p->read_str();
-			if(p->is_good())
-				dbg_msg("network/server", "client error. cid=%x reason='%s'", cid, reason);
-			else
-				dbg_msg("network/server", "client error. cid=%x", cid);
-			drop(cid, "client error");
+			clients[cid].last_acked_snapshot = msg_unpack_int();
 		}
 		else
 		{
-			dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg());
-			drop(cid, "invalid message");
+			dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size);
 		}
+		
 	}
 
-	void process_packet(packet *p, netaddr4 *from)
+	void process_packet(NETPACKET *packet)
 	{
-		// do version check
-		if(p->version() != TEEWARS_NETVERSION)
-		{
-			// send an empty packet back.
-			// this will allow the client to check the version
-			packet p;
-			game_socket.send(from, p.data(), p.size());
-			return;
-		}
-
-		if(p->msg() == NETMSG_CLIENT_CONNECT)
-		{
-			// we got no state for this client yet
-			const char *version;
-			const char *name;
-			const char *clan;
-			const char *password;
-			const char *skin;
-
-			version = p->read_str();
-			name = p->read_str();
-			clan = p->read_str();
-			password = p->read_str();
-			skin = p->read_str();
-
-			if(p->is_good())
-			{
-				/*
-				// check version
-				if(strcmp(version, TEEWARS_NETVERSION) != 0)
-				{
-					dbg_msg("network/server", "wrong version connecting '%s'", version);
-					// TODO: send error
-					return;
-				}*/
-
-				// look for empty slot, linear search
-				int id = -1;
-				for(int i = 0; i < MAX_CLIENTS; i++)
-					if(clients[i].is_empty())
-					{
-						id = i;
-						break;
-					}
-
-				if(id != -1)
-				{
-					// slot found
-					// TODO: perform correct copy here
-					mem_copy(clients[id].name, name, MAX_NAME_LENGTH);
-					mem_copy(clients[id].clan, clan, MAX_CLANNAME_LENGTH);
-					clients[id].state = client::STATE_CONNECTING;
-					clients[id].conn.init(&game_socket, from);
-
-					clients[id].lastactivity = lasttick;
-					clients[id].name[MAX_NAME_LENGTH-1] = 0;
-					clients[id].clan[MAX_CLANNAME_LENGTH-1] = 0;
-
-					dbg_msg("network/server", "client connected. '%s' on slot %d", name, id);
-
-					// TODO: return success
-					send_accept(&clients[id], map_name);
-				}
-				else
-				{
-					// no slot found
-					// TODO: send error
-					dbg_msg("network/server", "client connected but server is full");
-
-					for(int i = 0; i < MAX_CLIENTS; i++)
-						dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
-				}
-			}
-		}
-		else
-		{
-			int cid = find_client(from);
-			if(cid >= 0)
-			{
-				if(clients[cid].conn.feed(p))
-				{
-					// packet is ok
-					unsigned msg = p->msg();
-
-					// client found, check state
-					if(((msg>>16)&0xff)&clients[cid].state)
-					{
-						// state is ok
-						client_process_packet(cid, p);
-					}
-					else
-					{
-						// invalid state, disconnect the client
-						drop(cid, "invalid message at this state");
-					}
-				}
-				else
-				{
-					drop(cid, "connection error");
-				}
-
-			}
-			else
-				dbg_msg("network/server", "packet from strange address.");
-		}
 	}
 
 	void client_timeout(int clientId)
@@ -615,67 +432,53 @@ public:
 
 	void pump_network()
 	{
+		net.update();
+		
+		// process packets
+		NETPACKET packet;
+		while(net.recv(&packet))
+		{
+			
+			if(packet.client_id == -1)
+			{
+				// stateless
+			}
+			else
+				process_client_packet(&packet);
+		}
+		
+		// check for removed clients
 		while(1)
 		{
-			packet p;
-			netaddr4 from;
-
-			//int bytes = net_udp4_recv(
-			int bytes = game_socket.recv(&from, p.data(), p.max_size());
-			//int bytes = game_socket.recv(&from, p.data(), p.max_size());
-			if(bytes <= 0)
+			int cid = net.delclient();
+			if(cid == -1)
 				break;
-
-			process_packet(&p, &from);
+			
+			clients[cid].state = client::STATE_EMPTY;
+			clients[cid].name[0] = 0;
+			clients[cid].clan[0] = 0;
+			clients[cid].snapshots.purge_all();
+			
+			mods_client_drop(cid);
+			
+			dbg_msg("server", "del client %d", cid);
+		}
+		
+		// check for new clients
+		while(1)
+		{
+			int cid = net.newclient();
+			if(cid == -1)
+				break;
+			
+			clients[cid].state = client::STATE_CONNECTING;
+			clients[cid].name[0] = 0;
+			clients[cid].clan[0] = 0;
+			clients[cid].snapshots.purge_all();
+			clients[cid].last_acked_snapshot = -1;
+			
+			dbg_msg("server", "new client %d", cid);
 		}
-		// TODO: check for client timeouts
-	}
-
-	char *write_int(char *buffer, int integer)
-	{
-		*buffer++ = integer >> 24;
-		*buffer++ = integer >> 16;
-		*buffer++ = integer >> 8;
-		*buffer++ = integer;
-
-		return buffer;
-	}
-
-	char *write_netaddr4(char *buffer, NETADDR4 *address)
-	{
-		*buffer++ = address->ip[0];
-		*buffer++ = address->ip[1];
-		*buffer++ = address->ip[2];
-		*buffer++ = address->ip[3];
-
-		return write_int(buffer, address->port);
-	}
-
-	void send_heartbeat(int version, netaddr4 *address, int players, int max_players, const char *name, const char *map_name)
-	{
-		char buffer[216] = {0};
-		char *d = buffer;
-
-		d = write_int(d, 'TWHB');
-		d = write_int(d, version);
-		d = write_netaddr4(d, address);
-		d = write_int(d,players);
-		d = write_int(d, max_players);
-
-		int len = strlen(name);
-		if (len > 128)
-			len = 128;
-
-		memcpy(d, name, len);
-		d += 128;
-
-		len = strlen(map_name);
-		if (len > 64)
-			len = 64;
-
-		memcpy(d, map_name, len);
-		d += 64;
-		game_socket.send(&master_server, buffer, sizeof(buffer));
 	}
 };
 
@@ -683,8 +486,14 @@ int main(int argc, char **argv)
 {
 	dbg_msg("server", "starting...");
 
+	dbg_msg("server", "%d %d", sizeof(snapshot), sizeof(snapshot::item));
+
+	config_reset();
+	config_load("server.cfg");
+
 	const char *mapname = "data/demo.map";
 	const char *servername = 0;
+	
 	// parse arguments
 	for(int i = 1; i < argc; i++)
 	{
@@ -705,6 +514,12 @@ int main(int argc, char **argv)
 			// -p (private server)
 			send_heartbeats = 0;
 		}
+		else if(argv[i][0] == '-' && argv[i][1] == 'o' && argv[i][2] == 0)
+		{
+			// -o port
+			i++;
+			config_set_sv_port(&config, atol(argv[i]));
+		}
 	}
 
 	if(!mapname)