about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-09-09 18:21:14 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-09-09 18:21:14 +0000
commit3f4587ede831760b2f6976960658d202c9d847ff (patch)
tree6c3a22251906673e5e7491dcd86faec23887691b /src/game
parent350e968f514a00d4a89395ed13e415103390b9d9 (diff)
downloadzcatch-3f4587ede831760b2f6976960658d202c9d847ff.tar.gz
zcatch-3f4587ede831760b2f6976960658d202c9d847ff.zip
a lot of changes. client side prediction added
Diffstat (limited to 'src/game')
-rw-r--r--src/game/client/game_client.cpp169
-rw-r--r--src/game/game.h74
-rw-r--r--src/game/game_protocol.h37
-rw-r--r--src/game/game_variables.h3
-rw-r--r--src/game/server/game_server.cpp437
-rw-r--r--src/game/server/game_server.h29
6 files changed, 314 insertions, 435 deletions
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index 87fe767c..cc91c923 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -39,6 +39,7 @@ struct client_data
 	int team;
 	int emoticon;
 	int emoticon_start;
+	player_core predicted;
 } client_datas[MAX_CLIENTS];
 
 inline float frandom() { return rand()/(float)(RAND_MAX); }
@@ -137,6 +138,7 @@ void draw_sprite(float x, float y, float size)
 	gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale);
 }
 
+/*
 void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity)
 {
 	vec2 pos = *inout_pos;
@@ -166,7 +168,7 @@ void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity)
 	{
 		*inout_pos = pos + vel;
 	}
-}
+}*/
 
 class damage_indicators
 {
@@ -309,7 +311,7 @@ public:
 			particles[i].vel.y += particles[i].gravity*time_passed;
 			particles[i].vel *= particles[i].friction;
 			vec2 vel = particles[i].vel*time_passed;
-			move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom());
+			move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL);
 			particles[i].vel = vel* (1.0f/time_passed);
 			particles[i].life += time_passed;
 			particles[i].rot += time_passed * particles[i].rotspeed;
@@ -681,6 +683,75 @@ static void process_events(int s)
 	must_process_events = false;
 }
 
+static player_core predicted_prev_player;
+static player_core predicted_player;
+
+extern "C" void modc_predict()
+{
+	// repredict player
+	{
+		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)
+			{
+				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;
+			}
+		}
+		
+		// predict
+		for(int tick = client_tick(); tick <= client_predtick(); tick++)
+		{
+			// first calculate where everyone should move
+			for(int c = 0; c < MAX_CLIENTS; c++)
+			{
+				if(!world.players[c])
+					continue;
+				
+				mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
+				if(local_cid == c)
+				{
+					// apply player input
+					int *input = client_get_input(tick);
+					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)
+		{
+			predicted_prev_player = predicted_player;
+			predicted_player = *world.players[local_cid];
+		}
+	}
+}
+
 extern "C" void modc_newsnapshot()
 {
 	if(must_process_events)
@@ -945,7 +1016,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 		// first pass we draw the outline
 		// second pass we draw the filling
 		int outline = p==0 ? 1 : 0;
-		int shift = charids[skin%16];
+		int shift = skin;
 		
 		for(int f = 0; f < 2; f++)
 		{
@@ -1070,22 +1141,37 @@ 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, const obj_player *player)
+static void render_player(const obj_player *prev_obj, const obj_player *player_obj)
 {
-	if(player->health < 0) // dont render dead players
+	obj_player prev;
+	obj_player player;
+	prev = *prev_obj;
+	player = *player_obj;
+	
+	if(player.health < 0) // dont render dead players
 		return;
+	
+	if(player.local)
+	{
+		// apply predicted results
+		predicted_player.write(&player);
+		predicted_prev_player.write(&prev);
+	}
 		
-	int skin = gametype == GAMETYPE_DM ? player->clientid : skinseed + player->team;
+	int skin = charids[player.clientid];
+	
+	if(gametype != GAMETYPE_DM)
+		skin = player.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), client_intratick());
+	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), client_intratick());
 	
