about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-10-28 11:30:25 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-10-28 11:30:25 +0000
commita3ce2eb90cd26fe87042344175e5c9669adb7dcd (patch)
treed936a8ffafe366caea08c5bb384794034c590322
parenteba83b7e194cc6b4ba76fd5e048d81279becfc35 (diff)
downloadzcatch-a3ce2eb90cd26fe87042344175e5c9669adb7dcd.tar.gz
zcatch-a3ce2eb90cd26fe87042344175e5c9669adb7dcd.zip
major update. splitted the player information into two diffrent network items
-rw-r--r--src/engine/client/client.c8
-rw-r--r--src/engine/client/gfx.c7
-rw-r--r--src/engine/client/snd.c13
-rw-r--r--src/engine/config_variables.h2
-rw-r--r--src/engine/network.c16
-rw-r--r--src/engine/protocol.h2
-rw-r--r--src/engine/server/server.c15
-rw-r--r--src/engine/snapshot.c17
-rw-r--r--src/engine/system.c1
-rw-r--r--src/game/client/game_client.cpp716
-rw-r--r--src/game/game.cpp23
-rw-r--r--src/game/game_protocol.h64
-rw-r--r--src/game/server/game_server.cpp384
-rw-r--r--src/game/server/srv_ctf.cpp7
14 files changed, 698 insertions, 577 deletions
diff --git a/src/engine/client/client.c b/src/engine/client/client.c
index f8ccf6e5..c6a9252f 100644
--- a/src/engine/client/client.c
+++ b/src/engine/client/client.c
@@ -1007,6 +1007,14 @@ static void client_run(const char *direct_connect_server)
 int editor_main(int argc, char **argv);
 
 /*client main_client; */
+/*
+const char *user_directory()
+{
+	static char path[512] = {0};
+	
+}*/
+
+
 
 int main(int argc, char **argv)
 {
diff --git a/src/engine/client/gfx.c b/src/engine/client/gfx.c
index 13c35a39..fe72f4c7 100644
--- a/src/engine/client/gfx.c
+++ b/src/engine/client/gfx.c
@@ -531,9 +531,14 @@ int gfx_load_texture_raw(int w, int h, int format, const void *data)
 	unsigned char *texdata = (unsigned char *)data;
 	unsigned char *tmpdata = 0;
 	int oglformat = 0;
+	int tex = 0;
+	
+	/* don't waste memory on texture if we are stress testing */
+	if(config.stress)
+		return -1;
 	
 	/* grab texture */
-	int tex = first_free_texture;
+	tex = first_free_texture;
 	first_free_texture = textures[tex].next;
 	textures[tex].next = -1;
 	
diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c
index 79121584..725bc569 100644
--- a/src/engine/client/snd.c
+++ b/src/engine/client/snd.c
@@ -334,14 +334,23 @@ int snd_load_wv(const char *filename)
 	int sid = -1;
 	char error[100];
 	WavpackContext *context;
+	
+	/* don't waste memory on sound when we are stress testing */
+	if(config.stress)
+		return -1;
+
+	file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */
+	if(!file)
+	{
+		dbg_msg("sound/wv", "failed to open %s", filename);
+		return -1;
+	}
 
 	sid = snd_alloc_id();
 	if(sid < 0)
 		return -1;
 	snd = &samples[sid];
 
-	file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */
-
 	context = WavpackOpenFileInput(read_data, error);
 	if (context)
 	{
diff --git a/src/engine/config_variables.h b/src/engine/config_variables.h
index dfe638d0..4ef8958e 100644
--- a/src/engine/config_variables.h
+++ b/src/engine/config_variables.h
@@ -46,3 +46,5 @@ MACRO_CONFIG_STR(sv_map, 128, "dm1")
 
 MACRO_CONFIG_INT(sv_max_clients, 8, 1, 16)
 
+MACRO_CONFIG_INT(sv_bandwidth_mode, 0, 0, 2)
+
diff --git a/src/engine/network.c b/src/engine/network.c
index 05779831..1005bbf2 100644
--- a/src/engine/network.c
+++ b/src/engine/network.c
@@ -17,7 +17,8 @@ enum
 	NETWORK_VERSION = 1,
 	
 	NETWORK_HEADER_SIZE = 6,
-	NETWORK_MAX_PACKET_SIZE = 1024,
+	NETWORK_MAX_PAYLOAD = 1024,
+	NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD,
 	NETWORK_MAX_CLIENTS = 16,
 	
 	NETWORK_CONNSTATE_OFFLINE=0,
@@ -738,6 +739,8 @@ 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->flags&PACKETFLAG_CONNLESS)
 	{
 		/* send connectionless packet */
@@ -775,9 +778,12 @@ void netserver_stats(NETSERVER *s, NETSTATS *stats)
 	
 	for(c = 0; c < s->max_clients; c++)
 	{
-		int *sstats = (int *)(&(s->slots[c].conn.stats));
-		for(i = 0; i < num_stats; i++)
-			istats[i] += sstats[i];
+		if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE)
+		{
+			int *sstats = (int *)(&(s->slots[c].conn.stats));
+			for(i = 0; i < num_stats; i++)
+				istats[i] += sstats[i];
+		}
 	}
 }
 
@@ -869,6 +875,8 @@ 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->flags&PACKETFLAG_CONNLESS)
 	{
 		/* send connectionless packet */
diff --git a/src/engine/protocol.h b/src/engine/protocol.h
index b9aba258..96ad1e6b 100644
--- a/src/engine/protocol.h
+++ b/src/engine/protocol.h
@@ -30,5 +30,5 @@ enum
 	MAX_NAME_LENGTH=32,
 	MAX_CLANNAME_LENGTH=32,
 	MAX_INPUT_SIZE=128,
-	MAX_SNAPSHOT_PACKSIZE=1200
+	MAX_SNAPSHOT_PACKSIZE=900
 };
diff --git a/src/engine/server/server.c b/src/engine/server/server.c
index afbcc0dd..0262c65f 100644
--- a/src/engine/server/server.c
+++ b/src/engine/server/server.c
@@ -739,6 +739,10 @@ static int server_run()
 				}
 	
 				/* snap game */
+				if(config.sv_bandwidth_mode == 0 ||
+					(config.sv_bandwidth_mode == 1 && current_tick%2) ||
+					(config.sv_bandwidth_mode == 2 && (current_tick%3) == 0 ))
+				/* if(current_tick&1) */
 				{
 					int64 start = time_get();
 					server_do_snap();
@@ -765,12 +769,21 @@ static int server_run()
 			{
 				if(config.debug)
 				{
-					dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%",
+					static NETSTATS prev_stats;
+					NETSTATS stats;
+					netserver_stats(net, &stats);
+					dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms tot=%.02fms load=%.02f%%",
 						(simulationtime/reportinterval)/(double)time_freq()*1000,
 						(snaptime/reportinterval)/(double)time_freq()*1000,
 						(networktime/reportinterval)/(double)time_freq()*1000,
 						(totaltime/reportinterval)/(double)time_freq()*1000,
 						(totaltime)/reportinterval/(double)time_freq()*100.0f);
+
+					dbg_msg("server", "send=%8d recv=%8d",
+						(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
+						(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
+						
+					prev_stats = stats;
 				}
 	
 				simulationtime = 0;
diff --git a/src/engine/snapshot.c b/src/engine/snapshot.c
index 84f0416b..40bad21e 100644
--- a/src/engine/snapshot.c
+++ b/src/engine/snapshot.c
@@ -94,26 +94,11 @@ void snapshot_debug_dump(SNAPSHOT *snap)
 
 static int diff_item(int *past, int *current, int *out, int size)
 {
-	/*
 	int needed = 0;
 	while(size)
 	{
 		*out = *current-*past;
-		if(*out)
-			needed = 1;
-		out++;
-		current++;
-		past++;
-		size--;
-	}*/
-
-	int needed = 0;
-	while(size)
-	{
-		*out = *current-*past;
-		if(*out)
-			needed = 1;
-		
+		needed |= *out;
 		out++;
 		past++;
 		current++;
diff --git a/src/engine/system.c b/src/engine/system.c
index 67e84b74..71f5b545 100644
--- a/src/engine/system.c
+++ b/src/engine/system.c
@@ -81,6 +81,7 @@ void dbg_msg(const char *sys, const char *fmt, ...)
 	vprintf(fmt, args);
 	va_end(args);
 	printf("\n");
+	fflush(stdout);
 }
 
 int memory_alloced = 0;
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index 55c98bc1..d31b1b3e 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -46,8 +46,10 @@ static bool menu_active = false;
 static bool emoticon_selector_active = false;
 
 static vec2 mouse_pos;
-static vec2 local_player_pos;
-static obj_player *local_player;
+static vec2 local_character_pos;
+static const obj_player_character *local_character = 0;
+static const obj_player_info *local_info = 0;
+static const obj_game *gameobj = 0;
 
 struct client_data
 {
@@ -74,8 +76,8 @@ public:
 	}
 
 	float getorgzoom() { return zoom; }
-	
-	float getzoom(int tick, float intratick, obj_player* player)
+
+	float getzoom(int tick, float intratick, const obj_player_character *player)
 	{
 		float currentstage = ((float)player->weaponstage) * 0.1f;
 		if (currentstage < stage)
@@ -102,16 +104,16 @@ inline float frandom() { return rand()/(float)(RAND_MAX); }
 void snd_play_random(int chn, int setid, float vol, vec2 pos)
 {
 	soundset *set = &data->sounds[setid];
-	
+
 	if(!set->num_sounds)
 		return;
-		
+
 	if(set->num_sounds == 1)
 	{
 		snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y);
 		return;
 	}
-	
+
 	// play a random one
 	int id;
 	do {
@@ -133,7 +135,7 @@ static const float volume_music = 0.8f;
 
 void sound_vol_pan(const vec2& p, float *vol, float *pan)
 {
-	vec2 player_to_ev = p - local_player_pos;
+	vec2 player_to_ev = p - local_character_pos;
 	*pan = 0.0f;
 	*vol = 1.0f;
 
@@ -170,11 +172,11 @@ static void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0)
 	int h = spr->h;
 	int cx = spr->set->gridx;
 	int cy = spr->set->gridy;
-	
+
 	float f = sqrtf(h*h + w*w);
 	sprite_w_scale = w/f;
 	sprite_h_scale = h/f;
-	
+
 	if(flags&SPRITE_FLAG_FLIP_Y)
 		gfx_quads_setsubset(x/(float)cx,(y+h)/(float)cy,(x+w)/(float)cx,y/(float)cy);
 	else
@@ -204,7 +206,7 @@ public:
 		float life;
 		float startangle;
 	};
-	
+
 	enum
 	{
 		MAX_ITEMS=64,
@@ -215,10 +217,10 @@ public:
 		lastupdate = 0;
 		num_items = 0;
 	}
-	
+
 	item items[MAX_ITEMS];
 	int num_items;
-	
+
 	item *create_i()
 	{
 		if (num_items < MAX_ITEMS)
@@ -229,13 +231,13 @@ public:
 		}
 		return 0;
 	}
-	
+
 	void destroy_i(item *i)
 	{
 		num_items--;
 		*i = items[num_items];
 	}
-	
+
 	void create(vec2 pos, vec2 dir)
 	{
 		item *i = create_i();
@@ -247,7 +249,7 @@ public:
 			i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi;
 		}
 	}
-	
+
 	void render()
 	{
 		gfx_texture_set(data->images[IMAGE_GAME].id);
@@ -255,7 +257,7 @@ public:
 		for(int i = 0; i < num_items;)
 		{
 			vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f));
-			
+
 			items[i].life -= client_frametime();
 			if(items[i].life < 0.0f)
 				destroy_i(&items[i]);
@@ -270,7 +272,7 @@ public:
 		}
 		gfx_quads_end();
 	}
-	
+
 };
 
 static damage_indicators damageind;
@@ -285,14 +287,14 @@ public:
 		float life;
 		float max_life;
 		float size;
-		
+
 		float rot;
 		float rotspeed;
-		
+
 		float gravity;
 		float friction;
 		int iparticle;
-		
+
 		vec4 color;
 	};
 
@@ -300,15 +302,15 @@ public:
 	{
 		MAX_PARTICLES=1024,
 	};
-	
+
 	particle particles[MAX_PARTICLES];
 	int num_particles;
