about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--datasrc/client.dts1
-rw-r--r--datasrc/server.dts1
-rw-r--r--default.bam34
-rwxr-xr-xscripts/compiler.py4
-rw-r--r--scripts/netobj.py270
-rw-r--r--src/engine/client/ec_client.c58
-rw-r--r--src/engine/e_compression.c6
-rw-r--r--src/engine/e_engine.c10
-rw-r--r--src/engine/e_engine.h1
-rw-r--r--src/engine/e_if_other.h17
-rw-r--r--src/engine/e_network.c28
-rw-r--r--src/engine/e_packer.c68
-rw-r--r--src/engine/e_snapshot.c42
-rw-r--r--src/engine/e_snapshot.h5
-rw-r--r--src/engine/e_system.c53
-rw-r--r--src/engine/e_system.h6
-rw-r--r--src/engine/server/es_server.c18
-rw-r--r--src/game/client/gc_client.cpp183
-rw-r--r--src/game/client/gc_client.h32
-rw-r--r--src/game/client/gc_hooks.cpp112
-rw-r--r--src/game/client/gc_menu.cpp16
-rw-r--r--src/game/client/gc_render.cpp30
-rw-r--r--src/game/client/gc_render.h12
-rw-r--r--src/game/client/gc_render_obj.cpp57
-rw-r--r--src/game/g_game.cpp6
-rw-r--r--src/game/g_game.h6
-rw-r--r--src/game/g_layers.cpp2
-rw-r--r--src/game/g_protocol.def211
-rw-r--r--src/game/g_protocol.h5
-rw-r--r--src/game/server/gs_common.h12
-rw-r--r--src/game/server/gs_game.cpp8
-rw-r--r--src/game/server/gs_game_ctf.cpp6
-rw-r--r--src/game/server/gs_server.cpp99
-rw-r--r--src/tools/packetgen.c36
34 files changed, 1144 insertions, 311 deletions
diff --git a/datasrc/client.dts b/datasrc/client.dts
index edded536..93786646 100644
--- a/datasrc/client.dts
+++ b/datasrc/client.dts
@@ -81,4 +81,3 @@ const array:int image = images.*
 const array:int sprite = sprites.*.*
 const array:int anim = animations.*
 const array:int powerup = powerups.*
-const array:int gametype = playerstats.*
diff --git a/datasrc/server.dts b/datasrc/server.dts
index f141ede0..aa3e12fd 100644
--- a/datasrc/server.dts
+++ b/datasrc/server.dts
@@ -1,7 +1,6 @@
 const array:int sound = sounds.*
 const array:int weapon = weapons.*
 const array:int powerup = powerups.*
-const array:int gametype = playerstats.*
 
 struct weapon {
 	int firedelay = firedelay@1
diff --git a/default.bam b/default.bam
index edba03ab..79945986 100644
--- a/default.bam
+++ b/default.bam
@@ -5,6 +5,11 @@ if family == "windows" then
 	dc_compiler = "scripts\\compiler.py"
 end
 
+netobj_compiler = "python scripts/netobj.py"
+if family == "windows" then
+	netobj_compiler = "scripts\\netobj.py"
+end
+
 dat2c_compiler = "python scripts/dat2c.py"
 if family == "windows" then
 	dat2c_compiler = "scripts\\dat2c.py"
@@ -56,6 +61,15 @@ function dc_cdata(output, data, script)
 	return os.execute(dc_compiler .. " " .. data .. " " .. script .. " -c " .. output)
 end
 
+function netobj_source(output, proto)
+	print("netobj source " .. PathFilename(output) .. " = " .. PathFilename(proto))
+	return os.execute(netobj_compiler .. " source " .. proto .. " " .. output)
+end
+
+function netobj_header(output, proto)
+	print("netobj header " .. PathFilename(output) .. " = " .. PathFilename(proto))
+	return os.execute(netobj_compiler .. " header " .. proto .. " " .. output)
+end
 
 function CHash(output, ...)
 	local inputs = {}
@@ -91,6 +105,17 @@ function Dat2c(datafile, sourcefile, arrayname)
 	return sourcefile
 end
 
+function NetObjCompile(protofile, sourcefile, headerfile)
+	protofile = Path(protofile)
+	sourcefile = Path(sourcefile)
+	headerfile = Path(headerfile)
+	bam_add_job("netobj_source", sourcefile, protofile)
+	bam_add_job("netobj_header", headerfile, protofile)
+	bam_add_dependency(sourcefile, protofile)
+	bam_add_dependency(headerfile, protofile)
+	return {source = sourcefile, header=headerfile}
+end
+
 function DataCompile(datafile, scriptfile, headerfile, sourcefile, outputdatafile)
 	datafile = Path(datafile)
 	scriptfile = Path(scriptfile)
@@ -137,11 +162,16 @@ networkdata = DataCompile(
 		"datasrc/network.dts", 
 		"src/game/generated/g_protocol_ids.h",
 		"src/game/generated/g_protocol_ids.cpp")
+		
+netobj = NetObjCompile(
+		"src/game/g_protocol.def",
+		"src/game/generated/g_protocol.cpp",
+		"src/game/generated/g_protocol.h")
 
 nethash = CHash(
 	"src/game/generated/nethash.c",
 	"src/engine/e_protocol.h",
-	"src/game/g_protocol.h",
+	"src/game/generated/g_protocol.h",
 	"src/game/g_tuning.h",
 	"src/game/g_game.cpp", networkdata.header)
 
@@ -260,7 +290,7 @@ function build(settings)
 	server = Compile(server_settings, Collect("src/engine/server/*.c"))
 	
 	masterserver = Compile(settings, Collect("src/mastersrv/*.cpp"))
-	game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash)
+	game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash, netobj.source)
 	game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source, clientdata.cdata)
 	game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source, serverdata.cdata)
 	game_editor = Compile(settings, Collect("src/game/editor/*.cpp"))
diff --git a/scripts/compiler.py b/scripts/compiler.py
index b883398a..726785e0 100755
--- a/scripts/compiler.py
+++ b/scripts/compiler.py
@@ -487,8 +487,8 @@ class translator:
 		for s in self.structs:
 			s.emit_header_code(out)
 		print >>out, ""
-		print >>out, "data_container *load_data_from_file(const char *filename);"
-		print >>out, "data_container *load_data_from_memory(unsigned char *filename);"
+		print >>out, "struct data_container *load_data_from_file(const char *filename);"
+		print >>out, "struct data_container *load_data_from_memory(unsigned char *filename);"
 		print >>out, ""
 		
 
diff --git a/scripts/netobj.py b/scripts/netobj.py
new file mode 100644
index 00000000..107f49c4
--- /dev/null
+++ b/scripts/netobj.py
@@ -0,0 +1,270 @@
+import sys, os
+
+line_count = 0
+
+class variable:
+	name = "unknown"
+	def __init__(self, args, name):
+		global line_count
+		self.name = name
+		self.line = line_count
+	def emit_declaration(self):
+		return ["\tint %s;" % self.name]
+	def linedef(self):
+		return "#line %d" % self.line
+	def emit_secure(self):
+		return []
+
+class var_any(variable):
+	def __init__(self, args, name):
+		variable.__init__(self, args, name)
+
+class var_range(variable):
+	def __init__(self, args, name):
+		variable.__init__(self, args, name)
+		self.min = args[0]
+		self.max = args[1]
+	def emit_secure(self):
+		return [self.linedef(), "obj->%s = netobj_clamp_int(obj->%s, %s, %s);" % (self.name, self.name, self.min, self.max)]
+	
+class var_clientid(variable):
+	def __init__(self, args, name):
+		variable.__init__(self, args, name)
+	def emit_secure(self):
+		return [self.linedef(), "obj->%s = netobj_clamp_int(obj->%s, -1, MAX_CLIENTS);" % (self.name, self.name)]
+
+class var_string(variable):
+	def __init__(self, args, name):
+		variable.__init__(self, args, name)
+
+class object:
+	def __init__(self, line):
+		fields = line.split()
+		self.name = fields[1]
+		self.extends = None
+		if len(fields) == 4 and fields[2] == "extends":
+			self.extends = fields[3]
+		self.enum_name = "NETOBJTYPE_%s" % self.name.upper()
+		self.struct_name = "NETOBJ_%s" % self.name.upper()
+		self.members = []
+		
+	def parse(self, lines):
+		global line_count
+		for index in xrange(0, len(lines)):
+			line_count += 1
+			line = lines[index]
+			if not len(line):
+				continue
+				
+			if line == "end":
+				return lines[index+1:]
+			else:
+				# check for argument
+				fields = line.split(")", 1)
+				if len(fields) == 2:
+					names = [line.strip() for line in fields[1].split(",")]
+					l = fields[0].split("(", 1)
+					type = l[0]
+					args = [line.strip() for line in l[1].split(",")]
+				else:
+					l = fields[0].split(None, 1)
+					type = l[0]
+					args = []
+					names = [line.strip() for line in l[1].split(",")]
+					
+				for name in names:
+					create_string = 'var_%s(%s, "%s")' % (type, args, name)
+					new_member = eval(create_string)
+					self.members += [new_member]
+				
+		raise BaseException("Parse error")
+	
+	def emit_declaration(self):
+		lines = []
+		if self.extends:
+			lines += ["struct %s : public NETOBJ_%s\n {" % (self.struct_name, self.extends.upper())]
+		else:
+			lines += ["struct %s\n {" % self.struct_name]
+		for m in self.members:
+			lines += m.emit_declaration()
+		lines += ["};"]
+		return lines
+
+	def emit_secure(self):
+		lines = []
+		for m in self.members:
+			lines += m.emit_secure()
+		return lines
+		
+class event(object):
+	def __init__(self, line):
+		object.__init__(self, line)
+		self.enum_name = "NETEVENTTYPE_%s" % self.name.upper()
+		self.struct_name = "NETEVENT_%s" % self.name.upper()
+
+class raw_reader:
+	def __init__(self):
+		self.raw_lines = []
+	def parse(self, lines):
+		global line_count
+		for index in xrange(0, len(lines)):
+			line_count += 1
+			line = lines[index]
+			if not len(line):
+				continue
+				
+			if line == "end":
+				return lines[index+1:]
+			else:
+				self.raw_lines += [line]
+				
+		raise BaseException("Parse error")		
+
+class proto:
+	def __init__(self):
+		self.objects = []
+		self.source_raw = []
+		self.header_raw = []
+
+
+def load(filename):
+	# read the file
+	global line_count
+	line_count = 0
+	lines = [line.strip() for line in file(filename).readlines()]
+	
+	p = proto()
+
+	while len(lines):
+		line_count += 1
+		line = lines[0]
+		line = line.split("//", 2)[0] # strip comment
+		
+		if not len(line):
+			del lines[0]
+			continue
+			
+		fields = line.split(None, 1)
+		
+		del lines[0]
+		
+		if fields[0] == "object" or fields[0] == "msg":
+			new_obj = object(line)
+			lines = new_obj.parse(lines)
+			p.objects += [new_obj]
+		elif fields[0] == "event":
+			new_obj = event(line)
+			lines = new_obj.parse(lines)
+			p.objects += [new_obj]
+		elif fields[0] == "raw_source":
+			raw = raw_reader()
+			lines = raw.parse(lines)
+			p.source_raw += raw.raw_lines
+		elif fields[0] == "raw_header":
+			raw = raw_reader()
+			lines = raw.parse(lines)
+			p.header_raw += raw.raw_lines
+		else:
+			print "error, strange line:", line
+			
+	return p
+	
+def emit_header_file(f, p):
+	for l in p.header_raw:
+		print >>f, l
+		
+	if 1: # emit the enum table
+		print >>f, "enum {"
+		print >>f, "\tNETOBJTYPE_INVALID=0,"
+		for obj in p.objects:
+			print >>f, "\t%s," % obj.enum_name
+		print >>f, "\tNUM_NETOBJTYPES"
+		print >>f, "};"
+		print >>f, ""
+
+	print >>f, "int netobj_secure(int type, void *data, int size);"
+	print >>f, "const char *netobj_get_name(int type);"
+	print >>f, ""
+
+	for obj in p.objects:
+		for l in obj.emit_declaration():
+			print >>f, l
+		print >>f, ""
+			
+def emit_source_file(f, p, protofilename):
+	
+	
+	print >>f, "#line 1 \"%s\"" % os.path.abspath(protofilename) 
+	
+	for l in p.source_raw:
+		print >>f, l
+
+	print >>f, "static int netobj_clamp_int(int v, int min, int max)"
+	print >>f, "{"
+	print >>f, "if(v<min) return min;"
+	print >>f, "if(v>max) return max;"
+	print >>f, "return v;"
+	print >>f, "}"
+	print >>f, ""
+	
+	if 1: # names
+		print >>f, "static const char *object_names[] = {"
+		print >>f, "\t" + '"invalid",'
+		for obj in p.objects:
+			print >>f, '\t"%s",' % obj.name
+		print >>f, '\t""'
+		print >>f, "};"
+		print >>f, ""
+
+	if 1: # secure functions
+		print >>f, "static int secure_object_invalid(void *data, int size) { return 0; }"
+		for obj in p.objects:
+			print >>f, "static int secure_%s(void *data, int size)" % obj.name
+			print >>f, "{"
+			print >>f, "\t%s *obj = (%s *)data;" % (obj.struct_name, obj.struct_name)
+			print >>f, "\t(void)obj;" # to get rid of "unused variable" warning
+			print >>f, "\tif(size != sizeof(%s)) return -1;" % obj.struct_name
+			if obj.extends:
+				print >>f, "\tif(secure_%s(data, sizeof(NETOBJ_%s)) != 0) return -1;" % (obj.extends, obj.extends.upper())
+				
+			for l in obj.emit_secure():
+				print >>f, "\t" + l
+			print >>f, "\treturn 0;";
+			print >>f, "}"
+			print >>f, ""
+
+	if 1: # secure function table
+		print >>f, "typedef static int(*SECUREFUNC)(void *data, int size);"
+		print >>f, "static SECUREFUNC secure_funcs[] = {"
+		print >>f, "\t" + 'secure_object_invalid,'
+		for obj in p.objects:
+			print >>f, "\tsecure_%s," % obj.name
+		print >>f, "\t" + '0x0'
+		print >>f, "};"
+		print >>f, ""
+
+	if 1:
+		print >>f, "int netobj_secure(int type, void *data, int size)"
+		print >>f, "{"
+		print >>f, "\tif(type < 0 || type >= NUM_NETOBJTYPES) return -1;"
+		print >>f, "\treturn secure_funcs[type](data, size);"
+		print >>f, "};"
+		print >>f, ""
+
+	if 1:
+		print >>f, "const char *netobj_get_name(int type)"
+		print >>f, "{"
+		print >>f, "\tif(type < 0 || type >= NUM_NETOBJTYPES) return \"(invalid)\";"
+		print >>f, "\treturn object_names[type];"
+		print >>f, "};"
+		print >>f, ""
+	
+if sys.argv[1] == "header":
+	p = load(sys.argv[2])
+	emit_header_file(file(sys.argv[3], "w"), p)
+elif sys.argv[1] == "source":
+	p = load(sys.argv[2])
+	emit_source_file(file(sys.argv[3], "w"), p, sys.argv[2])
+else:
+	print "invalid command"
+	sys.exit(-1)
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index 7185c471..0ddcf7d9 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -211,30 +211,48 @@ enum
 	NUM_SNAPSHOT_TYPES=2
 };
 
