about summary refs log tree commit diff
path: root/src/game/client
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-09-23 07:43:41 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-09-23 07:43:41 +0000
commit33b50738e63a3c79861bcfd88cb39377f85776c4 (patch)
treea7857f0b219e02337da6d8a1a6b66693760b9b6e /src/game/client
parente21b6983abaefd0037435c76e9b41cfbbfbe51d5 (diff)
downloadzcatch-33b50738e63a3c79861bcfd88cb39377f85776c4.tar.gz
zcatch-33b50738e63a3c79861bcfd88cb39377f85776c4.zip
added dead reckoning to the characters
Diffstat (limited to 'src/game/client')
-rw-r--r--src/game/client/components/players.cpp44
-rw-r--r--src/game/client/gameclient.cpp183
-rw-r--r--src/game/client/gameclient.hpp17
3 files changed, 137 insertions, 107 deletions
diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp
index 4e315f32..43ff3e77 100644
--- a/src/game/client/components/players.cpp
+++ b/src/game/client/components/players.cpp
@@ -158,7 +158,7 @@ void PLAYERS::render_player(
 
 	bool stationary = player.vx < 1 && player.vx > -1;
 	bool inair = col_check_point(player.x, player.y+16) == 0;
-	bool want_other_dir = (player.wanted_direction == -1 && vel.x > 0) || (player.wanted_direction == 1 && vel.x < 0);
+	bool want_other_dir = (player.direction == -1 && vel.x > 0) || (player.direction == 1 && vel.x < 0);
 
 	// evaluate animation
 	float walk_time = fmod(position.x, 100.0f)/100.0f;
@@ -194,8 +194,8 @@ void PLAYERS::render_player(
 		}
 		
 		gameclient.effects->skidtrail(
-			position+vec2(-player.wanted_direction*6,12),
-			vec2(-player.wanted_direction*100*length(vel),-50)
+			position+vec2(-player.direction*6,12),
+			vec2(-player.direction*100*length(vel),-50)
 		);
 	}
 
@@ -425,27 +425,27 @@ void PLAYERS::render_player(
 
 void PLAYERS::on_render()
 {
-	int num = snap_num_items(SNAP_CURRENT);
-	for(int i = 0; i < num; i++)
+	//int num = snap_num_items(SNAP_CURRENT);
+	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		SNAP_ITEM item;
-		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
+		// only render active characters
+		if(!gameclient.snap.characters[i].active)
+			continue;
 
-		if(item.type == NETOBJTYPE_CHARACTER)
-		{
-			const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
-			const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id);
-			const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id);
+		const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, i);
+		const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i);
 
-			if(prev && prev_info && info)
-			{
-				render_player(
-						(const NETOBJ_CHARACTER *)prev,
-						(const NETOBJ_CHARACTER *)data,
-						(const NETOBJ_PLAYER_INFO *)prev_info,
-						(const NETOBJ_PLAYER_INFO *)info
-					);
-			}
-		}
+		if(prev_info && info)
+		{
+			NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev;
+			NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur;
+
+			render_player(
+					&prev_char,
+					&cur_char,
+					(const NETOBJ_PLAYER_INFO *)prev_info,
+					(const NETOBJ_PLAYER_INFO *)info
+				);
+		}		
 	}
 }
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index fe9b0476..43627669 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -150,15 +150,14 @@ void GAMECLIENT::on_console_init()
 	// add the some console commands
 	MACRO_REGISTER_COMMAND("team", "", con_team, this);
 	MACRO_REGISTER_COMMAND("kill", "", con_kill, this);
-			
+	
+	// let all the other components register their console commands
 	for(int i = 0; i < all.num; i++)
 		all.components[i]->on_console_init();
 }
 
 void GAMECLIENT::on_init()
 {
-
-
 	// init all components
 	for(int i = 0; i < all.num; i++)
 		all.components[i]->on_init();
@@ -250,7 +249,6 @@ int GAMECLIENT::on_snapinput(int *data)
 	return controls->snapinput(data);
 }
 
-
 void GAMECLIENT::on_connected()
 {
 	layers_init();
@@ -479,14 +477,31 @@ void GAMECLIENT::process_events()
 	}
 }
 
+static void evolve(NETOBJ_CHARACTER *character, int tick)
+{
+	WORLD_CORE tempworld;
+	CHARACTER_CORE tempcore;
+	mem_zero(&tempcore, sizeof(tempcore));
+	tempcore.world = &tempworld;
+	tempcore.read(character);
+	//tempcore.input.direction = character->wanted_direction;
+	while(character->tick < tick)
+	{
+		character->tick++;
+		tempcore.tick(false);
+		tempcore.move();
+		tempcore.quantize();
+	}
+	
+	tempcore.write(character);
+}
+
 void GAMECLIENT::on_snapshot()
 {
 	// clear out the invalid pointers
 	mem_zero(&gameclient.snap, sizeof(gameclient.snap));
+	snap.local_cid = -1;
 
-	static int snapshot_count = 0;
-	snapshot_count++;
-	
 	// secure snapshot
 	{
 		int num = snap_num_items(SNAP_CURRENT);
@@ -517,11 +532,10 @@ void GAMECLIENT::on_snapshot()
 		}
 	}
 
