about summary refs log tree commit diff
path: root/src/engine/client/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/client/client.c')
-rw-r--r--src/engine/client/client.c253
1 files changed, 183 insertions, 70 deletions
diff --git a/src/engine/client/client.c b/src/engine/client/client.c
index 63414f8e..26865f37 100644
--- a/src/engine/client/client.c
+++ b/src/engine/client/client.c
@@ -33,10 +33,6 @@ static int info_request_begin;
 static int info_request_end;
 static int snapshot_part;
 static int64 local_start_time;
-static int64 game_start_time;
-
-static float snapshot_latency = 0;
-static float prediction_latency = 0;
 
 static int extra_polating = 0;
 static int debug_font;
@@ -48,6 +44,8 @@ static int window_must_refocus = 0;
 static int snaploss = 0;
 static int snapcrcerrors = 0;
 
+static int ack_game_tick = -1;
+
 static int current_recv_tick = 0;
 
 // current time
@@ -62,10 +60,123 @@ static struct // TODO: handle input better
 {
 	int data[MAX_INPUT_SIZE]; // the input data
 	int tick; // the tick that the input is for
-	float latency; // prediction latency when we sent this input
+	int64 game_time; // prediction latency when we sent this input
+	int64 time;
 } inputs[200];
 static int current_input = 0;
 
+enum
+{
+	GRAPH_MAX=256,
+};
+
+typedef struct
+{
+	float min, max;
+	float values[GRAPH_MAX];
+	int index;
+} GRAPH;
+
+static void graph_add(GRAPH *g, float v)
+{
+	g->values[g->index] = v;
+	g->index = (g->index+1)&(GRAPH_MAX-1);
+}
+
+static void graph_render(GRAPH *g, float x, float y, float w, float h)
+{
+	int i;
+	gfx_texture_set(-1);
+	
+	gfx_quads_begin();
+	gfx_setcolor(0, 0, 0, 1);
+	gfx_quads_drawTL(x, y, w, h);
+	gfx_quads_end();
+		
+	gfx_lines_begin();
+	gfx_setcolor(0.5f, 0.5f, 0.5f, 1);
+	gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4);
+	gfx_lines_draw(x, y+h/2, x+w, y+h/2);
+	gfx_lines_draw(x, y+h/4, x+w, y+h/4);
+	for(i = 1; i < GRAPH_MAX; i++)
+	{
+		float a0 = (i-1)/(float)GRAPH_MAX;
+		float a1 = i/(float)GRAPH_MAX;
+		int i0 = (g->index+i-1)&(GRAPH_MAX-1);
+		int i1 = (g->index+i)&(GRAPH_MAX-1);
+		
+		float v0 = g->values[i0];
+		float v1 = g->values[i1];
+		
+		gfx_setcolor(0, 1, 0, 1);
+		gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h);
+	}
+	gfx_lines_end();
+}
+
+typedef struct
+{
+	int64 snap;
+	int64 current;
+	int64 target;
+	
+	int64 rlast;
+	int64 tlast;
+	GRAPH graph;
+} SMOOTHTIME;
+
+static void st_init(SMOOTHTIME *st, int64 target)
+{
+	st->snap = time_get();
+	st->current = target;
+	st->target = target;
+}
+
+static int64 st_get(SMOOTHTIME *st, int64 now)
+{
+	int64 c = st->current + (now - st->snap);
+	int64 t = st->target + (now - st->snap);
+	
+	// it's faster to adjust upward instead of downward
+	// we might need to adjust these abit
+	float adjust_speed = 0.3f;
+	if(t < c)
+		adjust_speed *= 5.0f;
+	
+	float a = ((now-st->snap)/(float)time_freq())*adjust_speed;
+	if(a > 1)
+		a = 1;
+		
+	int64 r = c + (int64)((t-c)*a);
+	
+	{
+		int64 drt = now - st->rlast;
+		int64 dtt = r - st->tlast;
+		
+		st->rlast = now;
+		st->tlast = r;
+		
+		if(drt == 0)
+			graph_add(&st->graph, 0.5f);
+		else
+			graph_add(&st->graph, (((dtt/(float)drt)-1.0f)*2.5f)+0.5f);
+	}
+	
+	return r;
+}
+
+static void st_update(SMOOTHTIME *st, int64 target)
+{
+	int64 now = time_get();
+	st->current = st_get(st, now);
+	st->snap = now;
+	st->target = target;
+}
+
+SMOOTHTIME game_time;
+SMOOTHTIME predicted_time;
+
+GRAPH intra_graph;
 
 // --- input snapping ---
 static int input_data[MAX_INPUT_SIZE] = {0};
@@ -125,8 +236,7 @@ static void snap_init()
 	snapshots[SNAP_CURRENT] = 0;
 	snapshots[SNAP_PREV] = 0;
 	recived_snapshots = 0;
-	game_start_time = -1;
-	
+	current_predtick = 0;
 }
 
 // ------ time functions ------
@@ -159,7 +269,6 @@ int client_send_msg()
 static void client_send_info()
 {
 	recived_snapshots = 0;
-	game_start_time = -1;
 
 	msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL);
 	msg_pack_string(modc_net_version(), 128);
