about summary refs log tree commit diff
path: root/src/engine/packet.h
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-13 13:40:04 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-13 13:40:04 +0000
commit125d04e51f4e444a38cf038d3ea095d92d3c6dbb (patch)
tree2288bbe4b923ab4d695e9f852c177a12f74ea799 /src/engine/packet.h
parent7be0ae1b2929a3c5dfedf542bce886deefa0f862 (diff)
downloadzcatch-125d04e51f4e444a38cf038d3ea095d92d3c6dbb.tar.gz
zcatch-125d04e51f4e444a38cf038d3ea095d92d3c6dbb.zip
large rewrite and code cleanup
Diffstat (limited to 'src/engine/packet.h')
-rw-r--r--src/engine/packet.h626
1 files changed, 270 insertions, 356 deletions
diff --git a/src/engine/packet.h b/src/engine/packet.h
index fd93d744..6c92bf31 100644
--- a/src/engine/packet.h
+++ b/src/engine/packet.h
@@ -1,442 +1,356 @@
+#include <stdarg.h>
 #include <baselib/stream/file.h>
 #include <baselib/network.h>
 
 #include "versions.h"
+#include "ringbuffer.h"
+#include "compression.h"
+#include "snapshot.h"
 
-#define MACRO_MAKEINT(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
+enum
+{
+	NETMSG_NULL=0,
+	
+	// sent by server
+	NETMSG_MAP,
+	NETMSG_SNAP,
+	NETMSG_SNAPEMPTY,
+	NETMSG_SNAPSMALL,
+	
+	// sent by client
+	NETMSG_INFO,
+	NETMSG_ENTERGAME,
+	NETMSG_INPUT,
+	NETMSG_SNAPACK,
+	
+	// sent by both
+	NETMSG_ERROR,
+};
 
-// TODO: this is not KISS
-class packet
+
+// this should be revised
+enum
 {
-	friend class connection;
-protected:
-	enum
-	{
-		MAX_PACKET_SIZE = 1024,
-	};
-	
-	// packet data
-	struct header
+	MAX_NAME_LENGTH=32,
+	MAX_CLANNAME_LENGTH=32,
+	MAX_INPUT_SIZE=128,
+	MAX_SNAPSHOT_SIZE=64*1024,
+	MAX_SNAPSHOT_PACKSIZE=768
+};
+
+
+class snapshot_storage
+{
+	struct holder
 	{
-		unsigned id;
-		unsigned version;
-		unsigned size_and_flags;
-		unsigned crc;
-		
-		unsigned msg;
-		unsigned ack;
-		unsigned seq;
+		int tick;
+		int data_size;
+		int *data() { return (int *)(this+1); }
 	};
 	
-	unsigned char packet_data[MAX_PACKET_SIZE];
-	unsigned char *current;
+	ring_buffer buffer;
 	
-	// these are used to prepend data in the packet
-	// used for debugging so we have checks that we 
-	// pack and unpack the same way
-	enum
+public:
+	void purge_until(int tick)
 	{
-		DEBUG_TYPE_INT=0x1,
-		DEBUG_TYPE_STR=0x2,
-		DEBUG_TYPE_RAW=0x3,
-	};
+		while(1)
+		{
+			ring_buffer::item *i = buffer.first();
+			if(!i)
+				break;
+			holder *h = (holder *)i->data();
+			if(h->tick < tick)
+				buffer.pop_first();
+			else
+				break;
+		}
+	}
 	
-	// writes an int to the packet
-	void write_int_raw(int i)
+	void purge_all()
 	{
-		// TODO: check for overflow
-		*(int*)current = i;
-		current += sizeof(int);
+		buffer.reset();
 	}
 
-	// reads an int from the packet
-	int read_int_raw()
-	{
-		// TODO: check for overflow
-		int i = *(int*)current;
-		current += sizeof(int);
-		return i;
-	}
-	
-	void debug_insert_mark(int type, int size)
+	void add(int tick, int data_size, void *data)
 	{
-		write_int_raw((type<<16)|size);
+		holder *h = (holder *)buffer.alloc(sizeof(holder)+data_size);
+		h->tick = tick;
+		h->data_size = data_size;
+		mem_copy(h->data(), data, data_size);
 	}
 	
-	void debug_verify_mark(int type, int size)
+	int get(int tick, void **data)
 	{
-		if(read_int_raw() != ((type<<16) | size))
-			dbg_assert(0, "error during packet disassembly");
+		ring_buffer::item *i = buffer.first();
+		while(i)
+		{
+			holder *h = (holder *)i->data();
+			if(h->tick == tick)
+			{
+				*data = h->data();
+				return h->data_size;
+			}
+				
+			i = i->next;
+		}
+		
+		return -1;
 	}