-	
+
 	particle_system()
 	{
 		num_particles = 0;
 	}
-	
+
 	void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction)
 	{
 		if (num_particles >= MAX_PARTICLES)
@@ -326,7 +328,7 @@ public:
 		particles[num_particles].rotspeed = frandom() * 10.0f;
 		num_particles++;
 	}
-	
+
 	void update(float time_passed)
 	{
 		for(int i = 0; i < num_particles; i++)
@@ -338,7 +340,7 @@ public:
 			particles[i].vel = vel* (1.0f/time_passed);
 			particles[i].life += time_passed;
 			particles[i].rot += time_passed * particles[i].rotspeed;
-			
+
 			// check particle death
 			if(particles[i].life > particles[i].max_life)
 			{
@@ -348,31 +350,31 @@ public:
 			}
 		}
 	}
-	
+
 	void render()
 	{
 		gfx_blend_additive();
 		gfx_texture_set(data->images[IMAGE_GAME].id);
 		gfx_quads_begin();
-		
+
 		for(int i = 0; i < num_particles; i++)
 		{
 			int type = particles[i].iparticle;
 			select_sprite(data->particles[type].spr);
 			float a = 1 - particles[i].life / particles[i].max_life;
 			vec2 p = particles[i].pos;
-			
+
 			gfx_quads_setrotation(particles[i].rot);
-			
+
 			gfx_setcolor(
 				data->particles[type].color_r,
 				data->particles[type].color_g,
 				data->particles[type].color_b,
 				pow(a, 0.75f));
-				
+
 			gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size);
 		}
-		gfx_quads_end();		
+		gfx_quads_end();
 		gfx_blend_normal();
 	}
 };
@@ -386,13 +388,13 @@ public:
 	{
 		LISTSIZE = 1000,
 	};
-	// meh, just use size % 
+	// meh, just use size %
 	int lastadd[LISTSIZE];
 	projectile_particles()
 	{
 		reset();
 	}
-	
+
 	void reset()
 	{
 		for (int i = 0; i < LISTSIZE; i++)
@@ -403,24 +405,24 @@ public:
 	{
 		int particlespersecond = data->projectileinfo[projectiletype].particlespersecond;
 		int lastaddtick = lastadd[projectileid % LISTSIZE];
-		
+
 		if(!particlespersecond)
 			return;
-		
+
 		if ((client_tick() - lastaddtick) > (client_tickspeed() / particlespersecond))
 		{
 			lastadd[projectileid % LISTSIZE] = client_tick();
 			float life = data->projectileinfo[projectiletype].particlelife;
 			float size = data->projectileinfo[projectiletype].particlesize;
 			vec2 v = vel * 0.2f + normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f);
-			
+
 			// add the particle (from projectiletype later on, but meh...)
 			temp_system.new_particle(pos, v, life, size, 0, 0.95f);
 		}
 	}
 };
 static projectile_particles proj_particles;
- 
+
 static char chat_input[512];
 static unsigned chat_input_len;
 static const int chat_max_lines = 10;
@@ -489,7 +491,7 @@ static void render_loading(float percent)
 	float y = 600/2-h/2;
 
 	gfx_blend_normal();
-	
+
 	gfx_texture_set(-1);
 	gfx_quads_begin();
 	gfx_setcolor(0,0,0,0.50f);
@@ -497,7 +499,7 @@ static void render_loading(float percent)
 	gfx_quads_end();
 
 	const char *caption = "Loading";
-	
+
 	tw = gfx_pretty_text_width(48.0f, caption, -1);
 	ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f);
 
@@ -516,7 +518,7 @@ extern "C" void modc_init()
 	snd_set_channel(CHN_GUI, 1.0f, 0.0f);
 	snd_set_channel(CHN_MUSIC, 1.0f, 0.0f);
 	snd_set_channel(CHN_WORLD, 1.0f, 1.0f);
-	
+
 	// load the data container
 	data = load_data_from_memory(internal_data);
 
@@ -541,10 +543,10 @@ extern "C" void modc_init()
 
 			data->sounds[s].sounds[i].id = id;
 		}
-		
+
 		current++;
 	}
-	
+
 	// load textures
 	for(int i = 0; i < data->num_images; i++)
 	{
@@ -560,9 +562,9 @@ extern "C" void modc_entergame()
 	img_init();
 	tilemap_init();
 	chat_reset();
-	
+
 	proj_particles.reset();
-	
+
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
 		client_datas[i].name[0] = 0;
@@ -570,7 +572,7 @@ extern "C" void modc_entergame()
 		client_datas[i].emoticon = 0;
 		client_datas[i].emoticon_start = -1;
 	}
-		
+
 	for(int i = 0; i < killmsg_max; i++)
 		killmsgs[i].tick = -100000;
 }
@@ -588,7 +590,7 @@ static void process_events(int s)
 	{
 		SNAP_ITEM item;
 		const void *data = snap_get_item(s, index, &item);
-		
+
 		if(item.type == EVENT_DAMAGEINDICATION)
 		{
 			ev_damageind *ev = (ev_damageind *)data;
@@ -598,19 +600,19 @@ static void process_events(int s)
 		{
 			ev_explosion *ev = (ev_explosion *)data;
 			vec2 p(ev->x, ev->y);
-			
+
 			// center explosion
 			temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f);
 			temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f);
 			temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f);
 			temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f);
-			
+
 			for(int i = 0; i < 16; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f);
 				temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f);
 			}
-			
+
 			for(int i = 0; i < 16; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f);
@@ -627,7 +629,7 @@ static void process_events(int s)
 		{
 			ev_explosion *ev = (ev_explosion *)data;
 			vec2 p(ev->x, ev->y);
-			
+
 			// center explosion
 			vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f);
 			temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f);
@@ -635,13 +637,13 @@ static void process_events(int s)
 			temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f);
 			v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f);
 			temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f);
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f);
 				temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f);
 			}
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f);
@@ -652,7 +654,7 @@ static void process_events(int s)
 		{
 			ev_explosion *ev = (ev_explosion *)data;
 			vec2 p(ev->x, ev->y);
-			
+
 			// center explosion
 			vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f);
 			temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f);
@@ -660,13 +662,13 @@ static void process_events(int s)
 			temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f);
 			v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f);
 			temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f);
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f);
 				temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f);
 			}
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f);
@@ -677,7 +679,7 @@ static void process_events(int s)
 		{
 			ev_explosion *ev = (ev_explosion *)data;
 			vec2 p(ev->x, ev->y);
-			
+
 			// center explosion
 			vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f);
 			temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f);
@@ -685,13 +687,13 @@ static void process_events(int s)
 			temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f);
 			v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f);
 			temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f);
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f);
 				temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f);
 			}
-			
+
 			for(int i = 0; i < 8; i++)
 			{
 				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f);
@@ -707,7 +709,7 @@ static void process_events(int s)
 			//bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0;
 			//float vol, pan;
 			//sound_vol_pan(p, &vol, &pan);
-			
+
 			if(soundid >= 0 && soundid < NUM_SOUNDS)
 			{
 				// TODO: we need to control the volume of the diffrent sounds
@@ -716,7 +718,7 @@ static void process_events(int s)
 			}
 		}
 	}
-	
+
 	must_process_events = false;
 }
 
@@ -729,25 +731,30 @@ extern "C" void modc_predict()
 	{
 		world_core world;
 		int local_cid = -1;
-		
+
 		// search for players
 		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)
+			int client_id = item.id;
+
+			if(item.type == OBJTYPE_PLAYER_CHARACTER)
 			{
-				const obj_player *player = (const obj_player *)data;
-				client_datas[player->clientid].predicted.world = &world;
-				world.players[player->clientid] = &client_datas[player->clientid].predicted;
-				
-				client_datas[player->clientid].predicted.read(player);
-				if(player->local)
-					local_cid = player->clientid;
+				const obj_player_character *character = (const obj_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)
+			{
+				const obj_player_info *info = (const obj_player_info *)data;
+				if(info->local)
+					local_cid = client_id;
 			}
 		}
-		
+
 		// predict
 		for(int tick = client_tick(); tick <= client_predtick(); tick++)
 		{
@@ -756,7 +763,7 @@ extern "C" void modc_predict()
 			{
 				if(!world.players[c])
 					continue;
-				
+
 				mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
 				if(local_cid == c)
 				{
@@ -765,23 +772,23 @@ extern "C" void modc_predict()
 					if(input)
 						world.players[c]->input = *((player_input*)input);
 				}
-				
+
 				world.players[c]->tick();
 			}
-			
+
 			// move all players and quantize their data
 			for(int c = 0; c < MAX_CLIENTS; c++)
 			{
 				if(!world.players[c])
 					continue;
-					
+
 				world.players[c]->move();
 				world.players[c]->quantize();
 			}
 		}
-		
+
 		// get the data from the local player
-		if(local_cid != -1)
+		if(local_cid != -1 && world.players[local_cid])
 		{
 			predicted_prev_player = predicted_player;
 			predicted_player = *world.players[local_cid];
@@ -794,7 +801,7 @@ extern "C" void modc_newsnapshot()
 	if(must_process_events)
 		process_events(SNAP_PREV);
 	must_process_events = true;
-	
+
 	if(config.stress)
 	{
 		if((client_tick()%250) == 0)
@@ -806,6 +813,47 @@ extern "C" void modc_newsnapshot()
 			client_send_msg();
 		}
 	}
+
+	// clear out the invalid pointers
+	local_character = 0;
+	local_info = 0;
+	gameobj = 0;
+
+	// setup world view
+	{
+		// 1. fetch local player
+		// 2. set him to the center
+		int num = snap_num_items(SNAP_CURRENT);
+		for(int i = 0; i < num; i++)
+		{
+			SNAP_ITEM item;
+			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
+
+			if(item.type == OBJTYPE_PLAYER_INFO)
+			{
+				const obj_player_info *info = (const obj_player_info *)data;
+				if(info->local)
+				{
+					local_info = info;
+					const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id);
+					if(data)
+					{
+						local_character = (const obj_player_character *)data;
+						local_character_pos = vec2(local_character->x, local_character->y);
+
+						const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id);
+						if(p)
+						{
+							local_character_pos = mix(vec2(((obj_player_character *)p)->x, ((obj_player_character *)p)->y),
+								local_character_pos, client_intratick());
+						}
+					}
+				}
+			}
+			else if(item.type == OBJTYPE_GAME)
+				gameobj = (obj_game *)data;
+		}
+	}
 }
 
 void send_changename_request(const char *name)
@@ -828,22 +876,22 @@ static void render_projectile(const obj_projectile *prev, const obj_projectile *
 {
 	gfx_texture_set(data->images[IMAGE_GAME].id);
 	gfx_quads_begin();
-	
+
 	select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj);
 	vec2 vel = mix(vec2(prev->vx, prev->vy), vec2(current->vx, current->vy), client_intratick());
 	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
-	
+
 	// add particle for this projectile
 	proj_particles.addparticle(current->type, itemid, pos, vel);
-	
+
 	if(length(vel) > 0.00001f)
 		gfx_quads_setrotation(get_angle(vel));
 	else
 		gfx_quads_setrotation(0);
-	
+
 	// TODO: do this, but nice
 	//temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f);
-	
+
 	gfx_quads_draw(pos.x, pos.y, 32, 32);
 	gfx_quads_setrotation(0);
 	gfx_quads_end();
@@ -872,7 +920,7 @@ static void render_powerup(const obj_powerup *prev, const obj_powerup *current)
 			SPRITE_POWERUP_TIMEFIELD
 			};
 		select_sprite(c[current->type]);
-		
+
 		if(c[current->type] == SPRITE_POWERUP_NINJA)
 		{
 			proj_particles.addparticle(0, 0,
@@ -882,9 +930,9 @@ static void render_powerup(const obj_powerup *prev, const obj_powerup *current)
 			pos.x += 10.0f;
 		}
 	}
-	
+
 	gfx_quads_setrotation(angle);
-	
+
 	float offset = pos.y/32.0f + pos.x/32.0f;
 	pos.x += cosf(client_localtime()*2.0f+offset)*2.5f;
 	pos.y += sinf(client_localtime()*2.0f+offset)*2.5f;