@@ -192,12 +301,18 @@ static void client_send_error(const char *error)
 
 static void client_send_input()
 {
+	if(current_predtick <= 0)
+		return;
+		
 	msg_pack_start_system(NETMSG_INPUT, 0);
+	msg_pack_int(ack_game_tick);
 	msg_pack_int(current_predtick);
 	msg_pack_int(input_data_size);
-	
+
+	int64 now = time_get();	
 	inputs[current_input].tick = current_predtick;
-	inputs[current_input].latency = prediction_latency;
+	inputs[current_input].game_time = st_get(&predicted_time, now);
+	inputs[current_input].time = now;
 	
 	int i;
 	for(i = 0; i < input_data_size/4; i++)
@@ -384,9 +499,6 @@ void client_connect(const char *server_address_str)
 	for(i = 0; i < 200; i++)
 		inputs[i].tick = -1;
 	current_input = 0;
-	
-	snapshot_latency = 0;
-	prediction_latency = 0;
 }
 
 void client_disconnect()
@@ -424,17 +536,23 @@ static void client_debug_render()
 	static float frametime_avg = 0;
 	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
 	char buffer[512];
-	sprintf(buffer, "send: %6d recv: %6d snaploss: %d snaplatency: %4.2f %c  predlatency: %4.2f  mem %dk   gfxmem: %dk  fps: %3d",
+	sprintf(buffer, "ticks: %8d %8d send: %6d recv: %6d snaploss: %d %c  mem %dk   gfxmem: %dk  fps: %3d",
+		current_tick, current_predtick,
 		(current.send_bytes-prev.send_bytes)*10,
 		(current.recv_bytes-prev.recv_bytes)*10,
 		snaploss,
-		snapshot_latency*1000.0f, extra_polating?'E':' ',
-		prediction_latency*1000.0f,
+		extra_polating?'E':' ',
 		mem_allocated()/1024,
 		gfx_memory_usage()/1024,
 		(int)(1.0f/frametime_avg));
 	gfx_quads_text(2, 2, 16, buffer);
 	
+	
+	// render graphs
+	gfx_mapscreen(0,0,400.0f,300.0f);
+	graph_render(&game_time.graph, 300, 10, 90, 50);
+	graph_render(&predicted_time.graph, 300, 10+50+10, 90, 50);
+	graph_render(&intra_graph, 300, 10+50+10+50+10, 90, 50);
 }
 
 void client_quit()
@@ -597,19 +715,15 @@ static void client_process_packet(NETPACKET *packet)
 					{
 						if(inputs[k].tick == input_predtick)
 						{
-							float wanted_latency = inputs[k].latency - time_left/1000.0f + 0.01f;
-							if(wanted_latency > prediction_latency)
-								prediction_latency = prediction_latency*0.90f + wanted_latency*0.10f;
-							else
-								prediction_latency = prediction_latency*0.95f + wanted_latency*0.05f;
-							//dbg_msg("DEBUG", "predlatency=%f", prediction_latency);
+							//-1000/50
+							int margin = 1000/50;
+							int64 target = inputs[k].game_time + (time_get() - inputs[k].time);
+							st_update(&predicted_time, target - (int64)(((time_left-margin)/1000.0f)*time_freq()));
 							break;
 						}
 					}
 				}
 				
-				//dbg_msg("DEBUG", "new predlatency=%f", prediction_latency);
-				
 				if(snapshot_part == part && game_tick > current_recv_tick)
 				{
 					// TODO: clean this up abit
@@ -642,10 +756,7 @@ static void client_process_packet(NETPACKET *packet)
 								
 								// ack snapshot
 								// TODO: combine this with the input message
-								msg_pack_start_system(NETMSG_SNAPACK, 0);
-								msg_pack_int(-1);
-								msg_pack_end();
-								client_send_msg();
+								ack_game_tick = -1;
 								return;
 							}
 						}
@@ -668,18 +779,15 @@ static void client_process_packet(NETPACKET *packet)
 						
 						unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE];
 						int snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize);