-	if(prev->health < 0) // Don't flicker from previous position
-		position = vec2(player->x, player->y);
+	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;
+	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;
@@ -1099,26 +1185,26 @@ static void render_player(const obj_player *prev, const obj_player *player)
 	else
 		anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f);
 
-	if (player->weapon == WEAPON_HAMMER)
+	if (player.weapon == WEAPON_HAMMER)
 	{
-		float a = clamp((client_tick()-player->attacktick+client_intratick())/10.0f, 0.0f, 1.0f);
+		float a = clamp((client_tick()-player.attacktick+client_intratick())/10.0f, 0.0f, 1.0f);
 		anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f);
 	}
-	if (player->weapon == WEAPON_NINJA)
+	if (player.weapon == WEAPON_NINJA)
 	{
-		float a = clamp((client_tick()-player->attacktick+client_intratick())/40.0f, 0.0f, 1.0f);
+		float a = clamp((client_tick()-player.attacktick+client_intratick())/40.0f, 0.0f, 1.0f);
 		anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f);
 	}
 		
 	// draw hook
-	if (prev->hook_active && player->hook_active)
+	if (prev.hook_state>0 && player.hook_state>0)
 	{
 		gfx_texture_set(data->images[IMAGE_GAME].id);
 		gfx_quads_begin();
 		//gfx_quads_begin();
 
 		vec2 pos = position;
-		vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), client_intratick());
+		vec2 hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), client_intratick());
 		
 		float d = distance(pos, hook_pos);
 		vec2 dir = normalize(pos-hook_pos);
@@ -1150,13 +1236,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
 		gfx_quads_setrotation(state.attach.angle*pi*2+angle);
 
 		// normal weapons
-		int iw = clamp(player->weapon, 0, NUM_WEAPONS-1);
+		int iw = clamp(player.weapon, 0, NUM_WEAPONS-1);
 		select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
 
 		vec2 dir = direction;
 		float recoil = 0.0f;
 		vec2 p;
-		if (player->weapon == WEAPON_HAMMER)
+		if (player.weapon == WEAPON_HAMMER)
 		{
 			// Static position for hammer
 			p = position;
@@ -1173,7 +1259,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
 			}
 			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
 		}