@@ -905,14 +953,14 @@ static void render_flag(const obj_flag *prev, const obj_flag *current)
 		select_sprite(SPRITE_FLAG_RED);
 	else
 		select_sprite(SPRITE_FLAG_BLUE);
-	
+
 	gfx_quads_setrotation(angle);
-	
+
 	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
-	
+
 	if(current->local_carry)
-		pos = local_player_pos;
-		
+		pos = local_character_pos;
+
     gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1);
     //draw_sprite(pos.x, pos.y, size);
     gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2);
@@ -949,7 +997,7 @@ static void anim_seq_eval(sequence *seq, float time, keyframe *frame)
 				blend = (time - frame1->time) / (frame2->time - frame1->time);
 				break;
 			}
-		}		
+		}
 
 		if (frame1 && frame2)
 		{
@@ -1045,10 +1093,10 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 {
 	vec2 direction =  dir;
 	vec2 position = pos;
-	
+
 	gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
 	gfx_quads_begin();
-	
+
 	// draw foots
 	for(int p = 0; p < 2; p++)
 	{
@@ -1056,7 +1104,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 		// second pass we draw the filling
 		int outline = p==0 ? 1 : 0;
 		int shift = skin;
-		
+
 		for(int f = 0; f < 2; f++)
 		{
 			float basesize = 10.0f;
@@ -1066,7 +1114,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 				// draw body
 				select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, shift*4);
 				gfx_quads_draw(position.x+anim->body.x, position.y+anim->body.y, 4*basesize, 4*basesize);
-				
+
 				// draw eyes
 				if(p == 1)
 				{
@@ -1096,18 +1144,18 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 
 			// draw feet
 			select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, shift*4);
-			
+
 			keyframe *foot = f ? &anim->front_foot : &anim->back_foot;
-			
+
 			float w = basesize*2.5f;
 			float h = basesize*1.425f;
-			
+
 			gfx_quads_setrotation(foot->angle*pi*2);
 			gfx_quads_draw(position.x+foot->x, position.y+foot->y, w, h);
 		}
 	}
-	
-	gfx_quads_end();	
+
+	gfx_quads_end();
 }
 
 void draw_circle(float x, float y, float r, int segments)
@@ -1124,7 +1172,7 @@ void draw_circle(float x, float y, float r, int segments)
 		float sa1 = sinf(a1);
 		float sa2 = sinf(a2);
 		float sa3 = sinf(a3);
-		
+
 		gfx_quads_draw_freeform(
 			x, y,
 			x+ca1*r, y+sa1*r,
@@ -1147,7 +1195,7 @@ void draw_round_rect(float x, float y, float w, float h, float r)
 		float sa1 = sinf(a1);
 		float sa2 = sinf(a2);
 		float sa3 = sinf(a3);
-		
+
 		gfx_quads_draw_freeform(
 			x+r, y+r,
 			x+(1-ca1)*r, y+(1-sa1)*r,
@@ -1172,7 +1220,7 @@ void draw_round_rect(float x, float y, float w, float h, float r)
 			x+w-r+ca3*r, y+h-r+sa3*r,
 			x+w-r+ca2*r, y+h-r+sa2*r);
 	}
-	
+
 	gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center
 	gfx_quads_drawTL(x+r, y, w-r*2, r); // top
 	gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom
@@ -1180,41 +1228,48 @@ void draw_round_rect(float x, float y, float w, float h, float r)
 	gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right
 }
 
-static void render_player(const obj_player *prev_obj, const obj_player *player_obj)
+static 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
+	)
 {
-	obj_player prev;
-	obj_player player;
-	prev = *prev_obj;
-	player = *player_obj;
-	
+	obj_player_character prev;
+	obj_player_character player;
+	prev = *prev_char;
+	player = *player_char;
+
+	obj_player_info info = *player_info;
+
 	float intratick = client_intratick();
-	
+
 	if(player.health < 0) // dont render dead players
 		return;
-	
-	if(player.local)
+
+	if(info.local)
 	{
 		// apply predicted results
 		predicted_player.write(&player);
 		predicted_prev_player.write(&prev);
 		intratick = client_intrapredtick();
 	}
-		
-	int skin = charids[player.clientid];
-	
+
+	int skin = charids[info.clientid];
+
 	if(gametype != GAMETYPE_DM)
-		skin = player.team*9; // 0 or 9
+		skin = info.team*9; // 0 or 9
 
 	vec2 direction = get_direction(player.angle);
 	float angle = player.angle/256.0f;
 	vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick);
-	
+
 	if(prev.health < 0) // Don't flicker from previous position
 		position = vec2(player.x, player.y);
-	
+
 	bool stationary = player.vx < 1 && player.vx > -1;
 	bool inair = col_check_point(player.x, player.y+16) == 0;
-	
+
 	// evaluate animation
 	float walk_time = fmod(position.x, 100.0f)/100.0f;
 	animstate state;
@@ -1237,7 +1292,7 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 		float a = clamp((client_tick()-player.attacktick+intratick)/40.0f, 0.0f, 1.0f);
 		anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f);
 	}
-		
+
 	// draw hook
 	if (prev.hook_state>0 && player.hook_state>0)
 	{
@@ -1247,16 +1302,16 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 
 		vec2 pos = position;
 		vec2 hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick);
-		
+
 		float d = distance(pos, hook_pos);
 		vec2 dir = normalize(pos-hook_pos);
 
 		gfx_quads_setrotation(get_angle(dir)+pi);
-		
+
 		// render head
 		select_sprite(SPRITE_HOOK_HEAD);
 		gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16);
-		
+
 		// render chain
 		select_sprite(SPRITE_HOOK_CHAIN);
 		for(float f = 24; f < d; f += 24)
@@ -1305,7 +1360,7 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 		{
 			p = position;
 			p.y += data->weapons[iw].offsety;
-			
+
 			if(direction.x < 0)
 			{
 				gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
@@ -1351,7 +1406,7 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 			p.y += data->weapons[iw].offsety;
 			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
 		}
-		
+
 		if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
 		{
 			// check if we're firing stuff
@@ -1392,16 +1447,16 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 			case WEAPON_SHOTGUN: render_hand(skin, p, direction, -pi/2, vec2(-5, 4)); break;
 			case WEAPON_ROCKET: render_hand(skin, p, direction, -pi/2, vec2(-4, 7)); break;
 		}
-		
+
 	}
 
 	// render the tee
-	if(player.local && config.debug)
+	if(info.local && config.debug)
 	{
-		vec2 ghost_position = mix(vec2(prev_obj->x, prev_obj->y), vec2(player_obj->x, player_obj->y), client_intratick());
+		vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick());
 		render_tee(&state, 15, player.emote, direction, ghost_position); // render ghost
 	}
-	
+
 	render_tee(&state, skin, player.emote, direction, position);
 
 	if(player.state == STATE_CHATTING)
@@ -1413,13 +1468,13 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 		gfx_quads_end();
 	}
 
-	if (client_datas[player.clientid].emoticon_start != -1 && client_datas[player.clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
+	if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].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[player.clientid].emoticon_start;
-		int from_end = client_datas[player.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
+		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();
 
 		float a = 1;
 
@@ -1440,7 +1495,7 @@ static void render_player(const obj_player *prev_obj, const obj_player *player_o
 
 		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[player.clientid].emoticon);
+		select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon);
 		gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
 		gfx_quads_end();
 	}
@@ -1461,7 +1516,7 @@ void render_sun(float x, float y)
 		float size = (1.0f/(float)rays)*0.25f;
 		vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f));
 		vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f));
-		
+
 		gfx_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f);
 		gfx_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f);
 		gfx_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f);
@@ -1475,11 +1530,11 @@ void render_sun(float x, float y)
 	}
 	gfx_quads_end();
 	gfx_blend_normal();
-	
+
 	gfx_texture_set(data->images[IMAGE_SUN].id);
 	gfx_quads_begin();
 	gfx_quads_draw(pos.x, pos.y, 256, 256);
-	gfx_quads_end();	
+	gfx_quads_end();
 }
 
 static bool emoticon_selector_inactive_override = false;
@@ -1507,7 +1562,7 @@ int emoticon_selector_render()
 
 	static bool mouse_down = false;
 	bool return_now = false;
-	int selected_emoticon; 
+	int selected_emoticon;
 
 	if (length(emoticon_selector_mouse) < 50)
 		selected_emoticon = -1;
@@ -1533,7 +1588,7 @@ int emoticon_selector_render()
 	gfx_mapscreen(0,0,400,300);
 
 	gfx_blend_normal();
-	
+
 	gfx_texture_set(-1);
 	gfx_quads_begin();
 	gfx_setcolor(0,0,0,0.3f);
@@ -1570,7 +1625,7 @@ int emoticon_selector_render()
 	return return_now ? selected_emoticon : -1;
 }
 
-void render_goals(obj_game *gameobj, float x, float y, float w)
+void render_goals(float x, float y, float w)
 {
 	float h = 50.0f;
 
@@ -1580,7 +1635,7 @@ void render_goals(obj_game *gameobj, float x, float y, float w)
 	gfx_setcolor(0,0,0,0.5f);
 	draw_round_rect(x-10.f, y-10.f, w, h, 10.0f);
 	gfx_quads_end();
-	
+
 	// render goals
 	//y = ystart+h-54;
 	if(gameobj && gameobj->time_limit)
@@ -1598,7 +1653,7 @@ void render_goals(obj_game *gameobj, float x, float y, float w)
 
 }
 
-void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, const char *title)
+void render_scoreboard(float x, float y, float w, int team, const char *title)
 {
 	//float w = 550.0f;
 	//float x = width/2-w/2;
@@ -1608,8 +1663,8 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 
 	animstate idlestate;
 	anim_eval(&data->animations[ANIM_BASE], 0, &idlestate);
-	anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f);	
-	
+	anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f);
+
 	//float ystart = y;
 	float h = 600.0f;
 
@@ -1619,7 +1674,7 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 	gfx_setcolor(0,0,0,0.5f);
 	draw_round_rect(x-10.f, y-10.f, w, h, 40.0f);
 	gfx_quads_end();