+/* the game snapshots are modifiable by the game */
 SNAPSTORAGE snapshot_storage;
 static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES];
+
 static int recived_snapshots;
 static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE];
 
 /* --- */
 
-const void *snap_get_item(int snapid, int index, SNAP_ITEM *item)
+void *snap_get_item(int snapid, int index, SNAP_ITEM *item)
 {
 	SNAPSHOT_ITEM *i;
 	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	i = snapshot_get_item(snapshots[snapid]->snap, index);
+	i = snapshot_get_item(snapshots[snapid]->alt_snap, index);
+	item->datasize = snapshot_get_item_datasize(snapshots[snapid]->alt_snap, index);
 	item->type = snapitem_type(i);
 	item->id = snapitem_id(i);
 	return (void *)snapitem_data(i);
 }
 
-const void *snap_find_item(int snapid, int type, int id)
+void snap_invalidate_item(int snapid, int index)
+{
+	SNAPSHOT_ITEM *i;
+	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
+	i = snapshot_get_item(snapshots[snapid]->alt_snap, index);
+	if(i)
+	{
+		if((char *)i < (char *)snapshots[snapid]->alt_snap || (char *)i > (char *)snapshots[snapid]->alt_snap + snapshots[snapid]->snap_size)
+			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
+		if((char *)i >= (char *)snapshots[snapid]->snap && (char *)i < (char *)snapshots[snapid]->snap + snapshots[snapid]->snap_size)
+			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
+		i->type_and_id = -1;
+	}
+}
+
+void *snap_find_item(int snapid, int type, int id)
 {
 	/* TODO: linear search. should be fixed. */
 	int i;
 	for(i = 0; i < snapshots[snapid]->snap->num_items; i++)
 	{
-		SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i);
+		SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->alt_snap, i);
 		if(snapitem_type(itm) == type && snapitem_id(itm) == id)
 			return (void *)snapitem_data(itm);
 	}
@@ -762,6 +780,11 @@ static void client_process_packet(NETPACKET *packet)
 				int total_size = msg_unpack_int();
 				int size = msg_unpack_int();
 				const unsigned char *data = msg_unpack_raw(size);
+				
+				/* check fior errors */
+				if(msg_unpack_error() || size <= 0 || total_size <= 0)
+					return;
+				
 				io_write(mapdownload_file, data, size);
 				
 				mapdownload_totalsize = total_size;
@@ -834,7 +857,7 @@ static void client_process_packet(NETPACKET *packet)
 					crc = msg_unpack_int();
 					part_size = msg_unpack_int();
 				}
-
+				
 				data = (const char *)msg_unpack_raw(part_size);
 				
 				if(msg_unpack_error())
@@ -882,6 +905,7 @@ static void client_process_packet(NETPACKET *packet)
 						
 						complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size;
 
+						/* reset snapshoting */
 						snapshot_part = 0;
 						
 						/* find snapshot that we should use as delta */
@@ -891,7 +915,7 @@ static void client_process_packet(NETPACKET *packet)
 						/* find delta */
 						if(delta_tick >= 0)
 						{
-							int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot);
+							int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot, 0);
 							
 							if(deltashot_size < 0)
 							{
@@ -912,17 +936,28 @@ static void client_process_packet(NETPACKET *packet)
 						deltasize = sizeof(int)*3;
 
 						if(complete_size)
-						{
+						{	
+							int intsize;
 							int compsize = zerobit_decompress(snapshot_incomming_data, complete_size, tmpbuffer);
-							int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
+							
+							if(compsize < 0) /* failure during decompression, bail */
+								return;
+								
+							intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
+
+							if(intsize < 0) /* failure during decompression, bail */
+								return;
+
 							deltadata = tmpbuffer2;
 							deltasize = intsize;
 						}
-
-						/*dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); */
 						
+						/* unpack delta */
 						purgetick = delta_tick;
 						snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize);
+						if(snapsize < 0)
+							return;
+							
 						if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc)
 						{
 							if(config.debug)
@@ -950,10 +985,9 @@ static void client_process_packet(NETPACKET *packet)
 						if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick)
 							purgetick = snapshots[SNAP_PREV]->tick;
 						snapstorage_purge_until(&snapshot_storage, purgetick);
-						/*client_snapshot_purge_until(game_tick-50); */
 						
 						/* add new */
-						snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3);
+						snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3, 1);
 						
 						/* apply snapshot, cycle pointers */
 						recived_snapshots++;
diff --git a/src/engine/e_compression.c b/src/engine/e_compression.c
index fa7d7866..0a16d0b4 100644
--- a/src/engine/e_compression.c
+++ b/src/engine/e_compression.c
@@ -125,9 +125,8 @@ long zerobit_decompress(const void *src_, int size, void *dst_)
 	unsigned char *src = (unsigned char *)src_;
 	unsigned char *dst = (unsigned char *)dst_;
 	unsigned char *end = src + size;
-
 	
-	while(src != end)
+	while(src < end)
 	{
 		unsigned char bit = 0x80;
 		unsigned char mask = *src++;
@@ -140,6 +139,9 @@ long zerobit_decompress(const void *src_, int size, void *dst_)
 			else
 				*dst++ = 0;
 		}
+
+		if(src > end)
+			return -1;
 	}
 	
 	return (long)(dst-(unsigned char *)dst_);
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index c5cb3616..6fe61efe 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -1,6 +1,7 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include <engine/e_system.h>
 #include <engine/e_server_interface.h>
@@ -25,6 +26,15 @@ const char *engine_savepath(const char *filename, char *buffer, int max)
 }
 
 
+int engine_stress(float probability)
+{
+	if(!config.dbg_stress)
+		return 0;
+	if(rand()/(float)RAND_MAX < probability)
+		return 1;
+	return 0;
+}
+
 void engine_init(const char *appname)
 {
 	dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h
index 6974db1d..6818d96d 100644
--- a/src/engine/e_engine.h
+++ b/src/engine/e_engine.h
@@ -4,6 +4,7 @@ const char *engine_savepath(const char *filename, char *buffer, int max);
 void engine_init(const char *appname);
 void engine_parse_arguments(int argc, char **argv);
 void engine_writeconfig();
+int engine_stress(float probability);
 
 
 enum
diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h
index 931df64d..b3a61e7f 100644
--- a/src/engine/e_if_other.h
+++ b/src/engine/e_if_other.h
@@ -34,6 +34,7 @@ typedef struct
 {
 	int type;
 	int id;
+	int datasize;
 } SNAP_ITEM;
 
 /*
@@ -234,7 +235,7 @@ int snap_num_items(int snapid);
 	Returns:
 		Returns a pointer to the item if it exists, otherwise NULL.
 */
-const void *snap_get_item(int snapid, int index, SNAP_ITEM *item);
+void *snap_get_item(int snapid, int index, SNAP_ITEM *item);
 
 /*
 	Function: snap_find_item
@@ -250,7 +251,19 @@ const void *snap_get_item(int snapid, int index, SNAP_ITEM *item);
 	Returns:
 		Returns a pointer to the item if it exists, otherwise NULL.
 */
-const void *snap_find_item(int snapid, int type, int id);
+void *snap_find_item(int snapid, int type, int id);
+
+/*
+	Function: snap_invalidate_item
+		Marks an item as invalid byt setting type and id to 0xffffffff.
+	
+	Arguments:
+		snapid - Snapshot ID to the data to fetch.
+			* SNAP_PREV for previous snapshot.
+			* SNAP_CUR for current snapshot.
+		index - Index of the item.
+*/
+void snap_invalidate_item(int snapid, int index);
 
 /*
 	Function: snap_input
diff --git a/src/engine/e_network.c b/src/engine/e_network.c
index acf723ba..9211726f 100644
--- a/src/engine/e_network.c
+++ b/src/engine/e_network.c
@@ -54,7 +54,6 @@ typedef struct
 	unsigned char *data;
 } NETPACKETDATA;
 
-
 static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet)
 {
 	unsigned char buffer[NETWORK_MAX_PACKET_SIZE];
@@ -356,7 +355,18 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
 		conn->remote_closed = 1;
 		
 		if(p->data_size)
-			conn_set_error(conn, (char *)p->data);
+		{
+			/* make sure to sanitize the error string form the other party*/
+			char str[128];
+			if(p->data_size < 128)
+				str_copy(str, (char *)p->data, p->data_size);
+			else
+				str_copy(str, (char *)p->data, 128);
+			str_sanitize_strong(str);
+			
+			/* set the error string */
+			conn_set_error(conn, str);
+		}
 		else
 			conn_set_error(conn, "no reason given");
 		if(config.debug)
@@ -739,7 +749,7 @@ int netserver_recv(NETSERVER *s, NETPACKET *packet)
 		else
 		{
 			/* errornous packet, drop it */
-			dbg_msg("server", "crazy packet");
+			/* dbg_msg("server", "crazy packet"); */
 		}
 		
 		/* read header */