-	
+};
+/*
+class snapshot_delta_builder
+{
 public:
+	static const int MAX_ITEMS = 512;
 
-	enum
-	{
-		FLAG_VITAL=1,
-		FLAG_RESEND=2
-	};
+	char data[MAX_SNAPSHOT_SIZE];
+	int data_size;
 
-	packet(unsigned msg=0)
-	{
-		current = packet_data;
-		current += sizeof(header);
+	int offsets[MAX_ITEMS];
+	int num_items;
+
+	int top_size;
+	int top_items;
 
-		((header*)packet_data)->id = MACRO_MAKEINT('K','M','A',1);
-		((header*)packet_data)->version = TEEWARS_NETVERSION;
-		((header*)packet_data)->msg = msg;
+	int snapnum;
+
+	snapshot_delta_builder()
+	{
+		top_size = 0;
+		top_items = 0;
+		snapnum = 0;
 	}
-	
-	void set_header(unsigned ack, unsigned seq)
+
+	void start()
 	{
-		((header*)packet_data)->ack = ack;
-		((header*)packet_data)->seq = seq;
+		data_size = 0;
+		num_items = 0;
 	}
-	
-	// writes an int to the packet
-	void write_int(int i)
+
+	int finish(void *snapdata)
 	{
-		debug_insert_mark(DEBUG_TYPE_INT, 4);
-		write_int_raw(i);
+		snapnum++;
+
+		// flattern and make the snapshot
+		snapshot *snap = (snapshot *)snapdata;
+		snap->data_size = data_size;
+		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;
 	}
+};
+*/
+
+class snapshot_builder
+{
+public:
+	static const int MAX_ITEMS = 512;
 
-	void write_raw(const char *raw, int size)
+	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()
 	{
-		debug_insert_mark(DEBUG_TYPE_RAW, size);
-		while(size--)
-			*current++ = *raw++;
+		top_size = 0;
+		top_items = 0;
+		snapnum = 0;
 	}
 
-	// writes a string to the packet
-	void write_str(const char *str)
+	void start()
 	{
-		debug_insert_mark(DEBUG_TYPE_STR, 0);
-		int s = strlen(str)+1;
-		write_int_raw(s);
-		for(;*str; current++, str++)
-			*current = *str;
-		*current = 0;
-		current++;
+		data_size = 0;
+		num_items = 0;
 	}
 	
-	// reads an int from the packet
-	int read_int()
+	snapshot::item *get_item(int index) 
 	{
-		debug_verify_mark(DEBUG_TYPE_INT, 4);
-		return read_int_raw();
+		return (snapshot::item *)&(data[offsets[index]]);
 	}
 	
-	// reads a string from the packet
-	const char *read_str()
+	int *get_item_data(int key)
 	{
-		debug_verify_mark(DEBUG_TYPE_STR, 0);
-		int size = read_int_raw();
-		const char *s = (const char *)current;
-		//dbg_msg("packet", "reading string '%s' (%d)", s, size);
-		current += size;
-		return s;
+		for(int i = 0; i < num_items; i++)
+		{
+			if(get_item(i)->key() == key)
+				return (int *)get_item(i)->data();
+		}
+		return 0;
 	}
-	
-	const char *read_raw(int size)
+
+	int finish(void *snapdata)
 	{
-		debug_verify_mark(DEBUG_TYPE_RAW, size);
-		const char *d = (const char *)current;
-		current += size;
-		return d;
+		snapnum++;
+
+		// flattern and make the snapshot
+		snapshot *snap = (snapshot *)snapdata;
+		snap->data_size = data_size;
+		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(snapshot) + offset_size + data_size;
 	}
-	
-	// TODO: impelement this
-	bool is_good() const { return true; }
 
-	unsigned version() const { return ((header*)packet_data)->version; }
-	unsigned msg() const { return ((header*)packet_data)->msg; }
-	unsigned seq() const { return ((header*)packet_data)->seq; }
-	unsigned ack() const { return ((header*)packet_data)->ack; }
-	unsigned flags() const { return (((header*)packet_data)->size_and_flags) & 0xffff; }
-	
-	// access functions to get the size and data
-	int size() const { return (int)(current-(unsigned char*)packet_data); }
-	int max_size() const { return MAX_PACKET_SIZE; }
-	void *data() { return packet_data; }
+	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(snapshot::item) + 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();
+	}
 };
 