-						if(snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc)
+						if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc)
 						{
 							if(config.debug)
-								dbg_msg("client", "snapshot crc error\n");
+								dbg_msg("client", "snapshot crc error");
 							snapcrcerrors++;
 							if(snapcrcerrors > 25)
 							{
 								// to many errors, send reset
-								msg_pack_start_system(NETMSG_SNAPACK, 0);
-								msg_pack_int(-1);
-								msg_pack_end();
-								client_send_msg();
+								ack_game_tick = -1;
 								snapcrcerrors = 0;
 							}
 							return;
@@ -718,12 +826,21 @@ static void client_process_packet(NETPACKET *packet)
 						// we got two snapshots until we see us self as connected
 						if(recived_snapshots == 2)
 						{
+							// start at 200ms and work from there
+							st_init(&predicted_time, (game_tick+10)*time_freq()/50);
+							st_init(&game_time, (game_tick-2)*time_freq()/50);
 							snapshots[SNAP_PREV] = snapshot_storage.first;
 							snapshots[SNAP_CURRENT] = snapshot_storage.last;
 							local_start_time = time_get();
 							client_set_state(CLIENTSTATE_ONLINE);
 						}
+						
+						st_update(&game_time, (game_tick-2)*time_freq()/50);
+						//client_send_input();
 
+						//st_update(&predicted_time, game_tick*time_freq());
+
+						/*
 						int64 now = time_get();
 						int64 t = now - game_tick*time_freq()/50;
 						if(game_start_time == -1 || t < game_start_time)
@@ -736,17 +853,16 @@ static void client_process_packet(NETPACKET *packet)
 						int64 wanted = game_start_time+(game_tick*time_freq())/50;
 						float current_latency = (now-wanted)/(float)time_freq();
 						snapshot_latency = snapshot_latency*0.95f+current_latency*0.05f;
+						*/
 						
 						// ack snapshot
-						msg_pack_start_system(NETMSG_SNAPACK, 0);
-						msg_pack_int(game_tick);
-						msg_pack_end();
-						client_send_msg();
+						//dbg_msg("snap!", "%d", game_tick);
+						ack_game_tick = game_tick;
 					}
 				}
 				else
 				{
-					dbg_msg("client", "snapshot reset!");
+					dbg_msg("client", "snapsht reset!");
 					snapshot_part = 0;
 				}
 			}
@@ -759,7 +875,6 @@ static void client_process_packet(NETPACKET *packet)
 	}
 }
 
-
 static void client_pump_network()
 {
 	netclient_update(net);
@@ -842,16 +957,14 @@ static void client_run(const char *direct_connect_server)
 		if(recived_snapshots >= 3)
 		{
 			int repredict = 0;
-			int64 now = time_get();
+			//int64 now = time_get();
+			int64 now = st_get(&game_time, time_get());
 			while(1)
 			{
 				SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT];
-				int64 tickstart = game_start_time + (cur->tick+1)*time_freq()/50;
-				int64 t = tickstart;
-				if(snapshot_latency > 0)
-					t += (int64)(time_freq()*(snapshot_latency*1.1f));
+				int64 tickstart = (cur->tick)*time_freq()/50;
 
-				if(t < now)
+				if(tickstart < now)
 				{
 					SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next;
 					if(next)
@@ -880,36 +993,28 @@ static void client_run(const char *direct_connect_server)
 					break;
 				}
 			}
+
+			//tg_add(&game_time_graph, now, extra_polating);
 			
 			if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
 			{
-				int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50;
-				if(snapshot_latency > 0)
-					curtick_start += (int64)(time_freq()*(snapshot_latency*1.1f));
-					
-				int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50;
-				if(snapshot_latency > 0)
-					prevtick_start += (int64)(time_freq()*(snapshot_latency*1.1f));
-					
+				int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50;
+				int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50;
 				intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start);
-
-				// 25 frames ahead
-				int64 last_pred_game_time = 0;
-				int64 predicted_game_time = (now - game_start_time) + (int64)(time_freq()*prediction_latency);
-				if(predicted_game_time < last_pred_game_time)
-					predicted_game_time = last_pred_game_time;
-				last_pred_game_time = predicted_game_time;
 				
-				//int64 predictiontime = game_start_time + time_freq()+ prediction_latency*SERVER_TICK_SPEED
-				int new_predtick = (predicted_game_time*SERVER_TICK_SPEED) / time_freq();
+				graph_add(&intra_graph, intratick*0.25f);
 				
-				int64 predtick_start_time = (new_predtick*time_freq())/SERVER_TICK_SPEED;
-				intrapredtick = (predicted_game_time - predtick_start_time)*SERVER_TICK_SPEED/(float)time_freq();
+				int64 pred_now = st_get(&predicted_time, time_get());
+				//tg_add(&predicted_time_graph, pred_now, 0);
+				int prev_pred_tick = (int)(pred_now*50/time_freq());
+				int new_pred_tick = prev_pred_tick+1;
+				curtick_start = new_pred_tick*time_freq()/50;
+				prevtick_start = prev_pred_tick*time_freq()/50;
+				intrapredtick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start);
 				
-				if(new_predtick > current_predtick)
+				if(new_pred_tick > current_predtick)
 				{
-					//dbg_msg("")
-					current_predtick = new_predtick;
+					current_predtick = new_pred_tick;
 					repredict = 1;
 					
 					// send input
@@ -918,9 +1023,13 @@ static void client_run(const char *direct_connect_server)
 			}
 			
 			//intrapredtick = current_predtick
-			
+
+			// only do sane predictions			
 			if(repredict)
-				modc_predict();
+			{
+				if(current_predtick > current_tick && current_predtick < current_tick+50)
+					modc_predict();
+			}
 		}
 
 		// STRESS TEST: join the server again
@@ -970,11 +1079,15 @@ static void client_run(const char *direct_connect_server)
 		
 			if(inp_key_pressed(KEY_F5))
 			{
+				ack_game_tick = -1;
+				client_send_input();
+				/*
 				// ack snapshot
 				msg_pack_start_system(NETMSG_SNAPACK, 0);
 				msg_pack_int(-1);
 				msg_pack_end();
 				client_send_msg();
+				*/
 			}
 		}