@@ -751,7 +761,11 @@ int netserver_recv(NETSERVER *s, NETPACKET *packet)
 
 int netserver_send(NETSERVER *s, NETPACKET *packet)
 {
-	dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big");
+	if(packet->data_size >= NETWORK_MAX_PAYLOAD)
+	{
+		dbg_msg("netserver", "packet payload too big. %d. dropping packet", packet->data_size);
+		return -1;
+	}
 	
 	if(packet->flags&PACKETFLAG_CONNLESS)
 	{
@@ -898,7 +912,11 @@ int netclient_recv(NETCLIENT *c, NETPACKET *packet)
 
 int netclient_send(NETCLIENT *c, NETPACKET *packet)
 {
-	dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big");
+	if(packet->data_size >= NETWORK_MAX_PAYLOAD)
+	{
+		dbg_msg("netclient", "packet payload too big. %d. dropping packet", packet->data_size);
+		return -1;
+	}
 	
 	if(packet->flags&PACKETFLAG_CONNLESS)
 	{
diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c
index 9e77927d..fa5d54cf 100644
--- a/src/engine/e_packer.c
+++ b/src/engine/e_packer.c
@@ -1,12 +1,39 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <stdlib.h>
 
 #include "e_system.h"
 #include "e_packer.h"
 #include "e_compression.h"
+#include "e_engine.h"
 
 /* useful for debugging */
-#define packing_error(p) p->error = 1
-/* #define packing_error(p) p->error = 1; dbg_break() */
+#if 0
+	#define packing_error(p) p->error = 1; dbg_break()
+#else
+	#define packing_error(p) p->error = 1
+#endif
+
+int stress_get_int()
+{
+	static const int nasty[] = {-1, 0, 1, 66000, -66000, (-1<<31), 0x7fffffff};
+	if(rand()&1)
+		return rand();
+	return nasty[rand()%6];
+}
+
+const char *stress_get_string(int *size)
+{
+	static char noise[1024];
+	int i;
+	int s;
+	s = (rand()%1024)-1;
+	for(i = 0; i < s; i++)
+		noise[i] = (rand()%254)+1;
+	noise[s] = 0;
+	if(size)
+		*size = s;
+	return noise;
+}
 
 void packer_reset(PACKER *p)
 {
@@ -19,6 +46,9 @@ void packer_add_int(PACKER *p, int i)
 {
 	if(p->error)
 		return;
+		
+	/*if(engine_stress(0.05f))
+		i = stress_get_int();*/
 	
 	/* make sure that we have space enough */
 	if(p->end - p->current < 6)
@@ -35,6 +65,15 @@ void packer_add_string(PACKER *p, const char *str, int limit)
 	if(p->error)
 		return;
 		
+	/* STRESS: do this better */
+	/*
+	if(engine_stress(0.1f))
+	{
+		str = stress_get_string(0);
+		limit = 0;
+	}*/
+	
+	/* */
 	if(limit > 0)
 	{
 		while(*str && limit != 0)
@@ -105,8 +144,14 @@ void unpacker_reset(UNPACKER *p, const unsigned char *data, int size)
 int unpacker_get_int(UNPACKER *p)
 {
 	int i;
-	if(p->error || p->current >= p->end)
+	if(p->error)
 		return 0;
+	if(p->current >= p->end)
+	{
+		packing_error(p);
+		return 0;
+	}
+	
 	p->current = vint_unpack(p->current, &i);
 	if(p->current > p->end)
 	{
@@ -118,11 +163,11 @@ int unpacker_get_int(UNPACKER *p)
 
 const char *unpacker_get_string(UNPACKER *p)
 {
-	const char *ptr;
+	char *ptr;
 	if(p->error || p->current >= p->end)
 		return "";
 		
-	ptr = (const char *)p->current;
+	ptr = (char *)p->current;
 	while(*p->current) /* skip the string */
 	{
 		p->current++;
@@ -133,17 +178,26 @@ const char *unpacker_get_string(UNPACKER *p)
 		}
 	}
 	p->current++;
+	
+	/* sanitize all strings */
+	str_sanitize(ptr);
 	return ptr;
 }
 
 const unsigned char *unpacker_get_raw(UNPACKER *p, int size)
 {
 	const unsigned char *ptr = p->current;
-	p->current += size;
-	if(p->current > p->end)
+	if(p->error)
+		return 0;
+	
+	/* check for nasty sizes */
+	if(size < 0 || p->current+size > p->end)
 	{
 		packing_error(p);
 		return 0;
 	}
+
+	/* "unpack" the data */	
+	p->current += size;
 	return ptr;
 }
diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c
index 6c908518..9dc64ae8 100644
--- a/src/engine/e_snapshot.c
+++ b/src/engine/e_snapshot.c
@@ -1,6 +1,7 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include <stdlib.h>
 #include "e_snapshot.h"
+#include "e_engine.h"
 #include "e_compression.h"
 #include "e_common_interface.h"
 
@@ -317,6 +318,8 @@ int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_
 	SNAPBUILD builder;
 	SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata;
 	int *data = (int *)delta->data;
+	int *end = (int *)(((char *)srcdata + data_size));
+	
 	SNAPSHOT_ITEM *fromitem;
 	int i, d, keep, itemsize;
 	int *deleted;
@@ -329,6 +332,8 @@ int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_
 	/* unpack deleted stuff */
 	deleted = data;
 	data += delta->num_deleted_items;
+	if(data > end)
+		return -1;
 
 	/* copy all non deleted stuff */
 	for(i = 0; i < from->num_items; i++)
@@ -358,11 +363,17 @@ int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_
 	/* unpack updated stuff */
 	for(i = 0; i < delta->num_update_items; i++)
 	{
+		if(data+3 > end)
+			return -1;
+		
 		itemsize = *data++;
 		type = *data++;
 		id = *data++;
 		snapshot_current = type;
 		
+		if(data+itemsize/4 > end)
+			return -1;
+		
 		key = (type<<16)|id;
 		
 		/* create the item if needed */
@@ -442,10 +453,16 @@ void snapstorage_purge_until(SNAPSTORAGE *ss, int tick)
 	ss->last = 0;
 }
 
-void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data)
+void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt)
 {
 	/* allocate memory for holder + snapshot_data */
-	SNAPSTORAGE_HOLDER *h = (SNAPSTORAGE_HOLDER *)mem_alloc(sizeof(SNAPSTORAGE_HOLDER)+data_size, 1);
+	SNAPSTORAGE_HOLDER *h;
+	int total_size = sizeof(SNAPSTORAGE_HOLDER)+data_size;
+	
+	if(create_alt)
+		total_size += data_size;
+	
+	h = (SNAPSTORAGE_HOLDER *)mem_alloc(total_size, 1);
 	
 	/* set data */
 	h->tick = tick;
@@ -453,6 +470,15 @@ void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, vo
 	h->snap_size = data_size;
 	h->snap = (SNAPSHOT*)(h+1);
 	mem_copy(h->snap, data, data_size);
+
+	if(create_alt) /* create alternative if wanted */
+	{
+		h->alt_snap = (SNAPSHOT*)(((char *)h->snap) + data_size);
+		mem_copy(h->alt_snap, data, data_size);
+	}
+	else
+		h->alt_snap = 0;
+		
 	
 	/* link */
 	h->next = 0;
@@ -464,7 +490,7 @@ void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, vo
 	ss->last = h;
 }
 
-int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data)
+int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data)
 {
 	SNAPSTORAGE_HOLDER *h = ss->first;
 	
@@ -476,6 +502,8 @@ int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data)
 				*tagtime = h->tagtime;
 			if(data)
 				*data = h->snap;
+			if(alt_data)
+				*alt_data = h->alt_snap;
 			return h->snap_size;
 		}
 		
@@ -524,6 +552,14 @@ int snapbuild_finish(SNAPBUILD *sb, void *snapdata)
 void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size)
 {
 	SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size);
+
+	if(engine_stress(0.01f))
+	{
+		size += ((rand()%5) - 2)*4;
+		if(size < 0)
+			size = 0;
+	}
+
 	mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size);
 	obj->type_and_id = (type<<16)|id;
 	sb->offsets[sb->num_items] = sb->data_size;
diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h
index 1396f6ee..9527cb24 100644
--- a/src/engine/e_snapshot.h
+++ b/src/engine/e_snapshot.h
@@ -52,6 +52,7 @@ typedef struct SNAPSTORAGE_HOLDER_t
 	
 	int snap_size;
 	SNAPSHOT *snap;
+	SNAPSHOT *alt_snap;
 } SNAPSTORAGE_HOLDER;
  
 typedef struct SNAPSTORAGE_t
@@ -63,8 +64,8 @@ typedef struct SNAPSTORAGE_t
 void snapstorage_init(SNAPSTORAGE *ss);
 void snapstorage_purge_all(SNAPSTORAGE *ss);
 void snapstorage_purge_until(SNAPSTORAGE *ss, int tick);
-void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data);
-int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data);
+void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt);
+int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data);
 
 /* SNAPBUILD */
 
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index d2fb4eeb..044b48a2 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -47,6 +47,10 @@
 extern "C" {
 #endif
 
+IOHANDLE io_stdin() { return (IOHANDLE)stdin; }
+IOHANDLE io_stdout() { return (IOHANDLE)stdout; }
+IOHANDLE io_stderr() { return (IOHANDLE)stderr; }
+
 IOHANDLE logfile = 0;
 
 void dbg_assert_imp(const char *filename, int line, int test, const char *msg)
@@ -462,19 +466,21 @@ int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
 
 int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr)
 {
-	struct hostent* ip = gethostbyname(hostname);
+	struct addrinfo hints;
+	struct addrinfo *result;
+	int e;
+	
+	mem_zero(&hints, sizeof(hints));
+	hints.ai_family = AF_INET;
 
-	if(ip && ip->h_length > 0)
-	{
-		addr->ip[0] = ip->h_addr_list[0][0];
-		addr->ip[1] = ip->h_addr_list[0][1];
-		addr->ip[2] = ip->h_addr_list[0][2];
-		addr->ip[3] = ip->h_addr_list[0][3];
-		addr->port = port;
-		return 0;
-	}
+	e = getaddrinfo(hostname, NULL, &hints, &result);
+	if(e != 0 || !result)
+		return -1;
 
-	return -1;
+	sockaddr_to_netaddr4(result->ai_addr, addr);
+	freeaddrinfo(result);
+	addr->port = port;
+	return 0;
 }
 
 NETSOCKET net_udp4_create(NETADDR4 bindaddr)
@@ -842,6 +848,31 @@ void str_format(char *buffer, int buffer_size, const char *format, ...)
 	buffer[buffer_size-1] = 0; /* assure null termination */
 }
 
+
+
+/* makes sure that the string only contains the characters between 32 and 127 */
+void str_sanitize_strong(char *str)
+{
+	while(*str)
+	{
+		*str &= 0x7f;
+		if(*str < 32)
+			*str = 32;
+		str++;
+	}
+}
+
+/* makes sure that the string only contains the characters between 32 and 255 + \r\n\t */
+void str_sanitize(char *str)
+{
+	while(*str)
+	{
+		if(*str < 32 && !(*str == '\r') && !(*str == '\n') && !(*str == '\t'))
+			*str = ' ';
+		str++;
+	}
+}
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/src/engine/e_system.h b/src/engine/e_system.h
index 1fd929e8..d524382b 100644
--- a/src/engine/e_system.h
+++ b/src/engine/e_system.h
@@ -526,6 +526,12 @@ void pstr_format(pstr *str, )*/
 void str_append(char *dst, const char *src, int dst_size);
 void str_copy(char *dst, const char *src, int dst_size);
 void str_format(char *buffer, int buffer_size, const char *format, ...);
+void str_sanitize_strong(char *str);
+void str_sanitize(char *str);
+
+IOHANDLE io_stdin();
+IOHANDLE io_stdout();
+IOHANDLE io_stderr();
 
 #ifdef __cplusplus
 }
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index d8b8d978..9fe75e39 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -371,14 +371,14 @@ static void server_do_snap()
 			snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED);
 			
 			/* save it the snapshot */
-			snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data);
+			snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0);
 			
 			/* find snapshot that we can preform delta against */
 			emptysnap.data_size = 0;
 			emptysnap.num_items = 0;
 			
 			{
-				deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot);
+				deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0);
 				if(deltashot_size >= 0)
 					delta_tick = clients[i].last_acked_snapshot;
 				else
@@ -625,15 +625,19 @@ static void server_process_client_packet(NETPACKET *packet)
 			int64 tagtime;
 
 			clients[cid].last_acked_snapshot = msg_unpack_int();
+			tick = msg_unpack_int();
+			size = msg_unpack_int();
+			
+			/* check for errors */
+			if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE)
+				return;
+			
 			if(clients[cid].last_acked_snapshot > 0)
 				clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL;
 				
-			if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0)
+			if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0)
 				clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq());
-			
-			tick = msg_unpack_int();
-			size = msg_unpack_int();
-			
+
 			input = &clients[cid].inputs[clients[cid].current_input];
 			input->timeleft = server_tick_start_time(tick)-time_get();
 			input->pred_tick = tick;
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 20d1df7c..8446d729 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -27,7 +27,7 @@ extern "C" {
 struct data_container *data = 0;
 int64 debug_firedelay = 0;
 
-player_input input_data = {0};
+NETOBJ_PLAYER_INPUT input_data = {0};
 int input_target_lock = 0;
 
 int chat_mode = CHATMODE_NONE;
@@ -40,11 +40,16 @@ tuning_params tuning;
 vec2 mouse_pos;
 vec2 local_character_pos;
 vec2 local_target_pos;
-const obj_player_character *local_character = 0;
-const obj_player_character *local_prev_character = 0;
-const obj_player_info *local_info = 0;
-const obj_flag *flags[2] = {0,0};
-const obj_game *gameobj = 0;
+
+/*
+const NETOBJ_PLAYER_CHARACTER *local_character = 0;
+const NETOBJ_PLAYER_CHARACTER *local_prev_character = 0;
+const NETOBJ_PLAYER_INFO *local_info = 0;
+const NETOBJ_FLAG *flags[2] = {0,0};
+const NETOBJ_GAME *gameobj = 0;
+*/
+
+snapstate netobjects;
 
 int picked_up_weapon = -1;
 
@@ -54,7 +59,7 @@ void client_data::update_render_info()
 	render_info = skin_info;
 
 	// force team colors
-	if(gameobj && gameobj->gametype != GAMETYPE_DM)
+	if(netobjects.gameobj && netobjects.gameobj->gametype != GAMETYPE_DM)
 	{
 		const int team_colors[2] = {65387, 10223467};
 		if(team >= 0 || team <= 1)
@@ -232,7 +237,7 @@ void chat_add_line(int client_id, int team, const char *line)
 		if(client_datas[client_id].team == -1)
 			chat_lines[chat_current_line].name_color = -1;
 
-		if(gameobj && gameobj->gametype != GAMETYPE_DM)
+		if(netobjects.gameobj && netobjects.gameobj->gametype != GAMETYPE_DM)
 		{
 			if(client_datas[client_id].team == 0)
 				chat_lines[chat_current_line].name_color = 0;
@@ -261,41 +266,40 @@ void process_events(int snaptype)
 		SNAP_ITEM item;
 		const void *data = snap_get_item(snaptype, index, &item);
 
-		if(item.type == EVENT_DAMAGEINDICATION)
+		if(item.type == NETEVENTTYPE_DAMAGEIND)
 		{
-			ev_damageind *ev = (ev_damageind *)data;
+			NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)data;
 			effect_damage_indicator(vec2(ev->x, ev->y), get_direction(ev->angle));
 		}
-		else if(item.type == EVENT_AIR_JUMP)
+		else if(item.type == NETEVENTTYPE_AIR_JUMP)
 		{
-			ev_common *ev = (ev_common *)data;
+			NETEVENT_COMMON *ev = (NETEVENT_COMMON *)data;
 			effect_air_jump(vec2(ev->x, ev->y));
 		}
-		else if(item.type == EVENT_EXPLOSION)
+		else if(item.type == NETEVENTTYPE_EXPLOSION)
 		{
-			ev_explosion *ev = (ev_explosion *)data;
+			NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)data;
 			effect_explosion(vec2(ev->x, ev->y));
 		}
-		else if(item.type == EVENT_SMOKE)
+		/*else if(item.type == EVENT_SMOKE)
 		{
-			ev_explosion *ev = (ev_explosion *)data;
+			EV_EXPLOSION *ev = (EV_EXPLOSION *)data;
 			vec2 p(ev->x, ev->y);
-		}
-		else if(item.type == EVENT_PLAYERSPAWN)
+		}*/
+		else if(item.type == NETEVENTTYPE_SPAWN)
 		{
-			ev_explosion *ev = (ev_explosion *)data;
+			NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)data;
 			effect_playerspawn(vec2(ev->x, ev->y));
 		}