-// TODO: remove all the allocations from this class
-class ring_buffer
+class data_packer
 {
-	struct item
+	enum
 	{
-		item *next;
-		item *prev;
-		int size;
+		BUFFER_SIZE=1024*2
 	};
 	
-	item *first;
-	item *last;
-	
-	unsigned buffer_size;
+	unsigned char buffer[BUFFER_SIZE];
+	unsigned char *current;
+	unsigned char *end;
+	int error;
 public:
-	ring_buffer()
-	{
-		first = 0;
-		last = 0;
-		buffer_size = 0;
-	}
-	
-	~ring_buffer()
-	{
-		reset();
-	}
-	
 	void reset()
 	{
-		// clear all
-		while(peek_data())
-			next();		
+		error = 0;
+		current = buffer;
+		end = current + BUFFER_SIZE;
 	}
 	
-	void *alloc(int size)
+	void add_int(int i)
 	{
-		item *i = (item*)mem_alloc(sizeof(item)+size, 1);
-		i->size = size;
-		
-		i->prev = last;
-		i->next = 0;
-		if(last)
-			last->next = i;
-		else
-			first = i;
-		last = i;
-		
-		buffer_size += size;
-		return (void*)(i+1);
-	}
-	
-	unsigned peek_size()
-	{
-		if(!first)
-			return 0;
-		return first->size;
+		// TODO: add space check
+		// TODO: variable length encoding perhaps
+		// TODO: add debug marker
+		current = vint_pack(current, i);
+		//*current++ = (i>>24)&0xff;
+		//*current++ = (i>>16)&0xff;
+		//*current++ = (i>>8)&0xff;
+		//*current++ = i&0xff;
 	}
 
-	void *peek_data()
+	void add_string(const char *p, int limit)
 	{
-		if(!first)
-			return 0;
-		return (void*)(first+1);
-	}
-	
-	void next()
-	{
-		if(first)
+		// TODO: add space check
+		// TODO: add debug marker
+		if(limit > 0)
 		{
-			item *next = first->next;
-			buffer_size += first->size;
-			mem_free(first);
-			first = next;
-			if(first)
-				first->prev = 0;
-			else
-				last = 0;
+			while(*p && limit != 0)
+			{
+				*current++ = *p++;
+				limit--;
+			}
+			*current++ = 0;
+		}
+		else
+		{
+			while(*p)
+				*current++ = *p++;
+			*current++ = 0;
 		}
 	}
 	
-	unsigned size() { return buffer_size; }
-};
-
-//
-class connection
-{
-	baselib::socket_udp4 *socket;
-	baselib::netaddr4 addr;
-	unsigned seq;
-	unsigned ack;
-	
-	unsigned counter_sent_bytes;
-	unsigned counter_recv_bytes;
-	
-	int needs_resend;
-	
-	/*
-	struct resend_packet
-	{
-		resend_packet *next;
-		unsigned seq;
-		unsigned msg;
-		unsigned size;
-		char data[1];
-	};
-	
-	resend_packet *first_resend;
-	resend_packet *last_resend;
-	*/
-	
-	ring_buffer resend_buffer;
-
-	void save_for_resend(packet *p)
-	{
-		/*
-		packet *n = (packet *)resend_buffer.alloc(p->size());
-		mem_copy(n->data(), p->data(), p->size());
-		n->current = (unsigned char*)n->data() + p->size();
-		*/
-	}
-	
-	void remove_resends(unsigned ack)
+	void add_raw(const unsigned char *data, int size)
 	{
-		/*
-		while(1)
+		// TODO: add space check
+		// TODO: add debug marker
+		//add_int(size);
+		while(size)
 		{
-			packet *p = (packet *)resend_buffer.peek_data();
-			if(!p)
-				break;
-			
-			if(p->seq() > ack)
-				break;
-			resend_buffer.next();
-		}*/
+			*current++ = *data++;
+			size--;
+		}
 	}
 	
-public:
-	void counter_reset()
+	int size() const
 	{
-		counter_sent_bytes = 0;
-		counter_recv_bytes = 0;
+		return (const unsigned char *)current-(const unsigned char *)buffer;
 	}
 	
-	void counter_get(unsigned *sent, unsigned *recved)
+	const unsigned char *data()
 	{
-		*sent = counter_sent_bytes;
-		*recved = counter_recv_bytes;
+		return (const unsigned char *)buffer;
 	}
+};
 