-	
+
 	// render title
 	if(!title)
 	{
@@ -1638,15 +1693,15 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 	else
 	{
 		gfx_pretty_text(x+10, y, 64, title, -1);
-		
+
 		char buf[128];
 		sprintf(buf, "%d", gameobj->teamscore[team&1]);
 		tw = gfx_pretty_text_width(64, buf, -1);
 		gfx_pretty_text(x+w-tw-40, y, 64, buf, -1);
 	}
-	
+
 	y += 64.0f;
-	
+
 	/*
 	if(team)
 	{
@@ -1657,20 +1712,20 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 
 
 	// find players
-	const obj_player *players[MAX_CLIENTS] = {0};
+	const obj_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)
+
+		if(item.type == OBJTYPE_PLAYER_INFO)
 		{
-			players[num_players] = (const obj_player *)data;
+			players[num_players] = (const obj_player_info *)data;
 			num_players++;
 		}
 	}
-	
+
 	// sort players
 	for(int k = 0; k < num_players; k++) // ffs, bubblesort
 	{
@@ -1678,7 +1733,7 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 		{
 			if(players[i]->score < players[i+1]->score)
 			{
-				const obj_player *tmp = players[i];
+				const obj_player_info *tmp = players[i];
 				players[i] = players[i+1];
 				players[i+1] = tmp;
 			}
@@ -1694,15 +1749,15 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 	// render player scores
 	for(int i = 0; i < num_players; i++)
 	{
-		const obj_player *player = players[i];
-		
+		const obj_player_info *info = players[i];
+
 		// make sure that we render the correct team
-		if(team != -1 && player->team != team)
+		if(team != -1 && info->team != team)
 			continue;
-		
+
 		char buf[128];
 		float font_size = 46.0f;
-		if(player->local)
+		if(info->local)
 		{
 			// background so it's easy to find the local player
 			gfx_texture_set(-1);
@@ -1711,17 +1766,17 @@ void render_scoreboard(obj_game *gameobj, float x, float y, float w, int team, c
 			draw_round_rect(x, y, w-20, 48, 20.0f);
 			gfx_quads_end();
 		}
-		
-		sprintf(buf, "%4d", player->score);
+
+		sprintf(buf, "%4d", info->score);
 		gfx_pretty_text(x+60-gfx_pretty_text_width(font_size,buf,-1), y, font_size, buf, -1);
-		gfx_pretty_text(x+128, y, font_size, client_datas[player->clientid].name, -1);
-		
-		sprintf(buf, "%4d", player->latency);
+		gfx_pretty_text(x+128, y, font_size, client_datas[info->clientid].name, -1);
+
+		sprintf(buf, "%4d", info->latency);
 		float tw = gfx_pretty_text_width(font_size, buf, -1);
 		gfx_pretty_text(x+w-tw-35, y, font_size, buf, -1);
 
 		// render avatar
-		render_tee(&idlestate, player->clientid, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28));
+		render_tee(&idlestate, info->clientid, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28));
 		y += 50.0f;
 	}
 }
@@ -1737,23 +1792,19 @@ void mapscreen_to_world(float center_x, float center_y, float zoom)
 // renders the complete game world
 void render_world(float center_x, float center_y, float zoom)
 {
-	const float default_zoom = 3.0f;
-	float width = 400*default_zoom*zoom;
-	float height = 300*default_zoom*zoom;
-	
-	gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2);
-	
-	// draw background
+	mapscreen_to_world(center_x, center_y, zoom);
+	//gfx_mapscreen(center_x-width/2, center_y-height/2, center_x+width/2, center_y+height/2);
 
 	// draw the sun
 	if(config.gfx_high_detail)
 	{
 		render_sun(20+center_x*0.6f, 20+center_y*0.6f);
-		
+
+		// draw clouds
 		static vec2 cloud_pos[6] = {vec2(-500,0),vec2(-500,200),vec2(-500,400)};
 		static float cloud_speed[6] = {30, 20, 10};
 		static int cloud_sprites[6] = {SPRITE_CLOUD1, SPRITE_CLOUD2, SPRITE_CLOUD3};
-		
+
 		gfx_texture_set(data->images[IMAGE_CLOUDS].id);
 		gfx_quads_begin();
 		for(int i = 0; i < 3; i++)
@@ -1765,7 +1816,7 @@ void render_world(float center_x, float center_y, float zoom)
 		}
 		gfx_quads_end();
 
-		
+
 		// draw backdrop
 		gfx_texture_set(data->images[IMAGE_BACKDROP].id);
 		gfx_quads_begin();
@@ -1774,10 +1825,10 @@ void render_world(float center_x, float center_y, float zoom)
 			gfx_quads_drawTL(1024*x+center_x*parallax_amount, (center_y)*parallax_amount+150+512, 1024, 512);
 		gfx_quads_end();
 	}
-	
+
 	// render background tilemaps
 	tilemap_render(32.0f, 0);
-	
+
 	// render items
 	{
 		int num = snap_num_items(SNAP_CURRENT);
@@ -1785,7 +1836,7 @@ void render_world(float center_x, float center_y, float zoom)
 		{
 			SNAP_ITEM item;
 			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-			
+
 			if(item.type == OBJTYPE_PROJECTILE)
 			{
 				const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
@@ -1807,24 +1858,31 @@ void render_world(float center_x, float center_y, float zoom)
 		}
 	}
 
-	// render players above all	
+	// render players above all
 	{
 		int num = snap_num_items(SNAP_CURRENT);
 		for(int i = 0; i < num; i++)
 		{
 			SNAP_ITEM item;
 			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-			
-			if(item.type == OBJTYPE_PLAYER)
+
+			if(item.type == OBJTYPE_PLAYER_CHARACTER)
 			{
 				const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
-				if(prev)
+				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);
+				if(prev && prev_info && info)
 				{
-					client_datas[((const obj_player *)data)->clientid].team = ((const obj_player *)data)->team;
-					render_player((const obj_player *)prev, (const obj_player *)data);
+					client_datas[((const obj_player_info *)data)->clientid].team = ((const obj_player_info *)data)->team;
+					render_player(
+							(const obj_player_character *)prev,
+							(const obj_player_character *)data,
+							(const obj_player_info *)prev_info,
+							(const obj_player_info *)info
+						);
 				}
 			}
-		}	
+		}
 	}
 
 	// render particles
@@ -1833,9 +1891,9 @@ void render_world(float center_x, float center_y, float zoom)
 
 	// render foreground tilemaps
 	tilemap_render(32.0f, 1);
-	
+
 	// render damage indications
-	damageind.render();	
+	damageind.render();
 }
 
 static void do_input(int *v, int key)
@@ -1847,51 +1905,22 @@ static void do_input(int *v, int key)
 }
 
 void render_game()
-{	
+{
 	float width = 400*3.0f;
 	float height = 300*3.0f;
-	
+
 	bool spectate = false;
-		
-	// setup world view
-	obj_game *gameobj = 0;
-	{
-		// 1. fetch local player
-		// 2. set him to the center
-		int num = snap_num_items(SNAP_CURRENT);
-		for(int i = 0; i < num; i++)
-		{
-			SNAP_ITEM item;
-			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-			
-			if(item.type == OBJTYPE_PLAYER)
-			{
-				obj_player *player = (obj_player *)data;
-				if(player->local)
-				{
-					local_player = player;
-					local_player_pos = vec2(player->x, player->y);
-					
-					const void *p = snap_find_item(SNAP_PREV, item.type, item.id);
-					if(p)
-						local_player_pos = mix(vec2(((obj_player *)p)->x, ((obj_player *)p)->y), local_player_pos, client_intratick());
-				}
-			}
-			else if(item.type == OBJTYPE_GAME)
-				gameobj = (obj_game *)data;
-		}
-	}
-	
-	local_player_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intrapredtick());
-	if(local_player && local_player->team == -1)
+
+	local_character_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intrapredtick());
+	if(local_info && local_info->team == -1)
 		spectate = true;
 
 	// set listner pos
-	snd_set_listener_pos(local_player_pos.x, local_player_pos.y);
-		
+	snd_set_listener_pos(local_character_pos.x, local_character_pos.y);
+
 	animstate idlestate;
 	anim_eval(&data->animations[ANIM_BASE], 0, &idlestate);
-	anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f);	
+	anim_eval_add(&idlestate, &data->animations[ANIM_IDLE], 0, 1.0f);
 
 	if (inp_key_down(KEY_ESC))
 	{
@@ -1900,7 +1929,7 @@ void render_game()
 		else
 			menu_active = !menu_active;
 	}
-	
+
 	// handle chat input
 	if (!menu_active)
 	{
@@ -1932,13 +1961,13 @@ void render_game()
 						}
 					}
 				}
-				
+
 				chat_mode = CHATMODE_NONE;
 			}
-			
+
 			int c = inp_last_char();
 			int k = inp_last_key();
-		
+
 			if (!(c >= 0 && c < 32))
 			{
 				if (chat_input_len < sizeof(chat_input) - 1)
@@ -1957,7 +1986,7 @@ void render_game()
 					chat_input_len--;
 				}
 			}
-			
+
 		}
 		else
 		{
@@ -1968,7 +1997,7 @@ void render_game()
 
 				if(inp_key_down(config.key_teamchat))
 					chat_mode = CHATMODE_TEAM;
-				
+
 				if(chat_mode != CHATMODE_NONE)
 				{
 					mem_zero(chat_input, sizeof(chat_input));
@@ -1977,10 +2006,10 @@ void render_game()
 			}
 		}
 	}
-	
+
 	if (!menu_active)
 		inp_clear();
-	
+
 	// fetch new input
 	if(!menu_active && (!emoticon_selector_active || emoticon_selector_inactive_override))
 	{
@@ -1994,14 +2023,14 @@ void render_game()
 				mouse_pos = normalize(mouse_pos)*600.0f;
 		}
 	}
-	
+
 	// snap input
 	{
 		static player_input input = {0};
-			
+
 		input.target_x = (int)mouse_pos.x;
 		input.target_y = (int)mouse_pos.y;
-	
+
 		if(chat_mode != CHATMODE_NONE)
 			input.state = STATE_CHATTING;
 		else if(menu_active)
@@ -2013,14 +2042,14 @@ void render_game()
 			input.right = inp_key_state(config.key_move_right);
 			input.hook = inp_key_state(config.key_hook);
 			input.jump  = inp_key_state(config.key_jump);
-			
+
 			if(!emoticon_selector_active)
 				do_input(&input.fire, config.key_fire);
-			
+
 			// weapon selection
 			do_input(&input.next_weapon, config.key_next_weapon);
 			do_input(&input.prev_weapon, config.key_prev_weapon);
-			
+
 			if(inp_key_presses(config.key_next_weapon) || inp_key_presses(config.key_prev_weapon))
 				input.wanted_weapon = 0;
 			else
@@ -2033,31 +2062,26 @@ void render_game()
 				if(inp_key_presses(config.key_weapon6)) input.wanted_weapon = 6;
 			}
 		}
-		
+
 		// stress testing
 		if(config.stress)
 		{
-			//float t = client_localtime();
+			float t = client_localtime();
 			mem_zero(&input, sizeof(input));
-			
-			/*
-			input.left = 1;
-			input.jump = ((int)t)&1;
-			input.fire = ((int)(t*10))&1;
-			input.hook = ((int)t)&1;
-			input.activeweapon = ((int)t)%NUM_WEAPONS;
+
+			input.left = ((int)t/2)&1;
+			input.right = ((int)t/2+1)&1;
+			input.jump = ((int)t);
+			input.fire = ((int)(t*10));
+			input.hook = ((int)(t*2))&1;
+			input.wanted_weapon = ((int)t)%NUM_WEAPONS;
 			input.target_x = (int)(sinf(t*3)*100.0f);
 			input.target_y = (int)(cosf(t*3)*100.0f);
-			*/
-			
-			//input.target_x = (int)((rand()/(float)RAND_MAX)*64-32);
-			//input.target_y = (int)((rand()/(float)RAND_MAX)*64-32);
-			
 		}
 
 		snap_input(&input, sizeof(input));
 	}
-	
+
 	// everything updated, do events
 	if(must_process_events)
 		process_events(SNAP_PREV);
@@ -2074,18 +2098,40 @@ void render_game()
 		offx = offx*2/3;
 		offy = offy*2/3;
 	}
-	
+
 	// render the world
 	gfx_clear(0.65f,0.78f,0.9f);
 	if(spectate)
 		render_world(mouse_pos.x, mouse_pos.y, 1.0f);
 	else
-		render_world(local_player_pos.x+offx, local_player_pos.y+offy, 1.0f);
+	{
+		render_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f);
+
+		// draw screen box
+		if(0)
+		{
+			gfx_texture_set(-1);
+			gfx_blend_normal();
+			gfx_lines_begin();
+				float cx = local_character_pos.x+offx;
+				float cy = local_character_pos.y+offy;
+				float w = 400*3/2;
+				float h = 300*3/2;
+				gfx_lines_draw(cx-w,cy-h,cx+w,cy-h);
+				gfx_lines_draw(cx+w,cy-h,cx+w,cy+h);
+				gfx_lines_draw(cx+w,cy+h,cx-w,cy+h);
+				gfx_lines_draw(cx-w,cy+h,cx-w,cy-h);
+			gfx_lines_end();
+		}
+	}
 
 
 	// pseudo format
 	// ZOOM ZOOM