-		else if(item.type == EVENT_DEATH)
+		else if(item.type == NETEVENTTYPE_DEATH)
 		{
-			ev_explosion *ev = (ev_explosion *)data;
+			NETEVENT_DEATH *ev = (NETEVENT_DEATH *)data;
 			effect_playerdeath(vec2(ev->x, ev->y));
 		}
-		else if(item.type == EVENT_SOUND_WORLD)
+		else if(item.type == NETEVENTTYPE_SOUND_WORLD)
 		{
-			ev_sound *ev = (ev_sound *)data;
-			if(ev->sound >= 0 && ev->sound < NUM_SOUNDS)
-				snd_play_random(CHN_WORLD, ev->sound, 1.0f, vec2(ev->x, ev->y));
+			NETEVENT_SOUND_WORLD *ev = (NETEVENT_SOUND_WORLD *)data;
+			snd_play_random(CHN_WORLD, ev->soundid, 1.0f, vec2(ev->x, ev->y));
 		}
 	}
 }
@@ -303,12 +307,7 @@ void process_events(int snaptype)
 void clear_object_pointers()
 {
 	// clear out the invalid pointers
-	local_character = 0;
-	local_prev_character = 0;
-	local_info = 0;
-	flags[0] = 0;
-	flags[1] = 0;
-	gameobj = 0;
+	mem_zero(&netobjects, sizeof(netobjects));
 }
 
 void send_info(bool start)
@@ -526,16 +525,16 @@ void render_goals(float x, float y, float w)
 
 	// render goals
 	//y = ystart+h-54;
-	if(gameobj && gameobj->time_limit)
+	if(netobjects.gameobj && netobjects.gameobj->time_limit)
 	{
 		char buf[64];
-		str_format(buf, sizeof(buf), "Time Limit: %d min", gameobj->time_limit);
+		str_format(buf, sizeof(buf), "Time Limit: %d min", netobjects.gameobj->time_limit);
 		gfx_text(0, x+w/2, y, 24.0f, buf, -1);
 	}
-	if(gameobj && gameobj->score_limit)
+	if(netobjects.gameobj && netobjects.gameobj->score_limit)
 	{
 		char buf[64];
-		str_format(buf, sizeof(buf), "Score Limit: %d", gameobj->score_limit);
+		str_format(buf, sizeof(buf), "Score Limit: %d", netobjects.gameobj->score_limit);
 		gfx_text(0, x+40, y, 24.0f, buf, -1);
 	}
 }
@@ -560,14 +559,14 @@ void render_spectators(float x, float y, float w)
 		SNAP_ITEM item;
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-		if(item.type == OBJTYPE_PLAYER_INFO)
+		if(item.type == NETOBJTYPE_PLAYER_INFO)
 		{
-			const obj_player_info *info = (const obj_player_info *)data;
+			const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
 			if(info->team == -1)
 			{
 				if(count)
 					strcat(buffer, ", ");
-				strcat(buffer, client_datas[info->clientid].name);
+				strcat(buffer, client_datas[info->cid].name);
 				count++;
 			}
 		}
@@ -595,7 +594,7 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 	// render title
 	if(!title)
 	{
-		if(gameobj->game_over)
+		if(netobjects.gameobj->game_over)
 			title = "Game Over";
 		else
 			title = "Score Board";
@@ -611,10 +610,11 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 	{
 		gfx_text(0, x+10, y, 48, title, -1);
 
-		if(gameobj)
+		if(netobjects.gameobj)
 		{
 			char buf[128];
-			str_format(buf, sizeof(buf), "%d", gameobj->teamscore[team&1]);
+			int score = team ? netobjects.gameobj->teamscore_blue : netobjects.gameobj->teamscore_red;
+			str_format(buf, sizeof(buf), "%d", score);
 			tw = gfx_text_width(0, 48, buf, -1);
 			gfx_text(0, x+w-tw-30, y, 48, buf, -1);
 		}
@@ -623,16 +623,16 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 	y += 54.0f;
 
 	// find players
-	const obj_player_info *players[MAX_CLIENTS] = {0};
+	const NETOBJ_PLAYER_INFO *players[MAX_CLIENTS] = {0};
 	int num_players = 0;
 	for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
 	{
 		SNAP_ITEM item;
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-		if(item.type == OBJTYPE_PLAYER_INFO)
+		if(item.type == NETOBJTYPE_PLAYER_INFO)
 		{
-			players[num_players] = (const obj_player_info *)data;
+			players[num_players] = (const NETOBJ_PLAYER_INFO *)data;
 			num_players++;
 		}
 	}
@@ -644,7 +644,7 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 		{
 			if(players[i]->score < players[i+1]->score)
 			{
-				const obj_player_info *tmp = players[i];
+				const NETOBJ_PLAYER_INFO *tmp = players[i];
 				players[i] = players[i+1];
 				players[i+1] = tmp;
 			}
@@ -660,7 +660,7 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 	// render player scores
 	for(int i = 0; i < num_players; i++)
 	{
-		const obj_player_info *info = players[i];
+		const NETOBJ_PLAYER_INFO *info = players[i];
 
 		// make sure that we render the correct team
 		if(team == -1 || info->team != team)
@@ -683,18 +683,19 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 		
 		if(config.cl_show_player_ids)
 		{
-			str_format(buf, sizeof(buf), "%d | %s", info->clientid, client_datas[info->clientid].name);
+			str_format(buf, sizeof(buf), "%d | %s", info->cid, client_datas[info->cid].name);
 			gfx_text(0, x+128, y, font_size, buf, -1);
 		}
 		else
-			gfx_text(0, x+128, y, font_size, client_datas[info->clientid].name, -1);
+			gfx_text(0, x+128, y, font_size, client_datas[info->cid].name, -1);
 
 		str_format(buf, sizeof(buf), "%4d", info->latency);
 		float tw = gfx_text_width(0, font_size, buf, -1);
 		gfx_text(0, x+w-tw-35, y, font_size, buf, -1);
 
 		// render avatar
-		if((flags[0] && flags[0]->carried_by == info->clientid) || (flags[1] && flags[1]->carried_by == info->clientid))
+		if((netobjects.flags[0] && netobjects.flags[0]->carried_by == info->cid) ||
+			(netobjects.flags[1] && netobjects.flags[1]->carried_by == info->cid))
 		{
 			gfx_blend_normal();
 			gfx_texture_set(data->images[IMAGE_GAME].id);
@@ -708,7 +709,7 @@ void render_scoreboard(float x, float y, float w, int team, const char *title)
 			gfx_quads_end();
 		}
 		
-		render_tee(&idlestate, &client_datas[info->clientid].render_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28));
+		render_tee(&idlestate, &client_datas[info->cid].render_info, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28));
 
 		
 		y += 50.0f;
@@ -739,21 +740,21 @@ void render_game()
 
 	if(config.cl_predict)
 	{
-		if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over))
+		if(!netobjects.local_character || (netobjects.local_character->health < 0) || (netobjects.gameobj && netobjects.gameobj->game_over))
 		{
 			// don't use predicted
 		}
 		else
 			local_character_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_predintratick());
 	}
-	else if(local_character && local_prev_character)
+	else if(netobjects.local_character && netobjects.local_prev_character)
 	{
 		local_character_pos = mix(
-			vec2(local_prev_character->x, local_prev_character->y),
-			vec2(local_character->x, local_character->y), client_intratick());
+			vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y),
+			vec2(netobjects.local_character->x, netobjects.local_character->y), client_intratick());
 	}
 	
-	if(local_info && local_info->team == -1)
+	if(netobjects.local_info && netobjects.local_info->team == -1)
 		spectate = true;
 
 	animstate idlestate;
@@ -1089,7 +1090,7 @@ void render_game()
 		gfx_quads_end();
 	}*/
 
-	if(local_character && !spectate && !(gameobj && gameobj->game_over))
+	if(netobjects.local_character && !spectate && !(netobjects.gameobj && netobjects.gameobj->game_over))
 	{
 		gfx_texture_set(data->images[IMAGE_GAME].id);
 		gfx_quads_begin();
@@ -1097,7 +1098,7 @@ void render_game()
 		// render cursor
 		if (!menu_active && !emoticon_selector_active)
 		{
-			select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_cursor);
+			select_sprite(data->weapons[netobjects.local_character->weapon%data->num_weapons].sprite_cursor);
 			float cursorsize = 64;
 			draw_sprite(local_target_pos.x, local_target_pos.y, cursorsize);
 		}
@@ -1113,10 +1114,10 @@ void render_game()
 		
 		// if weaponstage is active, put a "glow" around the stage ammo
 		select_sprite(SPRITE_TEE_BODY);
-		for (int i = 0; i < local_character->weaponstage; i++)
-			gfx_quads_drawTL(x+local_character->ammocount * 12 -i*12, y+22, 11, 11);
-		select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_proj);
-		for (int i = 0; i < min(local_character->ammocount, 10); i++)
+		for (int i = 0; i < netobjects.local_character->weaponstage; i++)
+			gfx_quads_drawTL(x+netobjects.local_character->ammocount * 12 -i*12, y+22, 11, 11);
+		select_sprite(data->weapons[netobjects.local_character->weapon%data->num_weapons].sprite_proj);
+		for (int i = 0; i < min(netobjects.local_character->ammocount, 10); i++)
 			gfx_quads_drawTL(x+i*12,y+24,10,10);
 
 		gfx_quads_end();
@@ -1127,7 +1128,7 @@ void render_game()
 
 		// render health
 		select_sprite(SPRITE_HEALTH_FULL);
-		for(; h < local_character->health; h++)
+		for(; h < netobjects.local_character->health; h++)
 			gfx_quads_drawTL(x+h*12,y,10,10);
 
 		select_sprite(SPRITE_HEALTH_EMPTY);
@@ -1137,7 +1138,7 @@ void render_game()
 		// render armor meter
 		h = 0;
 		select_sprite(SPRITE_ARMOR_FULL);
-		for(; h < local_character->armor; h++)
+		for(; h < netobjects.local_character->armor; h++)
 			gfx_quads_drawTL(x+h*12,y+12,10,10);
 
 		select_sprite(SPRITE_ARMOR_EMPTY);
@@ -1172,7 +1173,7 @@ void render_game()
 			// render victim tee
 			x -= 24.0f;
 			