-	void init(baselib::socket_udp4 *socket, const baselib::netaddr4 *addr)
+class data_unpacker
+{
+	const unsigned char *current;
+	const unsigned char *start;
+	const unsigned char *end;
+	int error;
+	
+public:
+	void reset(const unsigned char *data, int size)
 	{
-		resend_buffer.reset();
-		
-		this->addr = *addr;
-		this->socket = socket;
-		ack = 0;
-		seq = 0;
-		needs_resend = 0;
-		counter_reset();
+		error = 0;
+		start = data;
+		end = start + size;
+		current = start;
 	}
 	
-	void send(packet *p)
+	int get_int()
 	{
-		if(p->flags()&packet::FLAG_VITAL)
-			seq++;
-		
-		p->set_header(ack, seq);
-
-		if(p->flags()&packet::FLAG_VITAL)
-			save_for_resend(p);
-
-		// TODO: request resend if needed, use needs_resend variable
-		
-		socket->send(&address(), p->data(), p->size());
-		counter_sent_bytes += p->size();
+		int i;
+		current = vint_unpack(current, &i);
+		// TODO: might be changed into variable width
+		// TODO: add range check
+		// TODO: add debug marker
+		//i = (current[0]<<24) | (current[1]<<16) | (current[2]<<8) | (current[3]);
+		//current += 4;
+		return i;
 	}
 	
-	packet *feed(packet *p)
+	const char *get_string()
 	{
-		counter_recv_bytes += p->size();
-		
-		if(p->flags()&packet::FLAG_VITAL)
-		{
-			if(p->seq() == ack+1)
-			{
-				// packet in seqence, ack it
-				ack++;
-				//dbg_msg("network/connection", "packet in sequence. seq/ack=%x", ack);
-				return p;
-			}
-			else if(p->seq() > ack)
-			{
-				// packet loss
-				needs_resend = 1;
-				dbg_msg("network/connection", "packet loss! seq=%x ack=%x+1", p->seq(), ack);
-				return p;
-			}
-			else
-			{
-				// we already got this packet
-				return 0x0;
-			}
-		}
-		
-		// remove resends
-		remove_resends(p->ack());
-		
-		// handle resends
-		if(p->flags()&packet::FLAG_RESEND)
-		{
-			// peer as requested a resend of all non acked packages.
-			
-		}
-		
-		return p;		
+		// TODO: add range check
+		// TODO: add debug marker
+		const char *ptr = (const char *)current;
+		while(*current) // skip the string
+			current++;
+		current++;
+		return ptr;
 	}
 	
-	const baselib::netaddr4 &address() const { return addr; }
-	
-	void update()
+	const unsigned char *get_raw(int size)
 	{
+		// TODO: add range check
+		// TODO: add debug marker
+		//int s = get_int();
+		//if(size)
+			//*size = s;
+		const unsigned char *ptr = current;
+		current += size;
+		return ptr;
 	}
 };
-
-//const char *NETWORK_VERSION = "development";
-
-enum
-{
-	NETMSG_CONTEXT_CONNECT=0x00010000,
-	NETMSG_CONTEXT_GAME=0x00020000,
-	NETMSG_CONTEXT_GLOBAL=0x00040000,
-	
-	// connection phase
-	NETMSG_CLIENT_CONNECT=NETMSG_CONTEXT_CONNECT|1,
-		// str32 name
-		// str32 clan
-		// str32 password
-		// str32 skin	
-	
-	// TODO: These should be implemented to make the server send the map to the client on connect
-	// NETMSG_CLIENT_FETCH,
-	// NETMSG_SERVER_MAPDATA,
-	
-	NETMSG_SERVER_ACCEPT=NETMSG_CONTEXT_CONNECT|2,
-		// str32 mapname
-
-	
-	NETMSG_CLIENT_DONE=NETMSG_CONTEXT_CONNECT|3,
-		// nothing
-	
-	// game phase
-	NETMSG_SERVER_SNAP = NETMSG_CONTEXT_GAME|1, // server will spam these
-		// int num_parts
-		// int part
-		// int size
-		// data *
-		
-	NETMSG_CLIENT_INPUT = NETMSG_CONTEXT_GAME|1, // client will spam these
-		// int input[MAX_INPUTS]
-	
-	NETMSG_SERVER_EVENT = NETMSG_CONTEXT_GAME|2,
-	NETMSG_CLIENT_EVENT = NETMSG_CONTEXT_GAME|2,
-
-	NETMSG_CLIENT_CHECKALIVE = NETMSG_CONTEXT_GAME|3, // check if client is alive
-	
-	NETMSG_CLIENT_ERROR=0x0fffffff,
-		// str128 reason
-		
-	NETMSG_SERVER_ERROR=0x0fffffff,
-		// str128 reason
-};
-
-enum
-{
-	MAX_NAME_LENGTH=32,
-	MAX_CLANNAME_LENGTH=32,
-	MAX_INPUT_SIZE=128,
-	MAX_SNAPSHOT_SIZE=64*1024,
-	MAX_SNAPSHOT_PACKSIZE=768
-};