-	float zoom = cl_effects.getzoom(client_tick(), client_intratick(), local_player);//orgzoom + ((float)local_player->weaponstage) * 0.1f;
+	float zoom = 3.0;
+	if(local_character)
+		cl_effects.getzoom(client_tick(), client_intratick(), local_character);
+
 	// DEBUG TESTING
 	if(inp_key_pressed('M') || zoom > 3.01f)
 	{
@@ -2093,12 +2139,12 @@ void render_game()
 
 		gfx_texture_set(-1);
 		gfx_blend_normal();
-		
+
 		gfx_mask_op(MASK_NONE, 1);
-		
+
 		gfx_quads_begin();
 		gfx_setcolor(0.65f,0.78f,0.9f,1.0f);
-		
+
 		float fov;
 		if (zoom > 3.01f)
 			fov = pi * (zoom - 3.0f) / 6.0f;
@@ -2106,8 +2152,8 @@ void render_game()
 			fov = pi / 6.0f;
 
 		float fade = 0.7f;
-		
-		
+
+
 		float a = get_angle(normalize(vec2(mouse_pos.x, mouse_pos.y)));
 		vec2 d = get_dir(a);
 		vec2 d0 = get_dir(a-fov/2.0f);
@@ -2115,19 +2161,19 @@ void render_game()
 
 		vec2 cd0 = get_dir(a-(fov*fade)/2.0f); // center direction
 		vec2 cd1 = get_dir(a+(fov*fade)/2.0f);
-		
-		vec2 p0n = local_player_pos + d0*32.0f;
-		vec2 p1n = local_player_pos + d1*32.0f;
-		vec2 p0f = local_player_pos + d0*1000.0f;
-		vec2 p1f = local_player_pos + d1*1000.0f;
 
-		vec2 cn = local_player_pos + d*32.0f;
-		vec2 cf = local_player_pos + d*1000.0f;
+		vec2 p0n = local_character_pos + d0*32.0f;
+		vec2 p1n = local_character_pos + d1*32.0f;
+		vec2 p0f = local_character_pos + d0*1000.0f;
+		vec2 p1f = local_character_pos + d1*1000.0f;
 
-		vec2 cp0n = local_player_pos + cd0*32.0f;
-		vec2 cp0f = local_player_pos + cd0*1000.0f;
-		vec2 cp1n = local_player_pos + cd1*32.0f;
-		vec2 cp1f = local_player_pos + cd1*1000.0f;
+		vec2 cn = local_character_pos + d*32.0f;
+		vec2 cf = local_character_pos + d*1000.0f;
+
+		vec2 cp0n = local_character_pos + cd0*32.0f;
+		vec2 cp0f = local_character_pos + cd0*1000.0f;
+		vec2 cp1n = local_character_pos + cd1*32.0f;
+		vec2 cp1f = local_character_pos + cd1*1000.0f;
 
 		gfx_quads_draw_freeform(
 			p0n.x,p0n.y,
@@ -2135,14 +2181,14 @@ void render_game()
 			p0f.x,p0f.y,
 			p1f.x,p1f.y);
 		gfx_quads_end();
-		
+
 		gfx_mask_op(MASK_SET, 0);
-		
-		render_world(local_player_pos.x+offx, local_player_pos.y+offy, 2.0f);
-		
+
+		render_world(local_character_pos.x+offx, local_character_pos.y+offy, 2.0f);
+
 		gfx_mask_op(MASK_NONE, 0);
-		
-		mapscreen_to_world(local_player_pos.x+offx, local_player_pos.y+offy, 1.0f);
+
+		mapscreen_to_world(local_character_pos.x+offx, local_character_pos.y+offy, 1.0f);
 
 		gfx_texture_set(-1);
 		gfx_blend_normal();
@@ -2150,7 +2196,7 @@ void render_game()
 		gfx_setcolor(0.5f,0.9f,0.5f,0.25f);
 		float r=0.5f, g=1.0f, b=0.5f;
 		float r2=r*0.25f, g2=g*0.25f, b2=b*0.25f;
-		
+
 		gfx_setcolor(r,g,b,0.2f);
 		gfx_quads_draw_freeform(
 			cn.x,cn.y,
@@ -2173,28 +2219,24 @@ void render_game()
 			p1n.x,p1n.y,
 			cp1f.x,cp1f.y,
 			p1f.x,p1f.y);
-			
+
 		gfx_quads_end();
-				
-		
+
+
 		//gfx_mapscreen(0,0,400*3,300*3);
 	}
-	
-	if(local_player && !spectate)
+
+	if(local_character && !spectate)
 	{
 		gfx_texture_set(data->images[IMAGE_GAME].id);
 		gfx_quads_begin();
-		
+
 		// render cursor
 		if (!menu_active && (!emoticon_selector_active || emoticon_selector_inactive_override))
 		{
-			//float width = 400 * cl_effects.getorgzoom();
-			//float height = 300 * cl_effects.getorgzoom();
-			//gfx_mapscreen(screen_x-width/2, screen_y-height/2, screen_x+width/2, screen_y+height/2);
-
-			select_sprite(data->weapons[local_player->weapon%data->num_weapons].sprite_cursor);
+			select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_cursor);
 			float cursorsize = 64;
-			draw_sprite(local_player_pos.x+mouse_pos.x, local_player_pos.y+mouse_pos.y, cursorsize);
+			draw_sprite(local_character_pos.x+mouse_pos.x, local_character_pos.y+mouse_pos.y, cursorsize);
 		}
 
 		// render ammo count
@@ -2204,10 +2246,10 @@ void render_game()
 		gfx_mapscreen(0,0,400,300);
 		// if weaponstage is active, put a "glow" around the stage ammo
 		select_sprite(SPRITE_TEE_BODY);
-		for (int i = 0; i < local_player->weaponstage; i++)
-			gfx_quads_drawTL(local_player->ammocount * 12 -i*12, 32, 11, 11);
-		select_sprite(data->weapons[local_player->weapon%data->num_weapons].sprite_proj);
-		for (int i = 0; i < local_player->ammocount; i++)
+		for (int i = 0; i < local_character->weaponstage; i++)
+			gfx_quads_drawTL(local_character->ammocount * 12 -i*12, 32, 11, 11);
+		select_sprite(data->weapons[local_character->weapon%data->num_weapons].sprite_proj);
+		for (int i = 0; i < local_character->ammocount; i++)
 			gfx_quads_drawTL(10+i*12,34,10,10);
 
 		gfx_quads_end();
@@ -2215,12 +2257,12 @@ void render_game()
 		gfx_texture_set(data->images[IMAGE_GAME].id);
 		gfx_quads_begin();
 		int h = 0;
-		
+
 		// render health
 		select_sprite(SPRITE_HEALTH_FULL);
-		for(; h < local_player->health; h++)
+		for(; h < local_character->health; h++)
 			gfx_quads_drawTL(10+h*12,10,10,10);
-		
+
 		select_sprite(SPRITE_HEALTH_EMPTY);
 		for(; h < 10; h++)
 			gfx_quads_drawTL(10+h*12,10,10,10);
@@ -2228,38 +2270,38 @@ void render_game()
 		// render armor meter
 		h = 0;
 		select_sprite(SPRITE_ARMOR_FULL);
-		for(; h < local_player->armor; h++)
+		for(; h < local_character->armor; h++)
 			gfx_quads_drawTL(10+h*12,22,10,10);
-		
+
 		select_sprite(SPRITE_ARMOR_EMPTY);
 		for(; h < 10; h++)
 			gfx_quads_drawTL(10+h*12,22,10,10);
 		gfx_quads_end();
 	}
-	
+
 	// render kill messages
 	{
 		gfx_mapscreen(0, 0, width*1.5f, height*1.5f);
 		float startx = width*1.5f-10.0f;
 		float y = 10.0f;
-		
+
 		for(int i = 0; i < killmsg_max; i++)
 		{
-			
+
 			int r = (killmsg_current+i+1)%killmsg_max;
 			if(client_tick() > killmsgs[r].tick+50*10)
 				continue;
-			
+
 			float font_size = 48.0f;
 			float killername_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].killer].name, -1);
 			float victimname_w = gfx_pretty_text_width(font_size, client_datas[killmsgs[r].victim].name, -1);
-			
+
 			float x = startx;
-			
+
 			// render victim name
 			x -= victimname_w;
 			gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].victim].name, -1);
-			
+
 			// render victim tee
 			x -= 24.0f;
 			int skin = gametype == GAMETYPE_TDM ? skinseed + client_datas[killmsgs[r].victim].team : killmsgs[r].victim;
@@ -2283,15 +2325,15 @@ void render_game()
 			skin = gametype == GAMETYPE_TDM ? skinseed + client_datas[killmsgs[r].killer].team : killmsgs[r].killer;
 			render_tee(&idlestate, skin, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28));
 			x -= 32.0f;
-			
+
 			// render killer name
 			x -= killername_w;
 			gfx_pretty_text(x, y, font_size, client_datas[killmsgs[r].killer].name, -1);
-			
+
 			y += 44;
 		}
 	}
-	
+
 	// render chat
 	{
 		gfx_mapscreen(0,0,400,300);
@@ -2311,9 +2353,9 @@ void render_game()
 			gfx_pretty_text(x, y, 10.0f, buf, 380);
 			starty = y;
 		}
-		
+
 		y -= 10;
-		
+
 		int i;
 		for(i = 0; i < chat_max_lines; i++)
 		{
@@ -2322,20 +2364,20 @@ void render_game()
 				break;
 
 			int lines = int(gfx_pretty_text_width(10, chat_lines[r].text, -1)) / 380 + 1;
-			
+
 			gfx_pretty_text_color(1,1,1,1);
 			if(chat_lines[r].client_id == -1)
 				gfx_pretty_text_color(1,1,0.5f,1); // system
 			else if(chat_lines[r].team)
 				gfx_pretty_text_color(0.5f,1,0.5f,1); // team message
-			
+
 			gfx_pretty_text(x, y - 8 * (lines - 1), 10, chat_lines[r].text, 380);
 			y -= 8 * lines;
 		}
-		
+
 		gfx_pretty_text_color(1,1,1,1);
 	}
-	
+
 	// render goals
 	if(gameobj)
 	{
@@ -2348,13 +2390,13 @@ void render_game()
 			if(gameobj->time_limit)
 			{
 				time = gameobj->time_limit*60 - ((client_tick()-gameobj->round_start_tick)/client_tickspeed());
-				
+
 				if(gameobj->game_over)
 					time  = 0;
-			}	
+			}
 			else
 				time = (client_tick()-gameobj->round_start_tick)/client_tickspeed();
-			
+
 			sprintf(buf, "%d:%02d", time /60, time %60);
 			float w = gfx_pretty_text_width(16, buf, -1);
 			gfx_pretty_text(200-w/2, 2, 16, buf, -1);
@@ -2366,7 +2408,7 @@ void render_game()
 			float w = gfx_pretty_text_width(16, text, -1);
 			gfx_pretty_text(200-w/2, 2, 16, text, -1);
 		}
-		
+
 		if(gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF)
 		{
 			for(int t = 0; t < 2; t++)
@@ -2380,21 +2422,21 @@ void render_game()
 					gfx_setcolor(0,0,1,0.5f);
 				draw_round_rect(320+t*35, 300-15, 30, 50, 5.0f);
 				gfx_quads_end();
-				
+
 				char buf[32];
 				sprintf(buf, "%d", gameobj->teamscore[t]);
 				float w = gfx_pretty_text_width(14, buf, -1);
 				gfx_pretty_text(320+t*35+30/2-w/2, 300-15, 14, buf, -1);
 			}
 		}
-		
+
 		// render warmup timer
 		if(gameobj->warmup)
 		{
 			char buf[256];
 			float w = gfx_pretty_text_width(24, "Warmup", -1);
 			gfx_pretty_text(200+-w/2, 50, 24, "Warmup", -1);
-			
+
 			int seconds = gameobj->warmup/SERVER_TICK_SPEED;
 			if(seconds < 5)
 				sprintf(buf, "%d.%d", seconds, (gameobj->warmup*10/SERVER_TICK_SPEED)%10);
@@ -2409,7 +2451,7 @@ void render_game()
 	{
 		if (modmenu_render(true))
 			menu_active = false;
-			
+
 		//ingamemenu_render();
 		return;
 	}
@@ -2434,29 +2476,29 @@ void render_game()
 		if (emoticon != -1)
 			send_emoticon(emoticon);
 	}
-	
+
 	// render score board
 	if(inp_key_pressed(KEY_TAB) || // user requested
-		(!spectate && (local_player && local_player->health == -1)) || // not spectating and is dead
+		(!spectate && (local_character && local_character->health == -1)) || // not spectating and is dead
 		(gameobj && gameobj->game_over) // game over
 		)
 	{
 		gfx_mapscreen(0, 0, width, height);
 
 		float w = 550.0f;
-		
+
 		if (gameobj && gameobj->gametype == GAMETYPE_DM)
 		{
-			render_scoreboard(gameobj, width/2-w/2, 150.0f, w, -1, 0);
+			render_scoreboard(width/2-w/2, 150.0f, w, -1, 0);
 			//render_scoreboard(gameobj, 0, 0, -1, 0);
 		}
 		else
 		{
-			render_scoreboard(gameobj, width/2-w-20, 150.0f, w, 0, "Red Team");
-			render_scoreboard(gameobj, width/2 + 20, 150.0f, w, 1, "Blue Team");
+			render_scoreboard(width/2-w-20, 150.0f, w, 0, "Red Team");
+			render_scoreboard(width/2 + 20, 150.0f, w, 1, "Blue Team");
 		}
-		
-		render_goals(gameobj, width/2-w/2, 150+600+25, w);
+
+		render_goals(width/2-w/2, 150+600+25, w);
 
 	}
 }