-			if(gameobj && gameobj->gametype == GAMETYPE_CTF)
+			if(netobjects.gameobj && netobjects.gameobj->gametype == GAMETYPE_CTF)
 			{
 				if(killmsgs[r].mode_special&1)
 				{
@@ -1206,7 +1207,7 @@ void render_game()
 
 			if(killmsgs[r].victim != killmsgs[r].killer)
 			{
-				if(gameobj && gameobj->gametype == GAMETYPE_CTF)
+				if(netobjects.gameobj && netobjects.gameobj->gametype == GAMETYPE_CTF)
 				{
 					if(killmsgs[r].mode_special&2)
 					{
@@ -1305,34 +1306,34 @@ void render_game()
 	}
 
 	// render goals
-	if(gameobj)
+	if(netobjects.gameobj)
 	{
-		int gametype = gameobj->gametype;
+		int gametype = netobjects.gameobj->gametype;
 		
 		float whole = 300*gfx_screenaspect();
 		float half = whole/2.0f;
 		
 		gfx_mapscreen(0,0,300*gfx_screenaspect(),300);
-		if(!gameobj->sudden_death)
+		if(!netobjects.gameobj->sudden_death)
 		{
 			char buf[32];
 			int time = 0;
-			if(gameobj->time_limit)
+			if(netobjects.gameobj->time_limit)
 			{
-				time = gameobj->time_limit*60 - ((client_tick()-gameobj->round_start_tick)/client_tickspeed());
+				time = netobjects.gameobj->time_limit*60 - ((client_tick()-netobjects.gameobj->round_start_tick)/client_tickspeed());
 
-				if(gameobj->game_over)
+				if(netobjects.gameobj->game_over)
 					time  = 0;
 			}
 			else
-				time = (client_tick()-gameobj->round_start_tick)/client_tickspeed();
+				time = (client_tick()-netobjects.gameobj->round_start_tick)/client_tickspeed();
 
 			str_format(buf, sizeof(buf), "%d:%02d", time /60, time %60);
 			float w = gfx_text_width(0, 16, buf, -1);
 			gfx_text(0, half-w/2, 2, 16, buf, -1);
 		}
 
-		if(gameobj->sudden_death)
+		if(netobjects.gameobj->sudden_death)
 		{
 			const char *text = "Sudden Death";
 			float w = gfx_text_width(0, 16, text, -1);
@@ -1340,7 +1341,7 @@ void render_game()
 		}
 
 		// render small score hud
-		if(!(gameobj && gameobj->game_over) && (gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF))
+		if(!(netobjects.gameobj && netobjects.gameobj->game_over) && (gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF))
 		{
 			for(int t = 0; t < 2; t++)
 			{
@@ -1355,15 +1356,15 @@ void render_game()
 				gfx_quads_end();
 
 				char buf[32];
-				str_format(buf, sizeof(buf), "%d", gameobj->teamscore[t]);
+				str_format(buf, sizeof(buf), "%d", t?netobjects.gameobj->teamscore_blue:netobjects.gameobj->teamscore_red);
 				float w = gfx_text_width(0, 14, buf, -1);
 				
 				if(gametype == GAMETYPE_CTF)
 				{
 					gfx_text(0, whole-20-w/2+5, 300-40-15+t*20+2, 14, buf, -1);
-					if(flags[t])
+					if(netobjects.flags[t])
 					{
- 						if(flags[t]->carried_by == -2 || (flags[t]->carried_by == -1 && ((client_tick()/10)&1)))
+ 						if(netobjects.flags[t]->carried_by == -2 || (netobjects.flags[t]->carried_by == -1 && ((client_tick()/10)&1)))
  						{
 							gfx_blend_normal();
 							gfx_texture_set(data->images[IMAGE_GAME].id);
@@ -1376,9 +1377,9 @@ void render_game()
 							gfx_quads_drawTL(whole-40+5, 300-40-15+t*20+1, size/2, size);
 							gfx_quads_end();
 						}
-						else if(flags[t]->carried_by >= 0)
+						else if(netobjects.flags[t]->carried_by >= 0)
 						{
-							int id = flags[t]->carried_by%MAX_CLIENTS;
+							int id = netobjects.flags[t]->carried_by%MAX_CLIENTS;
 							const char *name = client_datas[id].name;
 							float w = gfx_text_width(0, 10, name, -1);
 							gfx_text(0, whole-40-5-w, 300-40-15+t*20+2, 10, name, -1);
@@ -1396,15 +1397,15 @@ void render_game()
 		}
 
 		// render warmup timer
-		if(gameobj->warmup)
+		if(netobjects.gameobj->warmup)
 		{
 			char buf[256];
 			float w = gfx_text_width(0, 24, "Warmup", -1);
 			gfx_text(0, 150*gfx_screenaspect()+-w/2, 50, 24, "Warmup", -1);
 
-			int seconds = gameobj->warmup/SERVER_TICK_SPEED;
+			int seconds = netobjects.gameobj->warmup/SERVER_TICK_SPEED;
 			if(seconds < 5)
-				str_format(buf, sizeof(buf), "%d.%d", seconds, (gameobj->warmup*10/SERVER_TICK_SPEED)%10);
+				str_format(buf, sizeof(buf), "%d.%d", seconds, (netobjects.gameobj->warmup*10/SERVER_TICK_SPEED)%10);
 			else
 				str_format(buf, sizeof(buf), "%d", seconds);
 			w = gfx_text_width(0, 24, buf, -1);
@@ -1439,12 +1440,12 @@ void render_game()
 		}
 	}
 	
-	if(config.debug && local_character && local_prev_character)
+	if(config.debug && netobjects.local_character && netobjects.local_prev_character)
 	{
 		gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
 		
-		float speed = distance(vec2(local_prev_character->x, local_prev_character->y),
-			vec2(local_character->x, local_character->y));
+		float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y),
+			vec2(netobjects.local_character->x, netobjects.local_character->y));
 		
 		char buf[512];
 		str_format(buf, sizeof(buf), "%.2f", speed/2);
@@ -1453,15 +1454,15 @@ void render_game()
 
 	// render score board
 	if(inp_key_pressed(KEY_TAB) || // user requested
-		(!spectate && (!local_character || local_character->health < 0)) || // not spectating and is dead
-		(gameobj && gameobj->game_over) // game over
+		(!spectate && (!netobjects.local_character || netobjects.local_character->health < 0)) || // not spectating and is dead
+		(netobjects.gameobj && netobjects.gameobj->game_over) // game over
 		)
 	{
 		gfx_mapscreen(0, 0, width, height);
 
 		float w = 650.0f;
 
-		if (gameobj && gameobj->gametype == GAMETYPE_DM)
+		if(netobjects.gameobj && netobjects.gameobj->gametype == GAMETYPE_DM)
 		{
 			render_scoreboard(width/2-w/2, 150.0f, w, 0, 0);
 			//render_scoreboard(gameobj, 0, 0, -1, 0);
@@ -1469,12 +1470,12 @@ void render_game()
 		else
 		{
 				
-			if(gameobj && gameobj->game_over)
+			if(netobjects.gameobj && netobjects.gameobj->game_over)
 			{
 				const char *text = "DRAW!";
-				if(gameobj->teamscore[0] > gameobj->teamscore[1])
+				if(netobjects.gameobj->teamscore_red > netobjects.gameobj->teamscore_blue)
 					text = "Red Team Wins!";
-				else if(gameobj->teamscore[1] > gameobj->teamscore[0])
+				else if(netobjects.gameobj->teamscore_blue > netobjects.gameobj->teamscore_red)
 					text = "Blue Team Wins!";
 					
 				float w = gfx_text_width(0, 92.0f, text, -1);
diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h
index 857a9088..93a90a86 100644
--- a/src/game/client/gc_client.h
+++ b/src/game/client/gc_client.h
@@ -20,11 +20,29 @@ extern vec2 local_character_pos;
 extern vec2 local_target_pos;
 
 // snap pointers
-extern const obj_player_character *local_character;
-extern const obj_player_character *local_prev_character;
-extern const obj_player_info *local_info;
-extern const obj_flag *flags[2];
-extern const obj_game *gameobj;
+struct snapstate
+{
+	const NETOBJ_PLAYER_CHARACTER *local_character;
+	const NETOBJ_PLAYER_CHARACTER *local_prev_character;
+	const NETOBJ_PLAYER_INFO *local_info;
+	const NETOBJ_FLAG *flags[2];
+	const NETOBJ_GAME *gameobj;
+
+	const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS];
+	const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS];
+	int num_players;
+};
+
+extern snapstate netobjects;
+
+/*
+extern const NETOBJ_PLAYER_CHARACTER *local_character;
+extern const NETOBJ_PLAYER_CHARACTER *local_prev_character;
+extern const NETOBJ_PLAYER_INFO *local_info;
+extern const NETOBJ_FLAG *flags[2];
+extern const NETOBJ_GAME *gameobj;
+* */
+
 extern tuning_params tuning;
 
 // predicted players
@@ -33,7 +51,7 @@ extern player_core predicted_player;
 
 // input
 extern int picked_up_weapon;
-extern player_input input_data;
+extern NETOBJ_PLAYER_INPUT input_data;
 extern int input_target_lock;
 
 // debug
@@ -45,7 +63,7 @@ enum
 	MAX_EXTRA_PROJECTILES=32,
 };
 
-extern obj_projectile extraproj_projectiles[MAX_EXTRA_PROJECTILES];
+extern NETOBJ_PROJECTILE extraproj_projectiles[MAX_EXTRA_PROJECTILES];
 extern int extraproj_num;
 
 void extraproj_reset();
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index a6391007..9292d08c 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -120,17 +120,17 @@ extern "C" void modc_predict()
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 		int client_id = item.id;
 
-		if(item.type == OBJTYPE_PLAYER_CHARACTER)
+		if(item.type == NETOBJTYPE_PLAYER_CHARACTER)
 		{
-			const obj_player_character *character = (const obj_player_character *)data;
+			const NETOBJ_PLAYER_CHARACTER *character = (const NETOBJ_PLAYER_CHARACTER *)data;
 			client_datas[client_id].predicted.world = &world;
 			world.players[client_id] = &client_datas[client_id].predicted;
 
 			client_datas[client_id].predicted.read(character);
 		}
-		else if(item.type == OBJTYPE_PLAYER_INFO)
+		else if(item.type == NETOBJTYPE_PLAYER_INFO)
 		{
-			const obj_player_info *info = (const obj_player_info *)data;
+			const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
 			if(info->local)
 				local_cid = client_id;
 		}
@@ -155,7 +155,7 @@ extern "C" void modc_predict()
 				// apply player input
 				int *input = client_get_input(tick);
 				if(input)
-					world.players[c]->input = *((player_input*)input);
+					world.players[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
 			}
 
 			world.players[c]->tick();
@@ -230,6 +230,23 @@ extern "C" void modc_newsnapshot()
 	static int snapshot_count = 0;
 	snapshot_count++;
 	
+	// secure snapshot
+	{
+		int num = snap_num_items(SNAP_CURRENT);
+		for(int index = 0; index < num; index++)
+		{
+			SNAP_ITEM item;
+			void *data = snap_get_item(SNAP_CURRENT, index, &item);
+			if(netobj_secure(item.type, data, item.datasize) != 0)
+			{
+				if(config.debug)
+					dbg_msg("game", "invalidated %d %d (%s) %d", index, item.type, netobj_get_name(item.type), item.id);
+				snap_invalidate_item(SNAP_CURRENT, index);
+			}
+		}
+	}
+	
+	
 	process_events(SNAP_CURRENT);
 
 	if(config.dbg_stress)
@@ -256,32 +273,32 @@ extern "C" void modc_newsnapshot()
 			SNAP_ITEM item;
 			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-			if(item.type == OBJTYPE_PLAYER_INFO)
+			if(item.type == NETOBJTYPE_PLAYER_INFO)
 			{
-				const obj_player_info *info = (const obj_player_info *)data;
+				const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
 				
-				client_datas[info->clientid].team = info->team;
+				client_datas[info->cid].team = info->team;
 				
 				if(info->local)
 				{
-					local_info = info;
-					const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id);
+					netobjects.local_info = info;
+					const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_CHARACTER, item.id);
 					if(data)
 					{
-						local_character = (const obj_player_character *)data;
-						local_character_pos = vec2(local_character->x, local_character->y);
+						netobjects.local_character = (const NETOBJ_PLAYER_CHARACTER *)data;
+						local_character_pos = vec2(netobjects.local_character->x, netobjects.local_character->y);
 
-						const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id);
+						const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_CHARACTER, item.id);
 						if(p)
-							local_prev_character = (obj_player_character *)p;
+							netobjects.local_prev_character = (NETOBJ_PLAYER_CHARACTER *)p;
 					}
 				}
 			}
-			else if(item.type == OBJTYPE_GAME)
-				gameobj = (obj_game *)data;
-			else if(item.type == OBJTYPE_FLAG)
+			else if(item.type == NETOBJTYPE_GAME)
+				netobjects.gameobj = (NETOBJ_GAME *)data;
+			else if(item.type == NETOBJTYPE_FLAG)
 			{
-				flags[item.id%2] = (const obj_flag *)data;
+				netobjects.flags[item.id%2] = (const NETOBJ_FLAG *)data;
 			}
 		}
 	}
@@ -394,7 +411,7 @@ extern "C" void modc_statechange(int state, int old)
 	}
 }
 
-obj_projectile extraproj_projectiles[MAX_EXTRA_PROJECTILES];
+NETOBJ_PROJECTILE extraproj_projectiles[MAX_EXTRA_PROJECTILES];
 int extraproj_num;
 
 void extraproj_reset()
@@ -409,6 +426,11 @@ extern "C" void modc_message(int msg)
 		int cid = msg_unpack_int();
 		int team = msg_unpack_int();
 		const char *message = msg_unpack_string();
+
+		/* check for errors and invalid inputs */
+		if(msg_unpack_error() || cid < 0 || cid >= MAX_CLIENTS)
+			return;
+			
 		dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message);
 		chat_add_line(cid, team, message);
 
