From c6ff11dd3edc0ae7b555b42c7ad0a652c2959d7e Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Mon, 13 Jan 2025 23:11:43 -0800
Subject: [PATCH] use replace_with to replace the enum variant in-place without
 cloning

---
 strafe-client/src/mouse_interpolator.rs | 75 +++++++++++++------------
 1 file changed, 40 insertions(+), 35 deletions(-)

diff --git a/strafe-client/src/mouse_interpolator.rs b/strafe-client/src/mouse_interpolator.rs
index ee55452..6998b10 100644
--- a/strafe-client/src/mouse_interpolator.rs
+++ b/strafe-client/src/mouse_interpolator.rs
@@ -28,15 +28,16 @@ enum BufferState{
 	Buffered(SessionTime,MouseState<PhysicsTimeInner>),
 }
 impl BufferState{
-	fn next_state(&self,ins:DoubleTimedUnbufferedInstruction,physics_timeline:&mut std::collections::VecDeque<TimedPhysicsInstruction>)->(Self,Option<TimedInstruction<OtherInstruction,PhysicsTimeInner>>){
-		match self{
+	fn next_state(self,ins:DoubleTimedUnbufferedInstruction,physics_timeline:&mut std::collections::VecDeque<TimedPhysicsInstruction>)->(Option<TimedInstruction<OtherInstruction,PhysicsTimeInner>>,Self){
+		let next_state=match self{
 			BufferState::Unbuffered=>{
 				if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{
-					return (BufferState::Initializing(ins.time,MouseState{pos,time:ins.instruction.time}),None);
+					return (None,BufferState::Initializing(ins.time,MouseState{pos,time:ins.instruction.time}));
 				}
+				BufferState::Unbuffered
 			},
 			BufferState::Initializing(time,mouse_state)=>{
-				let timeout=*time+MOUSE_TIMEOUT;
+				let timeout=time+MOUSE_TIMEOUT;
 				if timeout<ins.time{
 					// duplicate the current mouse
 					physics_timeline.push_front(TimedInstruction{
@@ -48,7 +49,7 @@ impl BufferState{
 							MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:ins.instruction.time})
 						),
 					});
-					return (BufferState::Unbuffered,None);
+					return (None,BufferState::Unbuffered);
 				}
 				if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{
 					let next_mouse_state=MouseState{pos,time:ins.instruction.time};
@@ -56,17 +57,18 @@ impl BufferState{
 						time:mouse_state.time,
 						instruction:PhysicsInputInstruction::Mouse(
 							MouseInstruction::ReplaceMouse{
-								s0:mouse_state.clone(),
+								s0:mouse_state,
 								s1:next_mouse_state.clone(),
 							}
 						),
 					});
-					return (BufferState::Buffered(ins.time,next_mouse_state),None);
+					return (None,BufferState::Buffered(ins.time,next_mouse_state));
 				}
+				BufferState::Initializing(time,mouse_state)
 			},
 			BufferState::Buffered(time,mouse_state)=>{
 				// TODO:  deduplicate code with above case
-				let timeout=*time+MOUSE_TIMEOUT;
+				let timeout=time+MOUSE_TIMEOUT;
 				if timeout<ins.time{
 					// duplicate the current mouse
 					physics_timeline.push_front(TimedInstruction{
@@ -78,7 +80,7 @@ impl BufferState{
 							MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:ins.instruction.time})
 						),
 					});
-					return (BufferState::Unbuffered,None);
+					return (None,BufferState::Unbuffered);
 				}
 				if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{
 					let next_mouse_state=MouseState{pos,time:ins.instruction.time};
@@ -88,10 +90,11 @@ impl BufferState{
 							MouseInstruction::SetNextMouse(next_mouse_state.clone())
 						),
 					});
-					return (BufferState::Buffered(ins.time,next_mouse_state),None);
+					return (None,BufferState::Buffered(ins.time,next_mouse_state));
 				}
+				BufferState::Buffered(time,mouse_state)
 			},
-		}
+		};
 		let instruction_out=match ins.instruction.instruction{
 			PhysicsUnbufferedInstruction::MoveMouse(_)=>None,
 			PhysicsUnbufferedInstruction::Other(other_instruction)=>Some(TimedInstruction{
@@ -99,7 +102,7 @@ impl BufferState{
 				instruction:other_instruction,
 			}),
 		};
-		(self.clone(),instruction_out)
+		(instruction_out,next_state)
 	}
 }
 pub struct MouseInterpolator{
@@ -139,9 +142,10 @@ 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
-		// TODO: remove clone
-		let ins_inner;
-		(self.buffer_state,ins_inner)=self.buffer_state.next_state(ins,&mut self.physics_timeline);
+		// replace_with allows the enum variant to safely be replaced from behind a mutable reference
+		let ins_inner=replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{
+			buffer_state.next_state(ins,&mut self.physics_timeline)
+		});
 		if let Some(ins)=ins_inner{
 			self.physics_timeline.push_back(TimedInstruction{
 				time:ins.time,
@@ -196,26 +200,27 @@ impl MouseInterpolator{
 		match ins{
 			// could check if self.is_first_ready()
 			StepInstruction::Pop=>self.physics_timeline.pop_front(),
-			StepInstruction::Timeout=>match &self.buffer_state{
-				BufferState::Unbuffered=>None,
-				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.buffer_state=BufferState::Unbuffered;
-					Some(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:mouse_state.time,
-						instruction:PhysicsInputInstruction::Mouse(
-							MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:mouse_state.time})
-						),
-					})
-				},
-			},
+			StepInstruction::Timeout=>replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{
+				match buffer_state{
+					BufferState::Unbuffered=>(None,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
+						(Some(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:mouse_state.time,
+							instruction:PhysicsInputInstruction::Mouse(
+								MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:mouse_state.time})
+							),
+						}),BufferState::Unbuffered)
+					},
+				}
+			}),
 		}
 	}
 }