-	// setup world view
+	// go trough all the items in the snapshot and gather the info we want
 	{
-		// 1. fetch local player
-		// 2. set him to the center
-		gameclient.snap.team_size[0] = gameclient.snap.team_size[1] = 0;
+		snap.team_size[0] = snap.team_size[1] = 0;
+		
 		int num = snap_num_items(SNAP_CURRENT);
 		for(int i = 0; i < num; i++)
 		{
@@ -532,88 +546,94 @@ void GAMECLIENT::on_snapshot()
 			{
 				const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
 				
-				gameclient.clients[info->cid].team = info->team;
+				clients[info->cid].team = info->team;
+				snap.player_infos[info->cid] = info;
 				
 				if(info->local)
 				{
-					gameclient.snap.local_info = info;
-					const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id);
-					if(data)
-					{
-						gameclient.snap.local_character = (const NETOBJ_CHARACTER *)data;
-						gameclient.local_character_pos = vec2(gameclient.snap.local_character->x, gameclient.snap.local_character->y);
-
-						const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
-						if(p)
-							gameclient.snap.local_prev_character = (NETOBJ_CHARACTER *)p;
-					}
+					snap.local_cid = item.id;
+					snap.local_info = info;
 					
 					if (info->team == -1)
-						gameclient.snap.spectate = true;
+						snap.spectate = true;
 				}
 				
 				// calculate team-balance
 				if(info->team != -1)
-					gameclient.snap.team_size[info->team]++;
+					snap.team_size[info->team]++;
 				
 			}
-			else if(item.type == NETOBJTYPE_GAME)
-				gameclient.snap.gameobj = (NETOBJ_GAME *)data;
-			else if(item.type == NETOBJTYPE_FLAG)
+			else if(item.type == NETOBJTYPE_CHARACTER)
 			{
-				gameclient.snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
+				const void *old = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
+				if(old)
+				{
+					snap.characters[item.id].active = true;
+					snap.characters[item.id].prev = *((const NETOBJ_CHARACTER *)old);
+					snap.characters[item.id].cur = *((const NETOBJ_CHARACTER *)data);
+					
+					// perform dead reckoning
+					if(snap.characters[item.id].prev.tick)
+						evolve(&snap.characters[item.id].prev, client_prevtick());
+					if(snap.characters[item.id].cur.tick)
+						evolve(&snap.characters[item.id].cur, client_tick());
+				}
 			}
+			else if(item.type == NETOBJTYPE_GAME)
+				snap.gameobj = (NETOBJ_GAME *)data;
+			else if(item.type == NETOBJTYPE_FLAG)
+				snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
+		}
+	}
+	
+	// setup local pointers
+	if(snap.local_cid >= 0)
+	{
+		SNAPSTATE::CHARACTERINFO *c = &snap.characters[snap.local_cid];
+		if(c->active)
+		{
+			snap.local_character = &c->cur;
+			snap.local_prev_character = &c->prev;
+			local_character_pos = vec2(snap.local_character->x, snap.local_character->y);
 		}
 	}
 
+	// update render info
 	for(int i = 0; i < MAX_CLIENTS; i++)
-		gameclient.clients[i].update_render_info();
-
+		clients[i].update_render_info();
 }
 
 void GAMECLIENT::on_predict()
 {
+	// store the previous values so we can detect prediction errors
 	CHARACTER_CORE before_prev_char = predicted_prev_char;
 	CHARACTER_CORE before_char = predicted_char;
 
+	// we can't predict without our own id or own character
+	if(snap.local_cid == -1 || !snap.characters[snap.local_cid].active)
+		return;
+
 	// repredict character
 	WORLD_CORE world;
 	world.tuning = tuning;
-	int local_cid = -1;
 
 	// search for players
-	for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
+	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		SNAP_ITEM item;
-		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-		int client_id = item.id;
-
-		if(item.type == NETOBJTYPE_CHARACTER)
-		{
-			const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data;
-			gameclient.clients[client_id].predicted.world = &world;
-			world.characters[client_id] = &gameclient.clients[client_id].predicted;
-
-			gameclient.clients[client_id].predicted.read(character);
-		}
-		else if(item.type == NETOBJTYPE_PLAYER_INFO)
-		{
-			const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
-			if(info->local)
-				local_cid = client_id;
-		}
+		if(!snap.characters[i].active)
+			continue;
+			
+		gameclient.clients[i].predicted.world = &world;
+		world.characters[i] = &gameclient.clients[i].predicted;
+		gameclient.clients[i].predicted.read(&snap.characters[i].cur);
 	}
 	