@@ -423,10 +445,13 @@ extern "C" void modc_message(int msg)
 		
 		for(int k = 0; k < num; k++)
 		{
-			obj_projectile proj;
-			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+			NETOBJ_PROJECTILE proj;
+			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
 				((int *)&proj)[i] = msg_unpack_int();
 				
+			if(msg_unpack_error())
+				return;
+				
 			if(extraproj_num != MAX_EXTRA_PROJECTILES)
 			{
 				extraproj_projectiles[extraproj_num] = proj;
@@ -440,6 +465,10 @@ extern "C" void modc_message(int msg)
 		const char *name = msg_unpack_string();
 		const char *skinname = msg_unpack_string();
 		
+		/* check for errors and invalid inputs */
+		if(msg_unpack_error() || cid < 0 || cid >= MAX_CLIENTS)
+			return;
+		
 		strncpy(client_datas[cid].name, name, 64);
 		strncpy(client_datas[cid].skin_name, skinname, 64);
 		
@@ -466,13 +495,24 @@ extern "C" void modc_message(int msg)
 	}
 	else if(msg == MSG_TUNE_PARAMS)
 	{
-		int *params = (int *)&tuning;
+		// unpack the new tuning		
+		tuning_params new_tuning;
+		int *params = (int *)&new_tuning;
 		for(unsigned i = 0; i < sizeof(tuning_params)/sizeof(int); i++)
 			params[i] = msg_unpack_int();
+
+		// check for unpacking errors
+		if(msg_unpack_error())
+			return;
+			
+		// apply new tuning
+		tuning = new_tuning;
 	}
     else if(msg == MSG_WEAPON_PICKUP)
     {
         int weapon = msg_unpack_int();
+		if(msg_unpack_error())
+			return;
         picked_up_weapon = weapon+1;
     }
 	else if(msg == MSG_READY_TO_ENTER)
@@ -481,23 +521,41 @@ extern "C" void modc_message(int msg)
 	}
 	else if(msg == MSG_KILLMSG)
 	{
+		// unpack messages
+		killmsg msg;
+		msg.killer = msg_unpack_int();
+		msg.victim = msg_unpack_int();
+		msg.weapon = msg_unpack_int();
+		msg.mode_special = msg_unpack_int();
+		msg.tick = client_tick();
+
+		// check for unpacking errors
+		if(msg_unpack_error() || msg.killer >= MAX_CLIENTS || msg.victim >= MAX_CLIENTS || msg.weapon >= NUM_WEAPONS)
+			return;
+
+		// add the message
 		killmsg_current = (killmsg_current+1)%killmsg_max;
-		killmsgs[killmsg_current].killer = msg_unpack_int();
-		killmsgs[killmsg_current].victim = msg_unpack_int();
-		killmsgs[killmsg_current].weapon = msg_unpack_int();
-		killmsgs[killmsg_current].mode_special = msg_unpack_int();
-		killmsgs[killmsg_current].tick = client_tick();
+		killmsgs[killmsg_current] = msg;
 	}
 	else if (msg == MSG_EMOTICON)
 	{
+		// unpack
 		int cid = msg_unpack_int();
 		int emoticon = msg_unpack_int();
+
+		// check for errors
+		if(msg_unpack_error() || cid < 0 || cid >= MAX_CLIENTS)
+			return;
+
+		// apply
 		client_datas[cid].emoticon = emoticon;
 		client_datas[cid].emoticon_start = client_tick();
 	}
 	else if(msg == MSG_SOUND_GLOBAL)
 	{
 		int soundid = msg_unpack_int();
+		if(msg_unpack_error() || soundid < 0)
+			return;
 		snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0));
 	}
 }
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index 51333288..ec2cc2fe 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -27,10 +27,6 @@ extern "C" {
 
 extern data_container *data;
 
-// abit uglyness
-extern const obj_player_info *local_info;
-extern const obj_game *gameobj;
-
 extern bool menu_active;
 extern bool menu_game_active;
 
@@ -1614,9 +1610,9 @@ static void menu2_render_game(RECT main_view)
 	if(ui_do_button(&disconnect_button, "Disconnect", 0, &button, ui_draw_menu_button, 0))
 		client_disconnect();
 
-	if(local_info && gameobj)
+	if(netobjects.local_info && netobjects.gameobj)
 	{
-		if(local_info->team != -1)
+		if(netobjects.local_info->team != -1)
 		{
 			ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
 			ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
@@ -1628,9 +1624,9 @@ static void menu2_render_game(RECT main_view)
 			}
 		}
 		
-		if(gameobj->gametype == GAMETYPE_DM)
+		if(netobjects.gameobj->gametype == GAMETYPE_DM)
 		{
-			if(local_info->team != 0)
+			if(netobjects.local_info->team != 0)
 			{
 				ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
 				ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
@@ -1644,7 +1640,7 @@ static void menu2_render_game(RECT main_view)
 		}
 		else
 		{
-			if(local_info->team != 0)
+			if(netobjects.local_info->team != 0)
 			{
 				ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
 				ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
@@ -1656,7 +1652,7 @@ static void menu2_render_game(RECT main_view)
 				}
 			}
 
-			if(local_info->team != 1)
+			if(netobjects.local_info->team != 1)
 			{
 				ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
 				ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp
index 1d276ec3..6e001bd1 100644
--- a/src/game/client/gc_render.cpp
+++ b/src/game/client/gc_render.cpp
@@ -349,25 +349,25 @@ static void render_items()
 		SNAP_ITEM item;
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-		if(item.type == OBJTYPE_PROJECTILE)
+		if(item.type == NETOBJTYPE_PROJECTILE)
 		{
-			render_projectile((const obj_projectile *)data, item.id);
+			render_projectile((const NETOBJ_PROJECTILE *)data, item.id);
 		}
-		else if(item.type == OBJTYPE_POWERUP)
+		else if(item.type == NETOBJTYPE_POWERUP)
 		{
 			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
 			if(prev)
-				render_powerup((const obj_powerup *)prev, (const obj_powerup *)data);
+				render_powerup((const NETOBJ_POWERUP *)prev, (const NETOBJ_POWERUP *)data);
 		}
-		else if(item.type == OBJTYPE_LASER)
+		else if(item.type == NETOBJTYPE_LASER)
 		{
-			render_laser((const obj_laser *)data);
+			render_laser((const NETOBJ_LASER *)data);
 		}
-		else if(item.type == OBJTYPE_FLAG)
+		else if(item.type == NETOBJTYPE_FLAG)
 		{
 			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
 			if (prev)
-				render_flag((const obj_flag *)prev, (const obj_flag *)data);
+				render_flag((const NETOBJ_FLAG *)prev, (const NETOBJ_FLAG *)data);
 		}
 	}
 
@@ -393,19 +393,19 @@ static void render_players()
 		SNAP_ITEM item;
 		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
 
-		if(item.type == OBJTYPE_PLAYER_CHARACTER)
+		if(item.type == NETOBJTYPE_PLAYER_CHARACTER)
 		{
 			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
-			const void *prev_info = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_INFO, item.id);
-			const void *info = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_INFO, item.id);
+			const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id);
+			const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id);
 
 			if(prev && prev_info && info)
 			{
 				render_player(
-						(const obj_player_character *)prev,
-						(const obj_player_character *)data,
-						(const obj_player_info *)prev_info,
-						(const obj_player_info *)info
+						(const NETOBJ_PLAYER_CHARACTER *)prev,
+						(const NETOBJ_PLAYER_CHARACTER *)data,
+						(const NETOBJ_PLAYER_INFO *)prev_info,
+						(const NETOBJ_PLAYER_INFO *)info
 					);
 			}
 		}
diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h
index b62206f7..a6f57b7b 100644
--- a/src/game/client/gc_render.h
+++ b/src/game/client/gc_render.h
@@ -54,13 +54,13 @@ void render_particles();
 
 // object render methods (gc_render_obj.cpp)
 void render_tee(class animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos);
-void render_flag(const struct obj_flag *prev, const struct obj_flag *current);
-void render_powerup(const struct obj_powerup *prev, const struct obj_powerup *current);
-void render_projectile(const struct obj_projectile *current, int itemid);
-void render_laser(const struct obj_laser *current);
+void render_flag(const struct NETOBJ_FLAG *prev, const struct NETOBJ_FLAG *current);
+void render_powerup(const struct NETOBJ_POWERUP *prev, const struct NETOBJ_POWERUP *current);
+void render_projectile(const struct NETOBJ_PROJECTILE *current, int itemid);
+void render_laser(const struct NETOBJ_LASER *current);
 void render_player(
-	const struct obj_player_character *prev_char, const struct obj_player_character *player_char,
-	const struct obj_player_info *prev_info, const struct obj_player_info *player_info);
+	const struct NETOBJ_PLAYER_CHARACTER *prev_char, const struct NETOBJ_PLAYER_CHARACTER *player_char,
+	const struct NETOBJ_PLAYER_INFO *prev_info, const struct NETOBJ_PLAYER_INFO *player_info);
 	
 // map render methods (gc_render_map.cpp)
 void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result);
diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp
index cedfc129..995994ed 100644
--- a/src/game/client/gc_render_obj.cpp
+++ b/src/game/client/gc_render_obj.cpp
@@ -11,7 +11,7 @@
 #include "gc_client.h"
 
 
-void render_projectile(const obj_projectile *current, int itemid)
+void render_projectile(const NETOBJ_PROJECTILE *current, int itemid)
 {
 	if(debug_firedelay)
 	{
@@ -34,7 +34,7 @@ void render_projectile(const obj_projectile *current, int itemid)
 	vec2 pos = calc_pos(startpos, startvel, gravity, ct);
 	vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f);
 
-	select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj);
+	select_sprite(data->weapons[clamp(current->type, 0, NUM_WEAPONS-1)].sprite_proj);
 	vec2 vel = pos-prevpos;
 	//vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
 	
@@ -63,7 +63,7 @@ void render_projectile(const obj_projectile *current, int itemid)
 	gfx_quads_end();
 }
 