-		else if (player->weapon == WEAPON_NINJA)
+		else if (player.weapon == WEAPON_NINJA)
 		{
 			p = position;
 			p.y += data->weapons[iw].offsety;
@@ -1190,13 +1276,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
 			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
 
 			// HADOKEN
-			if ((client_tick()-player->attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
+			if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
 			{
 				int itex = rand() % data->weapons[iw].nummuzzlesprites;
 				float alpha = 1.0f;
 				if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
 				{
-					vec2 dir = vec2(player->x,player->y) - vec2(prev->x, prev->y);
+					vec2 dir = vec2(player.x,player.y) - vec2(prev.x, prev.y);
 					dir = normalize(dir);
 					float hadokenangle = atan(dir.y/dir.x);
 					if (dir.x < 0.0f)
@@ -1216,7 +1302,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
 		{
 			// TODO: should be an animation
 			recoil = 0;
-			float a = (client_tick()-player->attacktick+client_intratick())/5.0f;
+			float a = (client_tick()-player.attacktick+client_intratick())/5.0f;
 			if(a < 1)
 				recoil = sinf(a*pi);
 			p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f;
@@ -1224,13 +1310,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
 			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
 		}
 		
-		if (player->weapon == WEAPON_GUN || player->weapon == WEAPON_SHOTGUN)
+		if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
 		{
 			// check if we're firing stuff
-			if (true)///prev->attackticks)
+			if (true)///prev.attackticks)
 			{
 				float alpha = 0.0f;
-				int phase1tick = (client_tick() - player->attacktick);
+				int phase1tick = (client_tick() - player.attacktick);
 				if (phase1tick < (data->weapons[iw].muzzleduration + 3))
 				{
 					float intratick = client_intratick();
@@ -1252,14 +1338,14 @@ static void render_player(const obj_player *prev, const obj_player *player)
 					draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
 					/*gfx_quads_setcolor(1.0f,1.0f,1.0f,alpha);
 					vec2 diry(-dir.y,dir.x);
-					p += dir * muzzleparams[player->weapon].offsetx + diry * offsety;
-					gfx_quads_draw(p.x,p.y,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey);*/
+					p += dir * muzzleparams[player.weapon].offsetx + diry * offsety;
+					gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/
 				}
 			}
 		}
 		gfx_quads_end();
 
-		switch (player->weapon)
+		switch (player.weapon)
 		{
 			case WEAPON_GUN: render_hand(skin, p, direction, -3*pi/4, vec2(-15, 4)); break;
 			case WEAPON_SHOTGUN: render_hand(skin, p, direction, -pi/2, vec2(-5, 4)); break;
@@ -1269,9 +1355,15 @@ static void render_player(const obj_player *prev, const obj_player *player)
 	}
 
 	// render the tee
-	render_tee(&state, skin, player->emote, direction, position);
+	if(player.local && config.debug)
+	{
+		vec2 ghost_position = mix(vec2(prev_obj->x, prev_obj->y), vec2(player_obj->x, player_obj->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)
+	if(player.state == STATE_CHATTING)
 	{
 		gfx_texture_set(data->images[IMAGE_CHAT_BUBBLES].id);
 		gfx_quads_begin();
@@ -1280,13 +1372,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
 		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[player.clientid].emoticon_start != -1 && client_datas[player.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[player.clientid].emoticon_start;
+		int from_end = client_datas[player.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
 
 		float a = 1;
 
@@ -1307,7 +1399,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
 
 		gfx_quads_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[player.clientid].emoticon);
 		gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
 		gfx_quads_end();
 	}
@@ -1726,6 +1818,9 @@ void render_game()
 		}
 	}
 	
+	local_player_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intratick());
+	//local_player_pos = predicted_player.pos;
+	
 	// everything updated, do events
 	if(must_process_events)
 		process_events(SNAP_PREV);
diff --git a/src/game/game.h b/src/game/game.h
index e3e4e99e..2a35bf97 100644
--- a/src/game/game.h
+++ b/src/game/game.h
@@ -20,6 +20,80 @@ inline float get_angle(vec2 dir)
 	return a;
 }
 
+
+template<typename T>
+inline T saturated_add(T min, T max, T current, T modifier)
+{
+	if(modifier < 0)
+	{
+		if(current < min)
+			return current;
+		current += modifier;
+		if(current < min)
+			current = min;
+		return current;
+	}
+	else
+	{
+		if(current > max)
+			return current;
+		current += modifier;
+		if(current > max)
+			current = max;
+		return current;
+	}
+}
+
+void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces);
+void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity);
+
+
+// hooking stuff
+enum
+{
+	HOOK_RETRACTED=-1,
+	HOOK_IDLE=0,
+	HOOK_FLYING,
+	HOOK_GRABBED
+};
+
+class world_core
+{
+public:
+	world_core()
+	{
+		mem_zero(players, sizeof(players));
+	}
+		
+	class player_core *players[MAX_CLIENTS];
+};
+
+class player_core
+{
+public:
+	world_core *world;
+	
+	vec2 pos;
+	vec2 vel;
+	
+	vec2 hook_pos;
+	vec2 hook_dir;
+	int hook_tick;
+	int hook_state;
+	int hooked_player;
+	
+	int jumped;
+	player_input input;
+	
+	void tick();
+	void move();
+	
+	void read(const obj_player_core *obj_core);
+	void write(obj_player_core *obj_core);
+	void quantize();
+};
+
+
 #define LERP(a,b,t) (a + (b-a) * t)
 #define min(a, b) ( a > b ? b : a)
 #define max(a, b) ( a > b ? a : b)
diff --git a/src/game/game_protocol.h b/src/game/game_protocol.h
index cd28b2d5..3ef491ef 100644
--- a/src/game/game_protocol.h
+++ b/src/game/game_protocol.h
@@ -1,5 +1,22 @@
 // NOTE: Be very careful when editing this file as it will change the network version
 
+// --------- PHYSICS TWEAK! --------
+const float ground_control_speed = 7.0f;
+const float ground_control_accel = 2.0f;
+const float ground_friction = 0.5f;
+const float ground_jump_speed = 13.5f;
+const float air_control_speed = 3.5f;
+const float air_control_accel = 1.2f;
+const float air_friction = 0.95f;
+const float hook_length = 34*10.0f;
+const float hook_fire_speed = 45.0f;
+const float hook_drag_accel = 3.0f;
+const float hook_drag_speed = 15.0f;
+const float gravity = 0.5f;
+const float wall_friction = 0.80f;
+const float wall_jump_speed_up = ground_jump_speed*0.8f;
+const float wall_jump_speed_out = ground_jump_speed*0.8f;
+
 // Network stuff
 enum
 {
@@ -129,7 +146,19 @@ struct obj_flag
 	int team;
 };
 
-struct obj_player
+
+struct obj_player_core
+{
+	int x, y;
+	int vx, vy;
+	int angle;
+
+	int hook_state;
+	int hook_x, hook_y;
+	int hook_dx, hook_dy;
+};
+
+struct obj_player : public obj_player_core
 {
 	int local;
 	int clientid;
@@ -139,10 +168,6 @@ struct obj_player
 	int armor;
 	int ammocount;
 	
-	int x, y;
-	int vx, vy;
-	int angle;
-	
 	int weapon; // current active weapon
 
 	int attacktick; // num attack ticks left of current attack
@@ -152,7 +177,5 @@ struct obj_player
 	int latency_flux;
 	int emote;
 	
-	int hook_active;
-	int hook_x, hook_y;
 	int team;
 };
diff --git a/src/game/game_variables.h b/src/game/game_variables.h
index c8cf0150..429c18aa 100644
--- a/src/game/game_variables.h
+++ b/src/game/game_variables.h
@@ -14,6 +14,9 @@ MACRO_CONFIG_INT(scorelimit, 20, 0, 1000)
 MACRO_CONFIG_INT(timelimit, 0, 0, 1000)
 MACRO_CONFIG_STR(gametype, 32, "dm")
 
+MACRO_CONFIG_INT(dbg_bots, 0, 0, 7)
+MACRO_CONFIG_INT(cl_predict, 1, 0, 1)
+
 MACRO_CONFIG_INT(dynamic_camera, 1, 0, 1)
 
 
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index ebb35cf1..7ad4ec09 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -9,26 +9,6 @@
 
 data_container *data = 0x0;
 
-// --------- DEBUG STUFF ---------
-const int debug_bots = 3;
-
-// --------- PHYSICS TWEAK! --------
-const float ground_control_speed = 7.0f;
-const float ground_control_accel = 2.0f;
-const float ground_friction = 0.5f;
-const float ground_jump_speed = 13.5f;
-const float air_control_speed = 3.5f;
-const float air_control_accel = 1.2f;
-const float air_friction = 0.95f;
-const float hook_length = 34*10.0f;
-const float hook_fire_speed = 45.0f;
-const float hook_drag_accel = 3.0f;
-const float hook_drag_speed = 15.0f;
-const float gravity = 0.5f;
-const float wall_friction = 0.80f;
-const float wall_jump_speed_up = ground_jump_speed*0.8f;
-const float wall_jump_speed_out = ground_jump_speed*0.8f;
-
 class player* get_player(int index);
 void create_damageind(vec2 p, float angle_mod, int amount);
 void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
@@ -39,134 +19,6 @@ void create_sound(vec2 pos, int sound, int loopflags = 0);
 void create_targetted_sound(vec2 pos, int sound, int target, int loopflags = 0);
 class player *intersect_player(vec2 pos0, vec2 pos1, vec2 &new_pos, class entity *notthis = 0);
 
-template<typename T>
-T saturated_add(T min, T max, T current, T modifier)
-{
-	if(modifier < 0)
-	{
-		if(current < min)
-			return current;
-		current += modifier;
-		if(current < min)
-			current = min;
-		return current;
-	}
-	else
-	{
-		if(current > max)
-			return current;
-		current += modifier;
-		if(current > max)
-			current = max;
-		return current;
-	}
-}
-
-// TODO: rewrite this smarter!
-void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
-{
-	if(bounces)
-		*bounces = 0;
-	
-	vec2 pos = *inout_pos;
-	vec2 vel = *inout_vel;
-	if(col_check_point(pos + vel))
-	{
-		int affected = 0;
-		if(col_check_point(pos.x + vel.x, pos.y))
-		{
-			inout_vel->x *= -elasticity;
-			if(bounces)
-				(*bounces)++;			
-			affected++;
-		}
-
-		if(col_check_point(pos.x, pos.y + vel.y))
-		{
-			inout_vel->y *= -elasticity;
-			if(bounces)
-				(*bounces)++;			
-			affected++;
-		}
-		
-		if(affected == 0)
-		{
-			inout_vel->x *= -elasticity;
-			inout_vel->y *= -elasticity;
-		}
-	}
-	else
-	{
-		*inout_pos = pos + vel;
-	}
-}
-
-// TODO: rewrite this smarter!
-void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
-{
-	// do the move
-	vec2 pos = *inout_pos;
-	vec2 vel = *inout_vel;
-	
-	float distance = length(vel);
-	int max = (int)distance;
-	
-	vec2 offsets[4] = {	vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2),
-						vec2(-size.x/2,  size.y/2), vec2( size.x/2,  size.y/2)};
-	
-	if(distance > 0.00001f)
-	{
-		vec2 old_pos = pos;
-		for(int i = 0; i <= max; i++)
-		{
-			float amount = i/(float)max;
-			if(max == 0)
-				amount = 0;
-			
-			vec2 new_pos = pos + vel*amount; // TODO: this row is not nice
-
-			for(int p = 0; p < 4; p++)
-			{
-				vec2 np = new_pos+offsets[p];
-				vec2 op = old_pos+offsets[p];
-				if(col_check_point(np))
-				{
-					int affected = 0;
-					if(col_check_point(np.x, op.y))
-					{
-						vel.x = -vel.x*elasticity;
-						pos.x = old_pos.x;
-						new_pos.x = old_pos.x;
-						affected++;
-					}
-
-					if(col_check_point(op.x, np.y))
-					{
-						vel.y = -vel.y*elasticity;
-						pos.y = old_pos.y;
-						new_pos.y = old_pos.y;
-						affected++;
-					}
-					
-					if(!affected)
-					{
-						new_pos = old_pos;
-						pos = old_pos;
-						vel *= -elasticity;
-					}
-				}
-			}
-			
-			old_pos = new_pos;
-		}
-		
-		pos = old_pos;
-	}
-	
-	*inout_pos = pos;
-	*inout_vel = vel;
-}
-
 //////////////////////////////////////////////////
 // Event handler
 //////////////////////////////////////////////////
@@ -784,8 +636,8 @@ void player::reset()
 	release_hooks();
 	
 	pos = vec2(0.0f, 0.0f);
-	vel = vec2(0.0f, 0.0f);
-	direction = vec2(0.0f, 1.0f);
+	core.vel = vec2(0.0f, 0.0f);
+	//direction = vec2(0.0f, 1.0f);
 	score = 0;
 	dead = true;
 	spawning = false;
@@ -863,7 +715,9 @@ void player::try_respawn()
 	
 	spawning = false;
 	pos = spawnpos;
-	defered_pos = pos;
+	
+	core.pos = pos;
+	core.hooked_player = -1;
 	
 
 	health = 10;
@@ -873,8 +727,10 @@ void player::try_respawn()
 	set_flag(entity::FLAG_ALIVE);
 	state = STATE_PLAYING;
 	
+	core.hook_state = HOOK_IDLE;
+	
 	mem_zero(&input, sizeof(input));
-	vel = vec2(0.0f, 0.0f);
+	core.vel = vec2(0.0f, 0.0f);
 		
 	// init weapons
 	mem_zero(&weapons, sizeof(weapons));
@@ -905,13 +761,14 @@ bool player::is_grounded()
 // releases the hooked player
 void player::release_hooked()
 {
-	hook_state = HOOK_RETRACTED;
-	hooked_player = 0x0;
+	//hook_state = HOOK_RETRACTED;
+	//hooked_player = 0x0;
 }
 
 // release all hooks to this player	
 void player::release_hooks()
 {
+	/*
 	// TODO: loop thru players only
 	for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
 	{
@@ -921,11 +778,13 @@ void player::release_hooks()
 			if(p->hooked_player == this)
 				p->release_hooked();
 		}
-	}
+	}*/
 }
 
 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
@@ -956,18 +815,18 @@ int player::handle_ninja()
 	if (currentmovetime == 0)
 	{	
 		// reset player velocity
-		vel *= 0.2f;
+		core.vel *= 0.2f;
 		//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
 	}
 	
 	if (currentmovetime > 0)
 	{
 		// Set player velocity
-		vel = activationdir * data->weapons[WEAPON_NINJA].velocity;
+		core.vel = activationdir * data->weapons[WEAPON_NINJA].velocity;
 		vec2 oldpos = pos;
-		move_box(&defered_pos, &vel, vec2(phys_size, phys_size), 0.0f);
+		move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f);
 		// reset velocity so the client doesn't predict stuff
-		vel = vec2(0.0f,0.0f);
+		core.vel = vec2(0.0f,0.0f);
 		if ((currentmovetime % 2) == 0)
 		{
 			create_smoke(pos);
@@ -1015,6 +874,8 @@ int player::handle_ninja()
 
 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++)
@@ -1175,7 +1036,7 @@ int player::handle_weapons()
 				dir = normalize(target->pos - pos);
 			else
 				dir = vec2(0,-1);
-			target->vel += dir * 25.0f + vec2(0,-5.0f);
+			target->core.vel += dir * 25.0f + vec2(0,-5.0f);
 		}
 	}
 	if (data->weapons[active_weapon].ammoregentime)