-	// we can't predict without our own id
-	if(local_cid == -1)
-		return;
-
 	// predict
 	for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
 	{
 		// fetch the local
-		if(tick == client_predtick() && world.characters[local_cid])
-			predicted_prev_char = *world.characters[local_cid];
+		if(tick == client_predtick() && world.characters[snap.local_cid])
+			predicted_prev_char = *world.characters[snap.local_cid];
 		
 		// first calculate where everyone should move
 		for(int c = 0; c < MAX_CLIENTS; c++)
@@ -622,15 +642,17 @@ void GAMECLIENT::on_predict()
 				continue;
 
 			mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input));
-			if(local_cid == c)
+			if(snap.local_cid == c)
 			{
 				// apply player input
 				int *input = client_get_input(tick);
 				if(input)
 					world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
+				world.characters[c]->tick(true);
 			}
+			else
+				world.characters[c]->tick(false);
 
-			world.characters[c]->tick();
 		}
 
 		// move all players and quantize their data
@@ -643,14 +665,15 @@ void GAMECLIENT::on_predict()
 			world.characters[c]->quantize();
 		}
 		
+		// check if we want to trigger effects
 		if(tick > last_new_predicted_tick)
 		{
 			last_new_predicted_tick = tick;
 			
-			if(local_cid != -1 && world.characters[local_cid])
+			if(snap.local_cid != -1 && world.characters[snap.local_cid])
 			{
-				vec2 pos = world.characters[local_cid]->pos;
-				int events = world.characters[local_cid]->triggered_events;
+				vec2 pos = world.characters[snap.local_cid]->pos;
+				int events = world.characters[snap.local_cid]->triggered_events;
 				if(events&COREEVENT_GROUND_JUMP) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
 				if(events&COREEVENT_AIR_JUMP)
 				{
@@ -662,34 +685,26 @@ void GAMECLIENT::on_predict()
 				if(events&COREEVENT_HOOK_ATTACH_GROUND) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
 				//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
 			}
-
-
-			/*
-			dbg_msg("predict", "%d %d %d", tick,
-				(int)world.players[c]->pos.x, (int)world.players[c]->pos.y,
-				(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
 		}
 		
-		if(tick == client_predtick() && world.characters[local_cid])
-			predicted_char = *world.characters[local_cid];
+		if(tick == client_predtick() && world.characters[snap.local_cid])
+			predicted_char = *world.characters[snap.local_cid];
 	}
 	
-	if(config.debug && predicted_tick == client_predtick())
+	if(config.debug && config.cl_predict && predicted_tick == client_predtick())
 	{
-		if(predicted_char.pos.x != before_char.pos.x ||
-			predicted_char.pos.y != before_char.pos.y)
-		{
-			dbg_msg("client", "prediction error, (%d %d) (%d %d)", 
-				(int)before_char.pos.x, (int)before_char.pos.y,
-				(int)predicted_char.pos.x, (int)predicted_char.pos.y);
-		}
+		NETOBJ_CHARACTER_CORE before = {0}, now = {0};
+		before_char.write(&before);
+		predicted_char.write(&now);
 
-		if(predicted_prev_char.pos.x != before_prev_char.pos.x ||
-			predicted_prev_char.pos.y != before_prev_char.pos.y)
+		if(mem_comp(&before, &now, sizeof(NETOBJ_CHARACTER_CORE)) != 0)
 		{
-			dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", 
-				(int)before_prev_char.pos.x, (int)before_prev_char.pos.y,
-				(int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y);
+			dbg_msg("client", "prediction error");
+			for(unsigned i = 0; i < sizeof(NETOBJ_CHARACTER_CORE)/sizeof(int); i++)
+				if(((int *)&before)[i] != ((int *)&now)[i])
+				{
+					dbg_msg("", "\t%d %d %d", i, ((int *)&before)[i], ((int *)&now)[i]);
+				}
 		}
 	}
 	
@@ -753,8 +768,6 @@ void GAMECLIENT::send_kill(int client_id)
 	client_send_msg();
 }
 
-
-
 void GAMECLIENT::con_team(void *result, void *user_data)
 {
 	((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0));
diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp
index dc3532a7..4b397e86 100644
--- a/src/game/client/gameclient.hpp
+++ b/src/game/client/gameclient.hpp
@@ -56,9 +56,26 @@ public:
 
 		const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS];
 		const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS];
+		
+		int local_cid;
 		int num_players;
 		int team_size[2];
 		bool spectate;
+		
+		//
+		struct CHARACTERINFO
+		{
+			bool active;
+			
+			// snapshots
+			NETOBJ_CHARACTER prev;
+			NETOBJ_CHARACTER cur;
+			
+			// interpolated position
+			vec2 position;
+		};
+		
+		CHARACTERINFO characters[MAX_CLIENTS];
 	};
 
 	SNAPSTATE snap;