-void render_powerup(const obj_powerup *prev, const obj_powerup *current)
+void render_powerup(const NETOBJ_POWERUP *prev, const NETOBJ_POWERUP *current)
 {
 	gfx_texture_set(data->images[IMAGE_GAME].id);
 	gfx_quads_begin();
@@ -73,8 +73,8 @@ void render_powerup(const obj_powerup *prev, const obj_powerup *current)
 	if (current->type == POWERUP_WEAPON)
 	{
 		angle = 0; //-pi/6;//-0.25f * pi * 2.0f;
-		select_sprite(data->weapons[current->subtype%data->num_weapons].sprite_body);
-		size = data->weapons[current->subtype%data->num_weapons].visual_size;
+		select_sprite(data->weapons[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body);
+		size = data->weapons[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size;
 	}
 	else
 	{
@@ -107,7 +107,7 @@ void render_powerup(const obj_powerup *prev, const obj_powerup *current)
 	gfx_quads_end();
 }
 
-void render_flag(const obj_flag *prev, const obj_flag *current)
+void render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current)
 {
 	float angle = 0.0f;
 	float size = 42.0f;
@@ -125,7 +125,7 @@ void render_flag(const obj_flag *prev, const obj_flag *current)
 
 	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
 
-	if(local_info && current->carried_by == local_info->clientid)
+	if(netobjects.local_info && current->carried_by == netobjects.local_info->cid)
 		pos = local_character_pos;
 
     //gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1);
@@ -135,7 +135,7 @@ void render_flag(const obj_flag *prev, const obj_flag *current)
 }
 
 
-void render_laser(const struct obj_laser *current)
+void render_laser(const struct NETOBJ_LASER *current)
 {
 
 	vec2 pos = vec2(current->x, current->y);
@@ -245,19 +245,19 @@ static void render_hand(tee_render_info *info, vec2 center_pos, vec2 dir, float
 }
 
 void render_player(
-	const obj_player_character *prev_char,
-	const obj_player_character *player_char,
-	const obj_player_info *prev_info,
-	const obj_player_info *player_info
+	const NETOBJ_PLAYER_CHARACTER *prev_char,
+	const NETOBJ_PLAYER_CHARACTER *player_char,
+	const NETOBJ_PLAYER_INFO *prev_info,
+	const NETOBJ_PLAYER_INFO *player_info
 	)
 {
-	obj_player_character prev;
-	obj_player_character player;
+	NETOBJ_PLAYER_CHARACTER prev;
+	NETOBJ_PLAYER_CHARACTER player;
 	prev = *prev_char;
 	player = *player_char;
 
-	obj_player_info info = *player_info;
-	tee_render_info render_info = client_datas[info.clientid].render_info;
+	NETOBJ_PLAYER_INFO info = *player_info;
+	tee_render_info render_info = client_datas[info.cid].render_info;
 
 	float intratick = client_intratick();
 	float ticktime = client_ticktime();
@@ -267,7 +267,7 @@ void render_player(
 
 	if(info.local && config.cl_predict)
 	{
-		if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over))
+		if(!netobjects.local_character || (netobjects.local_character->health < 0) || (netobjects.gameobj && netobjects.gameobj->game_over))
 		{
 		}
 		else
@@ -329,7 +329,7 @@ void render_player(
 		
 		if(player_char->hooked_player != -1)
 		{
-			if(local_info && player_char->hooked_player == local_info->clientid)
+			if(netobjects.local_info && player_char->hooked_player == netobjects.local_info->cid)
 			{
 				hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y),
 					vec2(predicted_player.pos.x, predicted_player.pos.y), client_predintratick());
@@ -351,7 +351,8 @@ void render_player(
 
 		// render chain
 		select_sprite(SPRITE_HOOK_CHAIN);
-		for(float f = 24; f < d; f += 24)
+		int i = 0;
+		for(float f = 24; f < d && i < 1024; f += 24, i++)
 		{
 			vec2 p = hook_pos + dir*f;
 			gfx_quads_draw(p.x, p.y,24,16);
@@ -360,7 +361,7 @@ void render_player(
 		gfx_quads_setrotation(0);
 		gfx_quads_end();
 
-		render_hand(&client_datas[info.clientid].render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0));
+		render_hand(&client_datas[info.cid].render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0));
 	}
 
 	// draw gun
@@ -478,9 +479,9 @@ void render_player(
 
 		switch (player.weapon)
 		{
-			case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
-			case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break;
-			case WEAPON_GRENADE: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
+			case WEAPON_GUN: render_hand(&client_datas[info.cid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
+			case WEAPON_SHOTGUN: render_hand(&client_datas[info.cid].render_info, p, direction, -pi/2, vec2(-5, 4)); break;
+			case WEAPON_GRENADE: render_hand(&client_datas[info.cid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
 		}
 
 	}
@@ -507,13 +508,13 @@ void render_player(
 		gfx_quads_end();
 	}
 
-	if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
+	if (client_datas[info.cid].emoticon_start != -1 && client_datas[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick())
 	{
 		gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
 		gfx_quads_begin();
 
-		int since_start = client_tick() - client_datas[info.clientid].emoticon_start;
-		int from_end = client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
+		int since_start = client_tick() - client_datas[info.cid].emoticon_start;
+		int from_end = client_datas[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick();
 
 		float a = 1;
 
@@ -534,7 +535,7 @@ void render_player(
 
 		gfx_setcolor(1.0f,1.0f,1.0f,a);
 		// client_datas::emoticon is an offset from the first emoticon
-		select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon);
+		select_sprite(SPRITE_OOP + client_datas[info.cid].emoticon);
 		gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
 		gfx_quads_end();
 	}
@@ -547,7 +548,7 @@ void render_player(
 		if(config.cl_nameplates_always == 0)
 			a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f);
 			
-		const char *name = client_datas[info.clientid].name;
+		const char *name = client_datas[info.cid].name;
 		float tw = gfx_text_width(0, 28.0f, name, -1);
 		gfx_text_color(1,1,1,a);
 		gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1);
diff --git a/src/game/g_game.cpp b/src/game/g_game.cpp
index 25114cb7..c217b5a3 100644
--- a/src/game/g_game.cpp
+++ b/src/game/g_game.cpp
@@ -400,7 +400,7 @@ void player_core::move()
 	move_box(&pos, &vel, vec2(28.0f, 28.0f), 0);
 }
 
-void player_core::write(obj_player_core *obj_core)
+void player_core::write(NETOBJ_PLAYER_CORE *obj_core)
 {
 	obj_core->x = (int)pos.x;
 	obj_core->y = (int)pos.y;
@@ -427,7 +427,7 @@ void player_core::write(obj_player_core *obj_core)
 	obj_core->angle = (int)(a*256.0f);
 }
 
-void player_core::read(const obj_player_core *obj_core)
+void player_core::read(const NETOBJ_PLAYER_CORE *obj_core)
 {
 	pos.x = obj_core->x;
 	pos.y = obj_core->y;
@@ -445,7 +445,7 @@ void player_core::read(const obj_player_core *obj_core)
 
 void player_core::quantize()
 {
-	obj_player_core c;
+	NETOBJ_PLAYER_CORE c;
 	write(&c);
 	read(&c);
 }
diff --git a/src/game/g_game.h b/src/game/g_game.h
index a9fddde7..00870319 100644
--- a/src/game/g_game.h
+++ b/src/game/g_game.h
@@ -133,15 +133,15 @@ public:
 	int hooked_player;
 	
 	int jumped;
-	player_input input;
+	NETOBJ_PLAYER_INPUT input;
 	
 	int triggered_events;
 	
 	void tick();
 	void move();
 	
-	void read(const obj_player_core *obj_core);
-	void write(obj_player_core *obj_core);
+	void read(const NETOBJ_PLAYER_CORE *obj_core);
+	void write(NETOBJ_PLAYER_CORE *obj_core);
 	void quantize();
 };
 
diff --git a/src/game/g_layers.cpp b/src/game/g_layers.cpp
index 1595e266..6614bc90 100644
--- a/src/game/g_layers.cpp
+++ b/src/game/g_layers.cpp
@@ -33,12 +33,10 @@ void layers_init()
 				
 				if(tilemap->flags&1)
 				{
-					dbg_msg("layers", "game");
 					game_layer = tilemap;
 					p = 2;
 				}
 			}
-			dbg_msg("layers", "%d %d", i, layer->type);
 		}
 	}
 }
diff --git a/src/game/g_protocol.def b/src/game/g_protocol.def
new file mode 100644
index 00000000..8df707c3
--- /dev/null
+++ b/src/game/g_protocol.def
@@ -0,0 +1,211 @@
+
+raw_source
+	#include "g_protocol.h"
+	#include "g_protocol_ids.h"
+	#include <engine/e_common_interface.h>
+	#define max_int 100000
+end
+
+raw_header
+	enum
+	{
+		// emotes
+		EMOTE_NORMAL=0,
+		EMOTE_PAIN,
+		EMOTE_HAPPY,
+		EMOTE_SURPRISE,
+		EMOTE_ANGRY,
+		EMOTE_BLINK,
+		NUM_EMOTES,
+		
+		// playerstates
+		PLAYERSTATE_UNKNOWN=0,
+		PLAYERSTATE_PLAYING,
+		PLAYERSTATE_IN_MENU,
+		PLAYERSTATE_CHATTING,
+		NUM_PLAYERSTATES,
+
+		// game types
+		GAMETYPE_DM=0,
+		GAMETYPE_TDM,
+		GAMETYPE_CTF,
+		NUM_GAMETYPES,
+		
+		// other stuff
+		INPUT_STATE_MASK=0x1f,
+	};
+	
+	enum
+	{
+		MSG_NULL=0,
+		MSG_SAY, // client -> server
+		MSG_CHAT, // server -> client
+		MSG_SETINFO, // server -> client - contains name, skin and color info
+		MSG_KILLMSG, // server -> client
+		MSG_SETTEAM,
+		MSG_JOIN,
+		MSG_QUIT,
+		MSG_EMOTICON,
+		MSG_STARTINFO, // client -> server
+		MSG_CHANGEINFO, // client -> server
+		MSG_READY_TO_ENTER, // server -> client
+		MSG_WEAPON_PICKUP,
+		MSG_SOUND_GLOBAL,
+		MSG_TUNE_PARAMS,
+		MSG_KILL,
+		MSG_EXTRA_PROJECTILE, // server -> client
+		
+	};
+	
+end
+
+
+
+object player_input
+	any left
+	any right
+
+	any target_x
+	any target_y
+
+	any jump
+	any fire
+	any hook
+	any blink
+
+	any player_state
+
+	any wanted_weapon
+	any next_weapon
+	any prev_weapon
+end
+
+object projectile
+	any x, y
+	any vx, vy
+	range(0, NUM_WEAPONS) type
+	range(0, max_int) start_tick
+end
+
+object laser
+	any x
+	any y
+	any from_x
+	any from_y
+	range(0, max_int) eval_tick
+end
+
+object powerup
+	any x, y
+	range(0, max_int) type
+	range(0, max_int) subtype
+end
+
+object flag
+	any x, y
+	range(0, 1) team
+	clientid carried_by
+end
+
+object game
+	range(0, max_int) round_start_tick
+	
+	range(0, 1) game_over
+	range(0, 1) sudden_death
+	range(0, 1) paused
+
+	range(0, max_int) score_limit
+	range(0, max_int) time_limit
+	range(0, NUM_GAMETYPES-1) gametype
+
+	range(0, max_int) warmup
+
+	any teamscore_red
+	any teamscore_blue
+end
+
+// core object needed for physics
+object player_core
+	any x, y
+	any vx, vy
+
+	any angle
+	range(0, 2) jumped
+
+	clientid hooked_player
+	range(0, 3) hook_state
+	range(0, max_int) hook_tick
+
+	any hook_x
+	any hook_y
+	any hook_dx
+	any hook_dy
+end
+
+// info about the player that is only needed when it's on screen
+object player_character extends player_core
+	range(0, NUM_PLAYERSTATES-1) player_state
+	
+	range(0, 10) health
+	range(0, 10) armor
+	range(0, 10) ammocount
+	range(0, 10) weaponstage
+
+	range(0, NUM_WEAPONS-1) weapon
+	range(0, NUM_EMOTES-1) emote
+	
+	range(0, max_int) attacktick
+end
+
+// information about the player that is always needed
+object player_info
+	range(0, 1) local
+	clientid cid
+	range(-1, 1) team
+	
+	any score
+	
+	any latency
+	any latency_flux
+end
+
+event common
+	any x, y
+end
+
+event explosion
+	any x, y
+end
+
+event spawn
+	any x, y
+end
+
+event death
+	any x, y
+end
+
+event air_jump
+	any x, y
+end
+
+event sound_global
+	any x, y
+	range(0, NUM_SOUNDS-1) soundid
+end
+
+event sound_world
+	any x, y
+	range(0, NUM_SOUNDS-1) soundid
+end
+
+event damageind
+	any x, y
+	any angle
+end
+
+//msg say
+//	clientid cid
+//	range(-1, 1) team
+//	string message
+//end
diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h
index f4878729..195f5ae2 100644
--- a/src/game/g_protocol.h
+++ b/src/game/g_protocol.h
@@ -4,7 +4,10 @@
 #ifndef GAME_PROTOCOL_H
 #define GAME_PROTOCOL_H
 
+#include <game/generated/g_protocol.h>
+
 // Network stuff
+/*
 enum
 {
 	OBJTYPE_NULL=0,
@@ -209,6 +212,6 @@ struct obj_player_info
 	int score;
 	int latency;
 	int latency_flux;
-};
+};*/
 
 #endif
diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h
index 194751b2..ffeadecf 100644
--- a/src/game/server/gs_common.h
+++ b/src/game/server/gs_common.h
@@ -204,7 +204,7 @@ public:
 	projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner,
 		int damage, int flags, float force, int sound_impact, int weapon);
 
-	void fill_info(obj_projectile *proj);
+	void fill_info(NETOBJ_PROJECTILE *proj);
 
 	virtual void reset();
 	virtual void tick();
@@ -270,12 +270,12 @@ public:
 	int color_feet;
 
 	// these are non-heldback inputs
-	player_input latest_previnput;
-	player_input latest_input;
+	NETOBJ_PLAYER_INPUT latest_previnput;
+	NETOBJ_PLAYER_INPUT latest_input;
 
 	// input	
-	player_input previnput;
-	player_input input;
+	NETOBJ_PLAYER_INPUT previnput;
+	NETOBJ_PLAYER_INPUT input;
 	int num_inputs;
 	int jumped;
 	
@@ -332,7 +332,7 @@ public:
 	int handle_weapons();
 	int handle_ninja();
 	
-	void on_direct_input(player_input *input);
+	void on_direct_input(NETOBJ_PLAYER_INPUT *input);
 	void fire_weapon();
 
 	virtual void tick();
diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp
index a1b5fd7b..228ae828 100644
--- a/src/game/server/gs_game.cpp
+++ b/src/game/server/gs_game.cpp
@@ -6,7 +6,7 @@
 #include "gs_common.h"
 
 gameobject::gameobject()
-: entity(OBJTYPE_GAME)
+: entity(NETOBJTYPE_GAME)
 {
 	// select gametype
 	if(strcmp(config.sv_gametype, "ctf") == 0)
@@ -260,7 +260,7 @@ void gameobject::tick()
 
 void gameobject::snap(int snapping_client)
 {
-	obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game));
+	NETOBJ_GAME *game = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME));
 	game->paused = world->paused;
 	game->game_over = game_over_tick==-1?0:1;
 	game->sudden_death = sudden_death;
@@ -272,8 +272,8 @@ void gameobject::snap(int snapping_client)
 	
 	game->warmup = warmup;
 	
-	game->teamscore[0] = teamscore[0];
-	game->teamscore[1] = teamscore[1];
+	game->teamscore_red = teamscore[0];
+	game->teamscore_blue = teamscore[1];
 }
 
 int gameobject::getteam(int notthisid)
diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gs_game_ctf.cpp
index 37e2baba..a703e6b6 100644
--- a/src/game/server/gs_game_ctf.cpp
+++ b/src/game/server/gs_game_ctf.cpp
@@ -99,7 +99,7 @@ void gameobject_ctf::tick()
 		else
 		{
 			player *close_players[MAX_CLIENTS];
-			int types[] = {OBJTYPE_PLAYER_CHARACTER};
+			int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
 			int num = world->find_entities(f->pos, 32.0f, (entity**)close_players, MAX_CLIENTS, types, 1);
 			for(int i = 0; i < num; i++)
 			{
@@ -161,7 +161,7 @@ void gameobject_ctf::tick()
 
 // Flag
 flag::flag(int _team)
-: entity(OBJTYPE_FLAG)
+: entity(NETOBJTYPE_FLAG)
 {
 	team = _team;
 	proximity_radius = phys_size;
@@ -183,7 +183,7 @@ void flag::reset()
 
 void flag::snap(int snapping_client)
 {
-	obj_flag *flag = (obj_flag *)snap_new_item(OBJTYPE_FLAG, team, sizeof(obj_flag));
+	NETOBJ_FLAG *flag = (NETOBJ_FLAG *)snap_new_item(NETOBJTYPE_FLAG, team, sizeof(NETOBJ_FLAG));
 	flag->x = (int)pos.x;
 	flag->y = (int)pos.y;
 	flag->team = team;
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index 656609a6..514850a0 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -107,7 +107,7 @@ void event_handler::snap(int snapping_client)
 	{
 		if(cmask_is_set(client_masks[i], snapping_client))
 		{
-			ev_common *ev = (ev_common *)&data[offsets[i]];
+			NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]];
 			if(distance(players[snapping_client].pos, vec2(ev->x, ev->y)) < 1500.0f)
 			{
 				void *d = snap_new_item(types[i], i, sizes[i]);
@@ -297,6 +297,7 @@ void game_world::tick()
 
 	if(!paused)
 	{
+		/*
 		static PERFORMACE_INFO scopes[OBJTYPE_FLAG+1] =
 		{
 			{"null", 0},
@@ -320,31 +321,32 @@ void game_world::tick()
 		};
 				
 		static PERFORMACE_INFO tick_scope = {"tick", 0};
-		perf_start(&tick_scope);
+		perf_start(&tick_scope);*/
 		
 		// update all objects
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 		{
-			if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_start(&scopes[ent->objtype]);
+			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
+				perf_start(&scopes[ent->objtype]);*/
 			ent->tick();
-			if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_end();
+			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
+				perf_end();*/
 		}
 		
+		/*
 		perf_end();
 
 		static PERFORMACE_INFO deftick_scope = {"tick_defered", 0};
-		perf_start(&deftick_scope);
+		perf_start(&deftick_scope);*/
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 		{
-			if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_start(&scopes_def[ent->objtype]);
+			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
+				perf_start(&scopes_def[ent->objtype]);*/
 			ent->tick_defered();
-			if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
-				perf_end();
+			/*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG)
+				perf_end();*/
 		}
-		perf_end();
+		/*perf_end();*/
 	}
 
 	remove_entities();
@@ -380,7 +382,7 @@ static input_count count_input(int prev, int cur)
 //////////////////////////////////////////////////
 projectile::projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner,
 	int damage, int flags, float force, int sound_impact, int weapon)
-: entity(OBJTYPE_PROJECTILE)
+: entity(NETOBJTYPE_PROJECTILE)
 {
 	this->type = type;
 	this->pos = pos;
@@ -437,7 +439,7 @@ void projectile::tick()
 	}
 }
 
-void projectile::fill_info(obj_projectile *proj)
+void projectile::fill_info(NETOBJ_PROJECTILE *proj)
 {
 	proj->x = (int)pos.x;
 	proj->y = (int)pos.y;
@@ -455,7 +457,7 @@ void projectile::snap(int snapping_client)
 	if(distance(players[snapping_client].pos, curpos) > 1000.0f)
 		return;
 
-	obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile));
+	NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE));
 	fill_info(proj);
 }
 