@@ -2471,9 +2513,9 @@ extern "C" void modc_render()
 			snd_stop(music_menu_id);
 			music_menu_id = -1;
 		}
-		
+
 		render_game();
-		
+
 		// handle team switching
 		if(config.team != -10)
 		{
@@ -2489,12 +2531,12 @@ extern "C" void modc_render()
 		{
 			music_menu_id = snd_play(CHN_MUSIC, music_menu, SNDFLAG_LOOP);
 		}
-		
+
 		//netaddr4 server_address;
 		if(modmenu_render(false) == -1)
 			client_quit();
 	}
-	
+
 	//
 	config.team = -10;
 }
diff --git a/src/game/game.cpp b/src/game/game.cpp
index c8c6acb3..136137e8 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -101,6 +101,10 @@ void player_core::tick()
 {
 	float phys_size = 28.0f;
 	
+	#define MACRO_CHECK_VELOCITY { dbg_assert(length(vel) < 1000.0f, "velocity error"); }
+	
+	MACRO_CHECK_VELOCITY
+	
 	bool grounded = false;
 	if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
 		grounded = true;
@@ -111,6 +115,8 @@ void player_core::tick()
 
 	vel.y += gravity;
 	
+	MACRO_CHECK_VELOCITY
+	
 	float max_speed = grounded ? ground_control_speed : air_control_speed;
 	float accel = grounded ? ground_control_accel : air_control_accel;
 	float friction = grounded ? ground_friction : air_friction;
@@ -121,9 +127,13 @@ void player_core::tick()
 	if(input.right)
 		vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
 		
+	MACRO_CHECK_VELOCITY
+		
 	if(!input.left && !input.right)
 		vel.x *= friction;
 	
+	MACRO_CHECK_VELOCITY
+	
 	// handle jumping
 	if(input.jump)
 	{
@@ -136,7 +146,9 @@ void player_core::tick()
 	}
 	else
 		jumped = 0;
-		
+	
+	MACRO_CHECK_VELOCITY
+	
 	// do hook
 	if(input.hook)
 	{
@@ -247,9 +259,12 @@ void player_core::tick()
 			// check if we are under the legal limit for the hook
 			if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
 				vel = new_vel; // no problem. apply
+				
 		}
 	}
 	
+	MACRO_CHECK_VELOCITY
+	
 	if(true)
 	{
 		for(int i = 0; i < MAX_CLIENTS; i++)
@@ -265,12 +280,14 @@ void player_core::tick()
 			// handle player <-> player collision
 			float d = distance(pos, p->pos);
 			vec2 dir = normalize(pos - p->pos);
-			if(d < phys_size*1.25f)
+			if(d < phys_size*1.25f && d > 1.0f)
 			{
 				float a = phys_size*1.25f - d;
 				vel = vel + dir*a;
 			}
 			
+			MACRO_CHECK_VELOCITY
+			
 			// handle hook influence
 			if(hooked_player == i)
 			{
@@ -279,6 +296,8 @@ void player_core::tick()
 					float accel = hook_drag_accel * (d/hook_length);
 					vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x);
 					vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
+					
+					MACRO_CHECK_VELOCITY
 				}
 			}
 		}
diff --git a/src/game/game_protocol.h b/src/game/game_protocol.h
index c6fb28c7..6be89e35 100644
--- a/src/game/game_protocol.h
+++ b/src/game/game_protocol.h
@@ -22,7 +22,8 @@ enum
 {
 	OBJTYPE_NULL=0,
 	OBJTYPE_GAME,
-	OBJTYPE_PLAYER,
+	OBJTYPE_PLAYER_INFO,
+	OBJTYPE_PLAYER_CHARACTER, // use this if you are searching for the player entity
 	OBJTYPE_PROJECTILE,
 	OBJTYPE_POWERUP,
 	OBJTYPE_FLAG,
@@ -69,7 +70,7 @@ enum
 	STATE_PLAYING,
 	STATE_IN_MENU,
 	STATE_CHATTING,
-	
+
 	//GAMETYPE_DM=0,
 	//GAMETYPE_TDM,
 	//GAMETYPE_CTF,
@@ -79,46 +80,45 @@ struct player_input
 {
 	int left;
 	int right;
-	
+
 	int target_x;
 	int target_y;
-	
+
 	int jump;
 	int fire;
 	int hook;
 	int blink;
 	int state;
-	
+
 	int wanted_weapon;
 	int next_weapon;
 	int prev_weapon;
 };
 
-
-struct ev_explosion
+struct ev_common
 {
 	int x, y;
 };
 
-struct ev_spawn
+struct ev_explosion : public ev_common
 {
-	int x, y;
 };
 
-struct ev_death
+struct ev_spawn : public ev_common
 {
-	int x, y;
 };
 
-struct ev_sound
+struct ev_death : public ev_common
 {
-	int x, y;
-	int sound; // if (0x80000000 flag is set -> looping) if (0x40000000 is set -> stop looping
 };
 
-struct ev_damageind
+struct ev_sound : public ev_common
+{
+	int sound;
+};
+
+struct ev_damageind : public ev_common
 {
-	int x, y;
 	int angle;
 };
 
@@ -128,13 +128,13 @@ struct obj_game
 	int game_over;
 	int sudden_death;
 	int paused;
-	
+
 	int score_limit;
 	int time_limit;
 	int gametype;
-	
+
 	int warmup;
-	
+
 	int teamscore[2];
 };
 
@@ -156,10 +156,10 @@ struct obj_flag
 {
 	int x, y;
 	int team;
-	int local_carry;
+	int local_carry; // is set if the local player has the flag
 };
 
-
+// core object needed for physics
 struct obj_player_core
 {
 	int x, y;
@@ -172,25 +172,31 @@ struct obj_player_core
 	int hook_dx, hook_dy;
 };
 
-struct obj_player : public obj_player_core
+// info about the player that is only needed when it's on screen
+struct obj_player_character : public obj_player_core
 {
-	int local;
-	int clientid;
 	int state;
 
 	int health;
 	int armor;
 	int ammocount;
 	int weaponstage;
-	
+
 	int weapon; // current active weapon
 
+	int emote;
+
 	int attacktick; // num attack ticks left of current attack
-	
+};
+
+// information about the player that is always needed
+struct obj_player_info
+{
+	int local;
+	int clientid;
+
+	int team;
 	int score;
 	int latency;
 	int latency_flux;
-	int emote;
-	
-	int team;
 };
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index 13ea42de..57b3b18f 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -60,8 +60,12 @@ void event_handler::snap(int snapping_client)
 	{
 		if (targets[i] == -1 || targets[i] == snapping_client)
 		{
-			void *d = snap_new_item(types[i], i, sizes[i]);
-			mem_copy(d, &data[offsets[i]], sizes[i]);
+			ev_common *ev = (ev_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]);
+				mem_copy(d, &data[offsets[i]], sizes[i]);
+			}
 		}
 	}
 }
@@ -77,9 +81,9 @@ entity::entity(int objtype)
 	pos = vec2(0,0);
 	flags = FLAG_PHYSICS;
 	proximity_radius = 0;
-	
+
 	id = snap_new_id();
-	
+
 	next_entity = 0;
 	prev_entity = 0;
 	prev_type_entity = 0;
@@ -110,7 +114,7 @@ int game_world::find_entities(vec2 pos, float radius, entity **ents, int max)
 	{
 		if(!(ent->flags&entity::FLAG_PHYSICS))
 			continue;
-			
+
 		if(distance(ent->pos, pos) < radius+ent->proximity_radius)
 		{
 			ents[num] = ent;
@@ -119,7 +123,7 @@ int game_world::find_entities(vec2 pos, float radius, entity **ents, int max)
 				break;
 		}
 	}
-	
+
 	return num;
 }
 
@@ -132,7 +136,7 @@ int game_world::find_entities(vec2 pos, float radius, entity **ents, int max, co
 		{
 			if(!(ent->flags&entity::FLAG_PHYSICS))
 				continue;
-			
+
 			if(distance(ent->pos, pos) < radius+ent->proximity_radius)
 			{
 				ents[num] = ent;
@@ -142,7 +146,7 @@ int game_world::find_entities(vec2 pos, float radius, entity **ents, int max, co
 			}
 		}
 	}
-	
+
 	return num;
 }
 
@@ -154,7 +158,7 @@ void game_world::insert_entity(entity *ent)
 		dbg_assert(cur != ent, "err");
 		cur = cur->next_entity;
 	}
-	
+
 	// insert it
 	if(first_entity)
 		first_entity->prev_entity = ent;
@@ -180,7 +184,7 @@ void game_world::remove_entity(entity *ent)
 	// not in the list
 	if(!ent->next_entity && !ent->prev_entity && first_entity != ent)
 		return;
-	
+
 	// remove
 	if(ent->prev_entity)
 		ent->prev_entity->next_entity = ent->next_entity;
@@ -195,7 +199,7 @@ void game_world::remove_entity(entity *ent)
 		first_entity_types[ent->objtype] = ent->next_type_entity;
 	if(ent->next_type_entity)
 		ent->next_type_entity->prev_type_entity = ent->prev_type_entity;
-		
+
 	ent->next_entity = 0;
 	ent->prev_entity = 0;
 	ent->next_type_entity = 0;
@@ -215,11 +219,11 @@ void game_world::reset()
 	for(entity *ent = first_entity; ent; ent = ent->next_entity)
 		ent->reset();
 	remove_entities();
-	
+
 	for(entity *ent = first_entity; ent; ent = ent->next_entity)
 		ent->post_reset();
 	remove_entities();
-	
+
 	reset_requested = false;
 }
 
@@ -243,17 +247,17 @@ void game_world::tick()
 {
 	if(reset_requested)
 		reset();
-	
+
 	if(!paused)
 	{
 		// update all objects
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 			ent->tick();
-			
+
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 			ent->tick_defered();
 	}
-	
+
 	remove_entities();
 }
 
@@ -287,7 +291,7 @@ void projectile::reset()
 void projectile::tick()
 {
 	vec2 oldpos = pos;
-	
+
 	int collide = 0;
 	if(bounce)
 	{
@@ -302,31 +306,34 @@ void projectile::tick()
 		pos += vel;
 		collide = col_check_point((int)pos.x, (int)pos.y);
 	}
-	
+
 	lifespan--;
-	
+
 	// check player intersection as well
 	vec2 new_pos;
 	entity *targetplayer = (entity*)intersect_player(oldpos, pos, new_pos, powner);
-	
+
 	if(targetplayer || lifespan < 0 || collide || bounce < 0)
 	{
 		if (lifespan >= 0 || weapon == WEAPON_ROCKET)
 			create_sound(pos, sound_impact);
-			
+
 		if (flags & PROJECTILE_FLAGS_EXPLODE)
 			create_explosion(oldpos, owner, weapon, false);
 		else if (targetplayer)
 		{
 			targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner, weapon);
 		}
-			
+
 		world->destroy_entity(this);
 	}
 }
 
 void projectile::snap(int snapping_client)
 {
+	if(distance(players[snapping_client].pos, pos) > 1000.0f)
+		return;
+
 	obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile));
 	proj->x = (int)pos.x;
 	proj->y = (int)pos.y;
@@ -340,7 +347,7 @@ void projectile::snap(int snapping_client)
 //////////////////////////////////////////////////
 // TODO: move to separate file
 player::player()
-: entity(OBJTYPE_PLAYER)
+: entity(OBJTYPE_PLAYER_CHARACTER)
 {
 	init();
 }
@@ -364,7 +371,7 @@ void player::init()
 	latency_avg = 0;
 	latency_min = 0;
 	latency_max = 0;
-	
+
 	reset();
 }
 
@@ -375,16 +382,17 @@ void player::reset()
 	//direction = vec2(0.0f, 1.0f);
 	score = 0;
 	dead = true;
+	clear_flag(entity::FLAG_PHYSICS);
 	spawning = false;
 	die_tick = 0;
 	damage_taken = 0;
 	state = STATE_UNKNOWN;