@@ -1228,7 +1089,6 @@ void player::tick()
 		try_respawn();
 
 	// TODO: rework the input to be more robust
-	// TODO: remove this tick count, it feels weird
 	if(dead)
 	{
 		if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec
@@ -1238,205 +1098,30 @@ void player::tick()
 		return;
 	}
 	
-	// fetch some info
-	bool grounded = is_grounded();
-	int wall_sliding = 0;
-	direction = normalize(vec2(input.target_x, input.target_y));
+	//player_core core;
+	//core.pos = pos;
+	//core.jumped = jumped;
+	core.input = input;
+	core.tick();
 	
-	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;
-	
-	if(!grounded && vel.y > 0)
-	{
-		if(input.left && col_check_point((int)(pos.x-phys_size/2)-4, (int)(pos.y)))
-			wall_sliding = -1;
-		if(input.right && col_check_point((int)(pos.x+phys_size/2)+4, (int)(pos.y)))
-			wall_sliding = 1;
-	}
-
-	if(wall_sliding)
-		vel.y *= wall_friction;
-	
-	// handle movement
-	if(input.left)
-		vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
-	if(input.right)
-		vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
-		
-	if(!input.left && !input.right)
-		vel.x *= friction;
-	
-	// handle jumping
-	if(input.jump)
-	{
-		if(!jumped && (grounded || wall_sliding))
-		{
-			create_sound(pos, SOUND_PLAYER_JUMP);
-			if(wall_sliding)
-			{
-				vel.y = -wall_jump_speed_up;
-				vel.x = -wall_jump_speed_out*wall_sliding;
-			}
-			else
-				vel.y = -ground_jump_speed;
-			jumped++;
-		}
-	}
-	else
-		jumped = 0;
-		
-	// do hook
-	if(input.hook)
-	{
-		if(hook_state == HOOK_IDLE)
-		{
-			hook_state = HOOK_FLYING;
-			hook_pos = pos;
-			hook_dir = direction;
-			hook_tick = -1;
-		}
-		else if(hook_state == HOOK_FLYING)
-		{
-			vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
-
-			// Check against other players first
-			for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
-			{
-				if(ent && ent->objtype == OBJTYPE_PLAYER)
-				{
-					player *p = (player*)ent;
-					if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size)
-					{
-						hook_state = HOOK_GRABBED;
-						hooked_player = p;
-						break;
-					}
-				}
-			}
-			
-			if(hook_state == HOOK_FLYING)
-			{
-				// check against ground
-				if(col_intersect_line(hook_pos, new_pos, &new_pos))
-				{
-					hook_state = HOOK_GRABBED;
-					hook_pos = new_pos;	
-				}
-				else if(distance(pos, new_pos) > hook_length)
-				{
-					hook_state = HOOK_RETRACTED;
-				}
-				else
-					hook_pos = new_pos;
-			}
-			
-			if(hook_state == HOOK_GRABBED)
-			{
-				create_sound(pos, SOUND_HOOK_ATTACH);
-				hook_tick = server_tick();
-			}
-		}
-	}
-	else
-	{
-		release_hooked();
-		hook_state = HOOK_IDLE;
-		hook_pos = pos;
-	}
-		
-	if(hook_state == HOOK_GRABBED)
-	{
-		if(hooked_player)
-		{
-			hook_pos = hooked_player->pos;
-			
-			// keep players hooked for a max of 1.5sec
-			if(server_tick() > hook_tick+(server_tickspeed()*3)/2)
-				release_hooked();
-		}
-			
-		/*if(hooked_player)
-			hook_pos = hooked_player->pos;
-
-		float d = distance(pos, hook_pos);
-		vec2 dir = normalize(pos - hook_pos);		
-		if(d > 10.0f) // TODO: fix tweakable variable
-		{
-			float accel = hook_drag_accel * (d/hook_length);
-			vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x*0.75f);
-			vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
-		}*/
-		
-		// Old version feels much better (to me atleast)
-		if(distance(hook_pos, pos) > 46.0f)
-		{
-			vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
-			// the hook as more power to drag you up then down.
-			// this makes it easier to get on top of an platform
-			if(hookvel.y > 0)
-				hookvel.y *= 0.3f;
-			
-			// the hook will boost it's power if the player wants to move
-			// in that direction. otherwise it will dampen everything abit
-			if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) 
-				hookvel.x *= 0.95f;
-			else
-				hookvel.x *= 0.75f;
-			
-			vec2 new_vel = vel+hookvel;
-			
-			// 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
-		}
-	}
-		
-	// fix influence of other players, collision + hook
-	// TODO: loop thru players only
-	for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
-	{
-		if(ent && ent->objtype == OBJTYPE_PLAYER)
-		{
-			player *p = (player*)ent;
-			if(p == this || !(p->flags&FLAG_ALIVE))
-				continue; // make sure that we don't nudge our self
-			
-			// handle player <-> player collision
-			float d = distance(pos, p->pos);
-			vec2 dir = normalize(pos - p->pos);
-			if(d < phys_size*1.25f)
-			{
-				float a = phys_size*1.25f - d;
-				vel = vel + dir*a;
-			}
-			
-			// handle hook influence
-			if(p->hooked_player == this)
-			{
-				if(d > phys_size*1.50f) // TODO: fix tweakable variable
-				{
-					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);
-				}
-			}
-		}
-	}
 	
 	// handle weapons
 	int retflags = handle_weapons();
