From 6138d70a6faa928034b07a648ca57f96d37144e9 Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Tue, 14 Jan 2025 23:23:41 -0800
Subject: [PATCH] =?UTF-8?q?unify=20timeout=20=F0=9F=98=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 strafe-client/src/mouse_interpolator.rs | 114 +++++++++---------------
 1 file changed, 44 insertions(+), 70 deletions(-)

diff --git a/strafe-client/src/mouse_interpolator.rs b/strafe-client/src/mouse_interpolator.rs
index 0c61089..d225bae 100644
--- a/strafe-client/src/mouse_interpolator.rs
+++ b/strafe-client/src/mouse_interpolator.rs
@@ -75,6 +75,37 @@ impl MouseInterpolator{
 			self.output.append(&mut self.buffer);
 		}
 	}
+	fn get_timedout_at(&self,time_limit:SessionTime)->Option<SessionTime>{
+		match &self.buffer_state{
+			BufferState::Unbuffered=>None,
+			BufferState::Initializing(time,_mouse_state)
+			|BufferState::Buffered(time,_mouse_state)=>{
+				let timeout=*time+MOUSE_TIMEOUT;
+				(timeout<time_limit).then_some(timeout)
+			}
+		}
+	}
+	fn timeout(&mut self){
+		let buffer_state=core::mem::replace(&mut self.buffer_state,BufferState::Unbuffered);
+		match buffer_state{
+			BufferState::Unbuffered=>(),
+			BufferState::Initializing(_time,mouse_state)
+			|BufferState::Buffered(_time,mouse_state)=>{
+				// convert to BufferState::Unbuffered
+				// use the first instruction which should be a mouse instruction
+				// to push a ReplaceMouse instruction
+				// duplicate the current mouse
+				self.push_mouse(TimedInstruction{
+					// This should be simulation_timer.time(timeout)
+					// but the timer is not accessible from this scope
+					// and it's just here to say that the mouse isn't moving anyways.
+					// I think this is a divide by zero bug, two identical mouse_states will occupy the interpolation state
+					time:mouse_state.time,
+					instruction:MouseInstruction::SetNextMouse(mouse_state),
+				});
+			},
+		}
+	}
 	pub fn push_unbuffered_input(&mut self,ins:DoubleTimedUnbufferedInstruction){
 		// new input
 		// if there is zero instruction buffered, it means the mouse is not moving
@@ -86,6 +117,11 @@ impl MouseInterpolator{
 		// a mouse event is buffered, and exists within the last 10ms
 		// case 3: stop
 		// a mouse event is buffered, but no mouse events have transpired within 10ms
+
+		// push buffered mouse instruction and flush buffer to output
+		if self.get_timedout_at(ins.time).is_some(){
+			self.timeout();
+		}
 		// replace_with allows the enum variant to safely be replaced from behind a mutable reference
 		let (ins_mouse,ins_other)=replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{
 			let next_state=match buffer_state{
@@ -96,18 +132,6 @@ impl MouseInterpolator{
 					BufferState::Unbuffered
 				},
 				BufferState::Initializing(time,mouse_state)=>{
-					let timeout=time+MOUSE_TIMEOUT;
-					if timeout<ins.time{
-						// duplicate the current mouse
-						let ins_mouse=TimedInstruction{
-							// This should be simulation_timer.time(time)
-							// but the timer is not accessible from this scope
-							// and it's just here to say that the mouse isn't moving anyways.
-							time:ins.instruction.time,
-							instruction:MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:ins.instruction.time}),
-						};
-						return ((Some(ins_mouse),None),BufferState::Unbuffered);
-					}
 					if let Instruction::MoveMouse(pos)=ins.instruction.instruction{
 						let next_mouse_state=MouseState{pos,time:ins.instruction.time};
 						let ins_mouse=TimedInstruction{
@@ -122,19 +146,6 @@ impl MouseInterpolator{
 					BufferState::Initializing(time,mouse_state)
 				},
 				BufferState::Buffered(time,mouse_state)=>{
-					// TODO:  deduplicate code with above case
-					let timeout=time+MOUSE_TIMEOUT;
-					if timeout<ins.time{
-						// duplicate the current mouse
-						let ins_mouse=TimedInstruction{
-							// This should be simulation_timer.time(timeout)
-							// but the timer is not accessible from this scope
-							// and it's just here to say that the mouse isn't moving anyways.
-							time:ins.instruction.time,
-							instruction:MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:ins.instruction.time}),
-						};
-						return ((Some(ins_mouse),None),BufferState::Unbuffered);
-					}
 					if let Instruction::MoveMouse(pos)=ins.instruction.instruction{
 						let next_mouse_state=MouseState{pos,time:ins.instruction.time};
 						let ins_mouse=TimedInstruction{
@@ -174,60 +185,23 @@ impl MouseInterpolator{
 		self.output.len()!=0
 	}
 	pub fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTimeInner>>{
-		match &self.buffer_state{
-			BufferState::Unbuffered=>self.has_output().then_some(TimedInstruction{
+		match self.get_timedout_at(time_limit){
+			Some(timeout)=>Some(TimedInstruction{
+				time:timeout,
+				instruction:StepInstruction::Timeout,
+			}),
+			None=>self.has_output().then_some(TimedInstruction{
+				// this timestamp should not matter
 				time:time_limit,
 				instruction:StepInstruction::Pop,
 			}),
-			BufferState::Initializing(time,_mouse_state)
-			|BufferState::Buffered(time,_mouse_state)=>{
-				let timeout=*time+MOUSE_TIMEOUT;
-				if timeout<time_limit{
-					// if the mouse has stopped moving for over 10ms, emit DoStepReplaceMouse at that exact timestamp
-					Some(TimedInstruction{
-						time:timeout,
-						instruction:StepInstruction::Timeout,
-					})
-				}else if self.has_output(){
-					// emit Step
-					Some(TimedInstruction{
-						// this timestamp should not matter
-						// verify this and potentially emit a different type using the enum only
-						time:time_limit,
-						instruction:StepInstruction::Pop,
-					})
-				}else{
-					None
-				}
-			},
 		}
 	}
 	// include timestamp and implement InstructionConsumer?
 	pub fn pop_buffered_instruction(&mut self,ins:StepInstruction)->Option<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>{
 		match ins{
-			// could check if self.is_first_ready()
 			StepInstruction::Pop=>(),
-			StepInstruction::Timeout=>{
-				let buffer_state=core::mem::replace(&mut self.buffer_state,BufferState::Unbuffered);
-				match buffer_state{
-					BufferState::Unbuffered=>(),
-					BufferState::Initializing(_time,mouse_state)
-					|BufferState::Buffered(_time,mouse_state)=>{
-						// convert to BufferState::Unbuffered
-						// use the first instruction which should be a mouse instruction
-						// to push a ReplaceMouse instruction
-						// duplicate the current mouse
-						self.push_mouse(TimedInstruction{
-							// This should be simulation_timer.time(timeout)
-							// but the timer is not accessible from this scope
-							// and it's just here to say that the mouse isn't moving anyways.
-							// I think this is a divide by zero bug, two identical mouse_states will occupy the interpolation state
-							time:mouse_state.time,
-							instruction:MouseInstruction::SetNextMouse(mouse_state),
-						});
-					},
-				}
-			},
+			StepInstruction::Timeout=>self.timeout(),
 		}
 		self.output.pop_front()
 	}