-	
+
 	mem_zero(&input, sizeof(input));
 	mem_zero(&previnput, sizeof(previnput));
-	
+
 	last_action = -1;
-	
+
 	emote_stop = 0;
 	damage_taken_tick = 0;
 	attack_tick = 0;
@@ -398,7 +406,7 @@ void player::set_weapon(int w)
 	active_weapon = w;
 }
 
-	
+
 void player::respawn()
 {
 	spawning = true;
@@ -410,9 +418,9 @@ void player::set_team(int new_team)
 	team = new_team;
 	die(client_id, -1);
 	score--;
-	
+
 	dbg_msg("game", "cid=%d team=%d", client_id, team);
-	
+
 	if(team == -1)
 		clear_flag(FLAG_PHYSICS);
 	else
@@ -420,24 +428,26 @@ void player::set_team(int new_team)
 }
 
 
-bool try_spawntype(int t, vec2 *pos)
+bool try_spawntype(int t, vec2 *outpos)
 {
 	// get spawn point
 	int start, num;
 	map_get_type(t, &start, &num);
 	if(!num)
 		return false;
-		
-	mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
-	*pos = vec2((float)sp->x, (float)sp->y);
+
+	int id = rand()%num;
+	dbg_msg("spawn", "%d trying to spawn at %d", server_tick(), id);
+
+	mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + id, NULL, NULL);
+	*outpos = vec2((float)sp->x, (float)sp->y);
 	return true;
 }
 
-
 void player::try_respawn()
 {
 	vec2 spawnpos = vec2(100.0f, -60.0f);
-	
+
 	// get spawn point
 	if(gameobj->gametype == GAMETYPE_CTF)
 	{
@@ -453,23 +463,28 @@ void player::try_respawn()
 		if(!try_spawntype(MAPRES_SPAWNPOINT, &spawnpos))
 			try_spawntype(MAPRES_SPAWNPOINT_RED+(rand()&1), &spawnpos);
 	}
-		
+
 	// check if the position is occupado
 	entity *ents[2] = {0};
-	int types[] = {OBJTYPE_PLAYER};
+	int types[] = {OBJTYPE_PLAYER_CHARACTER};
 	int num_ents = world->find_entities(spawnpos, 64, ents, 2, types, 1);
 	for(int i = 0; i < num_ents; i++)
 	{
 		if(ents[i] != this)
+		{
+			dbg_msg("spawn", "%d failed", server_tick());
 			return;
+		}
 	}
-	
+
+	dbg_msg("spawn", "%d spawned", server_tick());
 	spawning = false;
 	pos = spawnpos;
-	
+
 	core.pos = pos;
+	core.vel = vec2(0,0);
 	core.hooked_player = -1;
-	
+
 
 	health = 10;
 	armor = 0;
@@ -477,12 +492,11 @@ void player::try_respawn()
 	dead = false;
 	set_flag(entity::FLAG_PHYSICS);
 	state = STATE_PLAYING;
-	
+
 	core.hook_state = HOOK_IDLE;
-	
+
 	mem_zero(&input, sizeof(input));
-	core.vel = vec2(0.0f, 0.0f);
-		
+
 	// init weapons
 	mem_zero(&weapons, sizeof(weapons));
 	weapons[WEAPON_HAMMER].got = true;
@@ -490,17 +504,19 @@ void player::try_respawn()
 	weapons[WEAPON_GUN].got = true;
 	weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo;
 
+	weapons[WEAPON_SNIPER].got = true;
+	weapons[WEAPON_SNIPER].ammo = data->weapons[WEAPON_SNIPER].maxammo;
+
 
-	
 	active_weapon = WEAPON_GUN;
 	last_weapon = WEAPON_HAMMER;
-	
+
 	reload_timer = 0;
 
 	// Create sound and spawn effects
 	create_sound(pos, SOUND_PLAYER_SPAWN);
 	create_spawn(pos);
-	
+
 	gameobj->on_player_spawn(this);
 }
 
@@ -516,14 +532,14 @@ bool player::is_grounded()
 int player::handle_ninja()
 {
 	vec2 direction = normalize(vec2(input.target_x, input.target_y));
-	
+
 	if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000))
 	{
 		// time's up, return
 		active_weapon = last_weapon;
 		return 0;
 	}
-	
+
 	// Check if it should activate
 	if ((input.fire && !(previnput.fire)) && (server_tick() > currentcooldown))
 	{
@@ -536,21 +552,21 @@ int player::handle_ninja()
 		numobjectshit = 0;
 
 		create_sound(pos, SOUND_NINJA_FIRE);
-		
+
 		// release all hooks when ninja is activated
 		//release_hooked();
 		//release_hooks();
 	}
 
 	currentmovetime--;
-	
+
 	if (currentmovetime == 0)
-	{	
+	{
 		// reset player velocity
 		core.vel *= 0.2f;
 		//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
 	}
-	
+
 	if (currentmovetime > 0)
 	{
 		// Set player velocity
@@ -563,16 +579,16 @@ int player::handle_ninja()
 		{
 			create_smoke(pos);
 		}
-		
+
 		// check if we hit anything along the way
 		{
-			int type = OBJTYPE_PLAYER;
+			int type = OBJTYPE_PLAYER_CHARACTER;
 			entity *ents[64];
 			vec2 dir = pos - oldpos;
 			float radius = phys_size * 2.0f; //length(dir * 0.5f);
 			vec2 center = oldpos + dir * 0.5f;
 			int num = world->find_entities(center, radius, ents, 64, &type, 1);
-			
+
 			for (int i = 0; i < num; i++)
 			{
 				// Check if entity is a player
@@ -591,7 +607,7 @@ int player::handle_ninja()
 				// check so we are sufficiently close
 				if (distance(ents[i]->pos, pos) > (phys_size * 2.0f))
 					continue;
-			
+
 				// hit a player, give him damage and stuffs...
 				create_sound(ents[i]->pos, SOUND_NINJA_HIT);
 				// set his velocity to fast upward (for now)
@@ -624,8 +640,8 @@ static input_count count_input(int prev, int cur)
 		else
 			c.releases++;
 	}
-	
-	return c;		
+
+	return c;
 }
 
 int player::handle_sniper()
@@ -693,7 +709,7 @@ int player::handle_sniper()
 int player::handle_weapons()
 {
 	vec2 direction = normalize(vec2(input.target_x, input.target_y));
-	
+
 	if(config.stress)
 	{
 		for(int i = 0; i < NUM_WEAPONS; i++)
@@ -701,11 +717,11 @@ int player::handle_weapons()
 			weapons[i].got = true;
 			weapons[i].ammo = 10;
 		}
-		
+
 		if(reload_timer) // twice as fast reload
 			reload_timer--;
 	}
-	
+
 	// check reload timer
 	if(reload_timer)
 	{
@@ -737,10 +753,10 @@ int player::handle_weapons()
 			if(weapons[new_weapon].got)
 				prev--;
 		}
-		
+
 		if(input.wanted_weapon) // direct weapon selection
 			new_weapon = input.wanted_weapon-1;
-				
+
 		if(new_weapon != active_weapon && new_weapon >= 0 && new_weapon < NUM_WEAPONS && weapons[new_weapon].got)
 		{
 			if(active_weapon != new_weapon)
@@ -756,7 +772,7 @@ int player::handle_weapons()
 		// don't update other weapons while sniper is active
 		return handle_sniper();
 	}
-	
+
 	if(count_input(previnput.fire, input.fire).presses) //previnput.fire != input.fire && (input.fire&1))
 	{
 		if(reload_timer == 0)
@@ -812,9 +828,9 @@ int player::handle_weapons()
 						}
 						create_sound(pos, SOUND_SHOTGUN_FIRE);
 						break;
-					}	
+					}
 				}
-				
+
 				weapons[active_weapon].ammo--;
 				attack_tick = server_tick();
 				reload_timer = data->weapons[active_weapon].firedelay * server_tickspeed() / 1000;
@@ -825,7 +841,7 @@ int player::handle_weapons()
 			}
 		}
 	}
-	
+
 	// Update weapons
 	if (active_weapon == WEAPON_HAMMER && reload_timer > 0)
 	{
@@ -833,14 +849,14 @@ 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;
+		int type = OBJTYPE_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;
 		float radius = length(dir * 0.5f);
 		vec2 center = pos + dir * 0.5f;
 		int num = world->find_entities(center, radius, ents, 64, &type, 1);
-		
+
 		for (int i = 0; i < num; i++)
 		{
 			// Check if entity is a player
@@ -859,7 +875,7 @@ int player::handle_weapons()
 			// check so we are sufficiently close
 			if (distance(ents[i]->pos, pos) > (phys_size * 2.0f))
 				continue;
-		
+
 			// hit a player, give him damage and stuffs...
 			// create sound for bash
 			//create_sound(ents[i]->pos, sound_impact);
@@ -913,7 +929,7 @@ void player::tick()
 			latency_accum_max = max(latency_accum_max, info.latency);
 			latency_accum_min = min(latency_accum_min, info.latency);
 		}
-			
+
 		if(server_tick()%server_tickspeed() == 0)
 		{
 			latency_avg = latency_accum/server_tickspeed();
@@ -924,14 +940,14 @@ void player::tick()
 			latency_accum_max = 0;
 		}
 	}
-	
+
 	if(team == -1)
 	{
 		// spectator
 		return;
 	}
-	
-	
+
+
 	if(spawning)
 		try_respawn();
 
@@ -944,13 +960,13 @@ void player::tick()
 			respawn();
 		return;
 	}
-	
+
 	//player_core core;
 	//core.pos = pos;
 	//core.jumped = jumped;
 	core.input = input;
 	core.tick();