+	/*
 	if (!(retflags & (MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION)))
 	{
 		// add gravity
-		if (!(retflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
-			vel.y += gravity;
+		//if (!(retflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
+			//vel.y += gravity;
 	
 		// do the move
 		defered_pos = pos;
-		move_box(&defered_pos, &vel, vec2(phys_size, phys_size), 0);
-	}
+		move_box(&core.pos, &vel, vec2(phys_size, phys_size), 0);
+	}*/
 
+	//defered_pos = core.pos;
+	//jumped = core.jumped;
+	
 	state = input.state;
 	
 	// Previnput
@@ -1446,8 +1131,12 @@ void player::tick()
 
 void player::tick_defered()
 {
+	core.move();
+	core.quantize();
+	pos = core.pos;
+	
 	// apply the new position
-	pos = defered_pos;
+	//pos = defered_pos;
 }
 
 void player::die(int killer, int weapon)
@@ -1478,13 +1167,14 @@ void player::die(int killer, int weapon)
 
 bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 {
-	vel += force;
+	core.vel += force;
 
 	// player only inflicts half damage on self	
 	if(from == client_id)
 		dmg = max(1, dmg/2);
 
-	if (gameobj->gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
+	// CTF and TDM,
+	if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team)
 		return false;
 
 	damage_taken++;
@@ -1567,10 +1257,15 @@ void player::snap(int snaping_client)
 {
 	obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
 
-	player->x = (int)pos.x;
-	player->y = (int)pos.y;
-	player->vx = (int)vel.x;
-	player->vy = (int)vel.y;
+	core.write(player);
+	
+	if(snaping_client != client_id)
+	{
+		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;
+	}
 
 	if (emote_stop < server_tick())
 	{
@@ -1613,21 +1308,6 @@ void player::snap(int snaping_client)
 			player->emote = EMOTE_BLINK;
 	}
 	
-	player->hook_active = hook_state>0?1:0;
-	player->hook_x = (int)hook_pos.x;
-	player->hook_y = (int)hook_pos.y;
-
-	float a = 0;
-	if(input.target_x == 0)
-		a = atan((float)input.target_y);
-	else
-		a = atan((float)input.target_y/(float)input.target_x);
-		
-	if(input.target_x < 0)
-		a = a+pi;
-		
-	player->angle = (int)(a*256.0f);
-	
 	player->score = score;
 	player->team = team;
 
@@ -2011,7 +1691,7 @@ void mods_tick()
 	if(world.paused) // make sure that the game object always updates
 		gameobj->tick();
 
-	if(debug_bots)
+	if(config.dbg_bots)
 	{
 		static int count = 0;
 		if(count >= 0)
@@ -2019,13 +1699,14 @@ void mods_tick()
 			count++;
 			if(count == 10)
 			{
-				for(int i = 0; i < debug_bots ; i++)
+				for(int i = 0; i < config.dbg_bots ; i++)
 				{
 					mods_client_enter(MAX_CLIENTS-i-1);
 					strcpy(players[MAX_CLIENTS-i-1].name, "(bot)");
 					if(gameobj->gametype != GAMETYPE_DM)
-						players[MAX_CLIENTS-i-1].team = count&1;
+						players[MAX_CLIENTS-i-1].team = i&1;
 				}
+				
 				count = -1;
 			}
 		}
@@ -2187,7 +1868,15 @@ void mods_init()
 
 	players = new player[MAX_CLIENTS];
 	gameobj = new gameobject;
+	
+	// setup core world	
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		players[i].core.world = &world.core;
+		world.core.players[i] = &players[i].core;
+	}
 
+	//
 	int start, num;
 	map_get_type(MAPRES_ITEM, &start, &num);
 	
diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h
index b691e0de..56f668f6 100644
--- a/src/game/server/game_server.h
+++ b/src/game/server/game_server.h
@@ -85,6 +85,8 @@ public:
 	bool paused;
 	bool reset_requested;
 	
+	world_core core;
+	
 	game_world();
 	int find_entities(vec2 pos, float radius, entity **ents, int max);
 	int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes);
@@ -229,9 +231,9 @@ public:
 	int last_action;
 	
 	// we need a defered position so we can handle the physics correctly
-	vec2 defered_pos;
-	vec2 vel;
-	vec2 direction;
+	//vec2 defered_pos;
+	//vec2 vel;
+	//vec2 direction;
 
 	//
 	int client_id;
@@ -269,21 +271,14 @@ public:
 	int latency_avg;
 	int latency_min;
 	int latency_max;
-
-	// hooking stuff
-	enum
-	{
-		HOOK_RETRACTED=-1,
-		HOOK_IDLE=0,
-		HOOK_FLYING,
-		HOOK_GRABBED
-	};
 	
-	int hook_state;
-	int hook_tick;
-	player *hooked_player;
-	vec2 hook_pos;
-	vec2 hook_dir;
+	player_core core;
+	
+	//int hook_state;
+	//int hook_tick;
+	//player *hooked_player;
+	//vec2 hook_pos;
+	//vec2 hook_dir;
 
 	//
 	player();