@@ -464,7 +466,7 @@ void projectile::snap(int snapping_client)
 // laser
 //////////////////////////////////////////////////
 laser::laser(vec2 pos, vec2 direction, float start_energy, player *owner)
-: entity(OBJTYPE_LASER)
+: entity(NETOBJTYPE_LASER)
 {
 	this->pos = pos;
 	this->owner = owner;
@@ -554,7 +556,7 @@ void laser::snap(int snapping_client)
 	if(distance(players[snapping_client].pos, pos) > 1000.0f)
 		return;
 
-	obj_laser *obj = (obj_laser *)snap_new_item(OBJTYPE_LASER, id, sizeof(obj_laser));
+	NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER));
 	obj->x = (int)pos.x;
 	obj->y = (int)pos.y;
 	obj->from_x = (int)from.x;
@@ -568,7 +570,7 @@ void laser::snap(int snapping_client)
 //////////////////////////////////////////////////
 // TODO: move to separate file
 player::player()
-: entity(OBJTYPE_PLAYER_CHARACTER)
+: entity(NETOBJTYPE_PLAYER_CHARACTER)
 {
 	init();
 }
@@ -788,7 +790,7 @@ void player::try_respawn()
 
 	// check if the position is occupado
 	entity *ents[2] = {0};
-	int types[] = {OBJTYPE_PLAYER_CHARACTER};
+	int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
 	int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1);
 	for(int i = 0; i < num_ents; i++)
 	{
@@ -901,12 +903,12 @@ int player::handle_ninja()
 		core.vel = vec2(0.0f,0.0f);
 		if ((ninja.currentmovetime % 2) == 0)
 		{
-			create_smoke(pos);
+			//create_smoke(pos);
 		}
 
 		// check if we hit anything along the way
 		{
-			int type = OBJTYPE_PLAYER_CHARACTER;
+			int type = NETOBJTYPE_PLAYER_CHARACTER;
 			entity *ents[64];
 			vec2 dir = pos - oldpos;
 			float radius = phys_size * 2.0f; //length(dir * 0.5f);
@@ -990,12 +992,12 @@ void player::fire_weapon()
 				1, 0, 0, -1, WEAPON_GUN);
 				
 			// pack the projectile and send it to the client directly
-			obj_projectile p;
+			NETOBJ_PROJECTILE p;
 			proj->fill_info(&p);
 			
 			msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
 			msg_pack_int(1);
-			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
 				msg_pack_int(((int *)&p)[i]);
 			msg_pack_end();
 			server_send_msg(client_id);
@@ -1014,12 +1016,12 @@ void player::fire_weapon()
 				1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
 
 			// pack the projectile and send it to the client directly
-			obj_projectile p;
+			NETOBJ_PROJECTILE p;
 			proj->fill_info(&p);
 			
 			msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
 			msg_pack_int(1);
-			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+			for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
 				msg_pack_int(((int *)&p)[i]);
 			msg_pack_end();
 			server_send_msg(client_id);
@@ -1050,10 +1052,10 @@ void player::fire_weapon()
 					1, 0, 0, -1, WEAPON_SHOTGUN);
 					
 				// pack the projectile and send it to the client directly
-				obj_projectile p;
+				NETOBJ_PROJECTILE p;
 				proj->fill_info(&p);
 				
-				for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+				for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
 					msg_pack_int(((int *)&p)[i]);
 			}
 
@@ -1245,7 +1247,7 @@ int player::handle_weapons()
 		// only one that needs update (for now)
 		// do selection for the weapon and bash anything in it
 		// check if we hit anything along the way
-		int type = OBJTYPE_PLAYER_CHARACTER;
+		int type = NETOBJTYPE_PLAYER_CHARACTER;
 		entity *ents[64];
 		vec2 lookdir(direction.x > 0.0f ? 1.0f : -1.0f, 0.0f);
 		vec2 dir = lookdir * data->weapons[active_weapon].meleereach;
@@ -1278,7 +1280,7 @@ int player::handle_weapons()
 			vec2 fdir = normalize(ents[i]->pos- pos);
 
 			// set his velocity to fast upward (for now)
-			create_smoke(ents[i]->pos);
+			//create_smoke(ents[i]->pos);
 			create_sound(pos, SOUND_HAMMER_HIT);
 			if(numobjectshit < 10)
 				hitobjects[numobjectshit++] = ents[i];
@@ -1318,7 +1320,7 @@ int player::handle_weapons()
 	return 0;
 }
 
-void player::on_direct_input(player_input *new_input)
+void player::on_direct_input(NETOBJ_PLAYER_INPUT *new_input)
 {
 	mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
 	mem_copy(&latest_input, new_input, sizeof(latest_input));
@@ -1444,7 +1446,7 @@ void player::tick_defered()
 		if(events&COREEVENT_AIR_JUMP)
 		{
 			create_sound(pos, SOUND_PLAYER_AIRJUMP, mask);
-			ev_common *c = (ev_common *)::events.create(EVENT_AIR_JUMP, sizeof(ev_common), mask);
+			NETEVENT_COMMON *c = (NETEVENT_COMMON *)::events.create(NETEVENTTYPE_AIR_JUMP, sizeof(NETEVENT_COMMON), mask);
 			if(c)
 			{
 				c->x = (int)pos.x;
@@ -1580,12 +1582,12 @@ void player::snap(int snaping_client)
 {
 	if(1)
 	{
-		obj_player_info *info = (obj_player_info *)snap_new_item(OBJTYPE_PLAYER_INFO, client_id, sizeof(obj_player_info));
+		NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO));
 
 		info->latency = latency_min;
 		info->latency_flux = latency_max-latency_min;
 		info->local = 0;
-		info->clientid = client_id;
+		info->cid = client_id;
 		info->score = score;
 		info->team = team;
 
@@ -1595,7 +1597,7 @@ void player::snap(int snaping_client)
 
 	if(health > 0 && team >= 0 && distance(players[snaping_client].pos, pos) < 1000.0f)
 	{
-		obj_player_character *character = (obj_player_character *)snap_new_item(OBJTYPE_PLAYER_CHARACTER, client_id, sizeof(obj_player_character));
+		NETOBJ_PLAYER_CHARACTER *character = (NETOBJ_PLAYER_CHARACTER *)snap_new_item(NETOBJTYPE_PLAYER_CHARACTER, client_id, sizeof(NETOBJ_PLAYER_CHARACTER));
 
 		core.write(character);
 
@@ -1651,7 +1653,7 @@ player *players;
 // powerup
 //////////////////////////////////////////////////
 powerup::powerup(int _type, int _subtype)
-: entity(OBJTYPE_POWERUP)
+: entity(NETOBJTYPE_POWERUP)
 {
 	type = _type;
 	subtype = _subtype;
@@ -1747,7 +1749,7 @@ void powerup::tick()
 
 				// loop through all players, setting their emotes
 				entity *ents[64];
-				const int types[] = {OBJTYPE_PLAYER_CHARACTER};
+				const int types[] = {NETOBJTYPE_PLAYER_CHARACTER};
 				int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1);
 				for (int i = 0; i < num; i++)
 				{
@@ -1782,7 +1784,7 @@ void powerup::snap(int snapping_client)
 	if(spawntick != -1)
 		return;
 
-	obj_powerup *up = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup));
+	NETOBJ_POWERUP *up = (NETOBJ_POWERUP *)snap_new_item(NETOBJTYPE_POWERUP, id, sizeof(NETOBJ_POWERUP));
 	up->x = (int)pos.x;
 	up->y = (int)pos.y;
 	up->type = type; // TODO: two diffrent types? what gives?
@@ -1805,7 +1807,7 @@ void create_damageind(vec2 p, float angle, int amount)
 	for(int i = 0; i < amount; i++)
 	{
 		float f = mix(s, e, float(i+1)/float(amount+2));
-		ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind));
+		NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)events.create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND));
 		if(ev)
 		{
 			ev->x = (int)p.x;
@@ -1818,7 +1820,7 @@ void create_damageind(vec2 p, float angle, int amount)
 void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 {
 	// create the event
-	ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion));
+	NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION));
 	if(ev)
 	{
 		ev->x = (int)p.x;
@@ -1848,21 +1850,22 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 	}
 }
 
+/*
 void create_smoke(vec2 p)
 {
 	// create the event
-	ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion));
+	EV_EXPLOSION *ev = (EV_EXPLOSION *)events.create(EVENT_SMOKE, sizeof(EV_EXPLOSION));
 	if(ev)
 	{
 		ev->x = (int)p.x;
 		ev->y = (int)p.y;
 	}
-}
+}*/
 
 void create_playerspawn(vec2 p)
 {
 	// create the event
-	ev_spawn *ev = (ev_spawn *)events.create(EVENT_PLAYERSPAWN, sizeof(ev_spawn));
+	NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)events.create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN));
 	if(ev)
 	{
 		ev->x = (int)p.x;
@@ -1873,7 +1876,7 @@ void create_playerspawn(vec2 p)
 void create_death(vec2 p)
 {
 	// create the event
-	ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death));
+	NETEVENT_DEATH *ev = (NETEVENT_DEATH *)events.create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH));
 	if(ev)
 	{
 		ev->x = (int)p.x;
@@ -1887,12 +1890,12 @@ void create_sound(vec2 pos, int sound, int mask)
 		return;
 
 	// create a sound
-	ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND_WORLD, sizeof(ev_sound), mask);
+	NETEVENT_SOUND_WORLD *ev = (NETEVENT_SOUND_WORLD *)events.create(NETEVENTTYPE_SOUND_WORLD, sizeof(NETEVENT_SOUND_WORLD), mask);
 	if(ev)
 	{
 		ev->x = (int)pos.x;
 		ev->y = (int)pos.y;
-		ev->sound = sound;
+		ev->soundid = sound;
 	}
 }
 
@@ -2043,7 +2046,7 @@ void mods_snap(int client_id)
 void mods_client_direct_input(int client_id, void *input)
 {
 	if(!world->paused)
-		players[client_id].on_direct_input((player_input *)input);
+		players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input);
 	
 	/*
 	if(i->fire)
@@ -2058,11 +2061,11 @@ void mods_client_predicted_input(int client_id, void *input)
 {
 	if(!world->paused)
 	{
-		if (memcmp(&players[client_id].input, input, sizeof(player_input)) != 0)
+		if (memcmp(&players[client_id].input, input, sizeof(NETOBJ_PLAYER_INPUT)) != 0)
 			players[client_id].last_action = server_tick();
 
 		//players[client_id].previnput = players[client_id].input;
-		players[client_id].input = *(player_input*)input;
+		players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input;
 		players[client_id].num_inputs++;
 		
 		if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0)
diff --git a/src/tools/packetgen.c b/src/tools/packetgen.c
new file mode 100644
index 00000000..dcfe0885
--- /dev/null
+++ b/src/tools/packetgen.c
@@ -0,0 +1,36 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <engine/e_system.h>
+
+enum { NUM_SOCKETS = 64 };
+
+int run(NETADDR4 dest)
+{
+	NETSOCKET sockets[NUM_SOCKETS];
+	int i;
+	
+	for(i = 0; i < NUM_SOCKETS; i++)
+	{
+		NETADDR4 bindaddr = {{0,0,0,0}, 0};
+	 	sockets[i] = net_udp4_create(bindaddr);
+	}
+	
+	while(1)
+	{
+		unsigned char data[1024];
+		int size = 0;
+		int socket_to_use = 0;
+		io_read(io_stdin(), &size, 2);
+		io_read(io_stdin(), &socket_to_use, 1);
+		size %= 256;
+		socket_to_use %= NUM_SOCKETS;
+		io_read(io_stdin(), data, size);
+		net_udp4_send(sockets[socket_to_use], &dest, data, size);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	NETADDR4 dest = {{127,0,0,1},8303};
+	run(dest);
+	return 0;
+}