-	
+
 	// handle weapons
 	handle_weapons();
 	/*
@@ -959,7 +975,7 @@ void player::tick()
 		// add gravity
 		//if (!(retflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
 			//vel.y += gravity;
-	
+
 		// do the move
 		defered_pos = pos;
 		move_box(&core.pos, &vel, vec2(phys_size, phys_size), 0);
@@ -967,9 +983,9 @@ void player::tick()
 
 	//defered_pos = core.pos;
 	//jumped = core.jumped;
-	
+
 	state = input.state;
-	
+
 	// Previnput
 	previnput = input;
 	return;
@@ -979,8 +995,9 @@ void player::tick_defered()
 {
 	core.move();
 	core.quantize();
+	//dbg_msg("", "%d %.0f,%.0f -> %.0f,%.0f", client_id, pos.x, pos.y, core.pos.x, core.pos.y);
 	pos = core.pos;
-	
+
 	// apply the new position
 	//pos = defered_pos;
 }
@@ -988,9 +1005,9 @@ void player::tick_defered()
 void player::die(int killer, int weapon)
 {
 	gameobj->on_player_death(this, get_player(killer), weapon);
-	
+
 	dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d", killer, players[killer].name, client_id, name, weapon);
-	
+
 	// send the kill message
 	msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL);
 	msg_pack_int(killer);
@@ -998,10 +1015,10 @@ void player::die(int killer, int weapon)
 	msg_pack_int(weapon);
 	msg_pack_end();
 	server_send_msg(-1);
-	
+
 	// a nice sound
 	create_sound(pos, SOUND_PLAYER_DIE);
-	
+
 	// set dead state
 	dead = true;
 	die_tick = server_tick();
@@ -1013,7 +1030,7 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 {
 	core.vel += force;
 
-	// player only inflicts half damage on self	
+	// player only inflicts half damage on self
 	if(from == client_id)
 		dmg = max(1, dmg/2);
 
@@ -1040,7 +1057,7 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 		armor -= 1;
 		dmg--;
 	}
-	
+
 	if(dmg > armor)
 	{
 		dmg -= armor;
@@ -1049,13 +1066,13 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 	}
 	else
 		armor -= dmg;
-	
+
 	damage_taken_tick = server_tick();
-	
+
 	// do damage hit sound
 	if(from >= 0)
 		create_targetted_sound(get_player(from)->pos, SOUND_HIT, from);
-			
+
 	// check for death
 	if(health <= 0)
 	{
@@ -1067,9 +1084,9 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 			player *p = get_player(from);
 
 			p->emote_type = EMOTE_HAPPY;
-			p->emote_stop = server_tick() + server_tickspeed(); 
+			p->emote_stop = server_tick() + server_tickspeed();
 		}
-	
+
 		return false;
 	}
 
@@ -1087,64 +1104,73 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 
 void player::snap(int snaping_client)
 {
-	obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
-
-	core.write(player);
-	
-	if(snaping_client != client_id)
+	if(1)
 	{
-		player->vx = 0; // make sure that we don't send these to clients who don't need them
-		player->vy = 0;
-		player->hook_dx = 0;
-		player->hook_dy = 0;
-	}
+		obj_player_info *info = (obj_player_info *)snap_new_item(OBJTYPE_PLAYER_INFO, client_id, sizeof(obj_player_info));
 
-	if (emote_stop < server_tick())
-	{
-		emote_type = EMOTE_NORMAL;
-		emote_stop = -1;
-	}
+		info->latency = latency_avg;
+		info->latency_flux = latency_max-latency_min;
+		info->local = 0;
+		info->clientid = client_id;
+		info->score = score;
+		info->team = team;
 
-	player->emote = emote_type;
-
-	player->latency = latency_avg;
-	player->latency_flux = latency_max-latency_min;
-
-	player->ammocount = weapons[active_weapon].ammo;
-	player->weaponstage = weapons[active_weapon].weaponstage;
-	player->health = 0;
-	player->armor = 0;
-	player->local = 0;
-	player->clientid = client_id;
-	player->weapon = active_weapon;
-	player->attacktick = attack_tick;
-	
-	if(client_id == snaping_client)
-	{
-		player->local = 1;
-		player->health = health;
-		player->armor = armor;
+		if(client_id == snaping_client)
+			info->local = 1;
 	}
-	
-	if(dead)
-		player->health = -1;
-	
-	//if(length(vel) > 15.0f)
-	//	player->emote = EMOTE_HAPPY;
-	
-	//if(damage_taken_tick+50 > server_tick())
-	//	player->emote = EMOTE_PAIN;
-	
-	if (player->emote == EMOTE_NORMAL)
+
+	if(health > 0 && distance(players[snaping_client].pos, pos) < 1000.0f)
 	{
-		if(250 - ((server_tick() - last_action)%(250)) < 5)
-			player->emote = EMOTE_BLINK;
-	}
-	
-	player->score = score;
-	player->team = team;
+		obj_player_character *character = (obj_player_character *)snap_new_item(OBJTYPE_PLAYER_CHARACTER, client_id, sizeof(obj_player_character));
+
+		core.write(character);
+
+		if(snaping_client != client_id)
+		{
+			character->vx = 0; // make sure that we don't send these to clients who don't need them
+			character->vy = 0;
+			character->hook_dx = 0;
+			character->hook_dy = 0;
+		}
+
+		if (emote_stop < server_tick())
+		{
+			emote_type = EMOTE_NORMAL;
+			emote_stop = -1;
+		}
+
+		character->emote = emote_type;
+
+		character->ammocount = weapons[active_weapon].ammo;
+		character->health = 0;
+		character->armor = 0;
+		character->weapon = active_weapon;
+		character->weaponstage = weapons[active_weapon].weaponstage;
+		character->attacktick = attack_tick;
+
+		if(client_id == snaping_client)
+		{
+			character->health = health;
+			character->armor = armor;
+		}
+
+		if(dead)
+			character->health = -1;
+
+		//if(length(vel) > 15.0f)
+		//	player->emote = EMOTE_HAPPY;
 
-	player->state = state;
+		//if(damage_taken_tick+50 > server_tick())
+		//	player->emote = EMOTE_PAIN;
+
+		if (character->emote == EMOTE_NORMAL)
+		{
+			if(250 - ((server_tick() - last_action)%(250)) < 5)
+				character->emote = EMOTE_BLINK;
+		}
+
+		character->state = state;
+	}
 }
 
 player *players;
@@ -1158,9 +1184,9 @@ powerup::powerup(int _type, int _subtype)
 	type = _type;
 	subtype = _subtype;
 	proximity_radius = phys_size;
-	
+
 	reset();
-	
+
 	// TODO: should this be done here?
 	world->insert_entity(this);
 }
@@ -1182,7 +1208,7 @@ void powerup::tick()
 		{
 			// respawn
 			spawntick = -1;
-			
+
 			if(type == POWERUP_WEAPON)
 				create_sound(pos, SOUND_WEAPON_SPAWN, 0);
 		}
@@ -1214,7 +1240,7 @@ void powerup::tick()
 				respawntime = data->powerupinfo[type].respawntime;
 			}
 			break;
-				
+
 		case POWERUP_WEAPON:
 			if(subtype >= 0 && subtype < NUM_WEAPONS)
 			{
@@ -1223,7 +1249,7 @@ void powerup::tick()
 					pplayer->weapons[subtype].got = true;
 					pplayer->weapons[subtype].ammo = min(10, pplayer->weapons[subtype].ammo + data->powerupinfo[type].amount);
 					respawntime = data->powerupinfo[type].respawntime;
-					
+
 					// TODO: data compiler should take care of stuff like this
 					if(subtype == WEAPON_ROCKET)
 						create_sound(pos, SOUND_PICKUP_ROCKET);
@@ -1244,7 +1270,7 @@ void powerup::tick()
 
 				// loop through all players, setting their emotes
 				entity *ents[64];
-				const int types[] = {OBJTYPE_PLAYER};
+				const int types[] = {OBJTYPE_PLAYER_CHARACTER};
 				int num = world->find_entities(vec2(0, 0), 1000000, ents, 64, types, 1);
 				for (int i = 0; i < num; i++)
 				{
@@ -1264,7 +1290,7 @@ void powerup::tick()
 		default:
 			break;
 		};
-		
+
 		if(respawntime >= 0)
 		{
 			dbg_msg("game", "pickup player='%d:%s' item=%d/%d", pplayer->client_id, pplayer->name, type, subtype);
@@ -1320,7 +1346,7 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 		ev->x = (int)p.x;
 		ev->y = (int)p.y;
 	}
-	
+
 	if (!bnodamage)
 	{
 		// deal damage
@@ -1404,7 +1430,7 @@ player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 	vec2 dir = pos1 - pos0;
 	float radius = length(dir * 0.5f);
 	vec2 center = pos0 + dir * 0.5f;
-	const int types[] = {OBJTYPE_PLAYER};
+	const int types[] = {OBJTYPE_PLAYER_CHARACTER};
 	int num = world->find_entities(center, radius, ents, 64, types, 1);
 	for (int i = 0; i < num; i++)
 	{
@@ -1426,7 +1452,7 @@ void send_chat(int cid, int team, const char *msg)
 		dbg_msg("chat", "%d:%d:%s: %s", cid, team, players[cid].name, msg);
 	else
 		dbg_msg("chat", "*** %s", msg);
-	
+
 	if(team == -1)
 	{
 		msg_pack_start(MSG_CHAT, MSGFLAG_VITAL);
@@ -1443,7 +1469,7 @@ void send_chat(int cid, int team, const char *msg)
 		msg_pack_int(1);
 		msg_pack_string(msg, 512);
 		msg_pack_end();
-				
+
 		for(int i = 0; i < MAX_CLIENTS; i++)
 		{
 			if(players[i].client_id != -1 && players[i].team == team)
@@ -1459,20 +1485,20 @@ void mods_tick()
 	// clear all events
 	events.clear();
 	world->tick();
-	
+
 	if(world->paused) // make sure that the game object always updates
 		gameobj->tick();
-	
+
 	if(config.restart)
 	{
 		if(config.restart > 1)
 			gameobj->do_warmup(config.restart);
 		else
 			gameobj->startround();
-			
+
 		config.restart = 0;
 	}
-	
+
 	if(config.sv_msg[0] != 0)
 	{
 		send_chat(-1, 0, config.sv_msg);
@@ -1526,7 +1552,7 @@ void mods_client_enter(int client_id)
 	players[client_id].client_id = client_id;
 	world->insert_entity(&players[client_id]);
 	players[client_id].respawn();
-	
+
 	CLIENT_INFO info; // fetch login name
 	if(server_getclientinfo(client_id, &info))
 	{
@@ -1544,13 +1570,13 @@ void mods_client_enter(int client_id)
 	else
 		players[client_id].team = gameobj->getteam(client_id);
 
-	//	
+	//
 	msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL);
 	msg_pack_int(client_id);
 	msg_pack_string(players[client_id].name, 64);
 	msg_pack_end();
 	server_send_msg(-1);
-	
+
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
 		if(players[client_id].client_id != -1)
@@ -1575,7 +1601,7 @@ void mods_client_drop(int client_id)
 	send_chat(-1, -1, buf);
 
 	dbg_msg("game", "leave player='%d:%s'", client_id, players[client_id].name);
-	
+
 	gameobj->on_player_death(&players[client_id], 0, -1);
 	world->remove_entity(&players[client_id]);
 	players[client_id].client_id = -1;
@@ -1624,12 +1650,12 @@ void mods_init()
 {
 	if(!data) /* only load once */
 		data = load_data_from_memory(internal_data);
-		
+
 	col_init(32);
 
 	world = new game_world;
 	players = new player[MAX_CLIENTS];
-	
+
 	// select gametype
 	if(strcmp(config.gametype, "ctf") == 0)
 		gameobj = new gameobject_ctf;
@@ -1637,8 +1663,8 @@ void mods_init()
 		gameobj = new gameobject_tdm;
 	else
 		gameobj = new gameobject_dm;
-	
-	// setup core world	
+
+	// setup core world
 	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
 		players[i].core.world = &world->core;
@@ -1648,15 +1674,15 @@ void mods_init()
 	//
 	int start, num;
 	map_get_type(MAPRES_ITEM, &start, &num);
-	
+
 	// TODO: this is way more complicated then it should be
 	for(int i = 0; i < num; i++)
 	{
 		mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0);
-		
+
 		int type = -1;
 		int subtype = 0;
-		
+
 		switch(it->type)
 		{
 		case ITEM_WEAPON_GUN:
@@ -1675,11 +1701,11 @@ void mods_init()
 			type = POWERUP_WEAPON;
 			subtype = WEAPON_HAMMER;
 			break;
-		
+
 		case ITEM_HEALTH:
 			type = POWERUP_HEALTH;
 			break;
-		
+
 		case ITEM_ARMOR:
 			type = POWERUP_ARMOR;
 			break;
@@ -1689,7 +1715,7 @@ void mods_init()
 			subtype = WEAPON_NINJA;
 			break;
 		};
-		
+
 		if(type != -1)
 		{
 			 // LOL, the only new in the entire game code
@@ -1698,13 +1724,13 @@ void mods_init()
 			ppower->pos = vec2(it->x, it->y);
 		}
 	}
-	
+
 	if(gameobj->gametype == GAMETYPE_CTF)
 	{
 	}
-	
+
 	world->insert_entity(gameobj);
-	
+
 
 	if(config.dbg_bots)
 	{
@@ -1726,7 +1752,7 @@ void mods_init()
 				count = -1;
 			}
 		}*/
-	}	
+	}
 }
 
 void mods_shutdown()
@@ -1738,7 +1764,7 @@ void mods_shutdown()
 	players = 0;
 	world = 0;
 }
-	
+
 void mods_presnap() {}
 void mods_postsnap() {}
 
diff --git a/src/game/server/srv_ctf.cpp b/src/game/server/srv_ctf.cpp
index 1831db31..57d81aef 100644
--- a/src/game/server/srv_ctf.cpp
+++ b/src/game/server/srv_ctf.cpp
@@ -68,7 +68,7 @@ void gameobject_ctf::tick()
 		else
 		{
 			player *players[MAX_CLIENTS];
-			int types[] = {OBJTYPE_PLAYER};
+			int types[] = {OBJTYPE_PLAYER_CHARACTER};
 			int num = world->find_entities(f->pos, 32.0f, (entity**)players, MAX_CLIENTS, types, 1);
 			for(int i = 0; i < num; i++)
 			{
@@ -96,9 +96,7 @@ void gameobject_ctf::tick()
 	}
 }
 
-//////////////////////////////////////////////////
-// FLAG
-//////////////////////////////////////////////////
+// Flag
 flag::flag(int _team)
 : entity(OBJTYPE_FLAG)
 {
@@ -135,4 +133,3 @@ void flag::snap(int snapping_client)
 	else
 		flag->local_carry = 0;
 }
-// FLAG END ///////////////////////