2025-01-15 23:44:42 -08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2025-01-15 01:01:24 -08:00
|
|
|
use strafesnet_common::gameplay_modes::{ModeId,StageId};
|
|
|
|
use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,InstructionFeedback,TimedInstruction};
|
|
|
|
// session represents the non-hardware state of the client.
|
|
|
|
// Ideally it is a deterministic state which is atomically updated by instructions, same as the simulation state.
|
|
|
|
use strafesnet_common::physics::{
|
2025-01-15 21:09:08 -08:00
|
|
|
ModeInstruction,MiscInstruction,
|
2025-01-15 01:01:24 -08:00
|
|
|
Instruction as PhysicsInputInstruction,
|
|
|
|
TimeInner as PhysicsTimeInner,
|
|
|
|
Time as PhysicsTime
|
|
|
|
};
|
|
|
|
use strafesnet_common::timer::{Scaled,Timer};
|
|
|
|
use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime};
|
|
|
|
|
|
|
|
use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction};
|
|
|
|
use crate::settings::UserSettings;
|
|
|
|
|
|
|
|
pub enum Instruction<'a>{
|
|
|
|
Input(SessionInputInstruction),
|
2025-01-15 22:59:59 -08:00
|
|
|
Control(SessionControlInstruction),
|
|
|
|
Playback(SessionPlaybackInstruction),
|
2025-01-15 01:01:24 -08:00
|
|
|
ChangeMap(&'a strafesnet_common::map::CompleteMap),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum SessionInputInstruction{
|
|
|
|
Mouse(glam::IVec2),
|
|
|
|
SetControl(strafesnet_common::physics::SetControlInstruction),
|
|
|
|
Mode(ImplicitModeInstruction),
|
2025-01-15 20:19:20 -08:00
|
|
|
Misc(strafesnet_common::physics::MiscInstruction),
|
2025-01-15 01:01:24 -08:00
|
|
|
}
|
|
|
|
/// Implicit mode instruction are fed separately to session.
|
|
|
|
/// Session generates the explicit mode instructions interlaced with a SetSensitivity instruction
|
|
|
|
#[derive(Clone,Debug)]
|
|
|
|
pub enum ImplicitModeInstruction{
|
|
|
|
ResetAndRestart,
|
|
|
|
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
|
|
|
|
}
|
|
|
|
|
2025-01-15 22:59:59 -08:00
|
|
|
pub enum SessionControlInstruction{
|
|
|
|
SetPaused(bool),
|
|
|
|
// copy the current session simulation recording into a replay and view it
|
|
|
|
CopyRecordingIntoReplayAndSpectate,
|
|
|
|
StopSpectate,
|
|
|
|
}
|
|
|
|
pub enum SessionPlaybackInstruction{
|
|
|
|
SkipForward,
|
|
|
|
SkipBack,
|
|
|
|
TogglePaused,
|
|
|
|
DecreaseTimescale,
|
|
|
|
IncreaseTimescale,
|
|
|
|
}
|
|
|
|
|
2025-01-15 01:01:24 -08:00
|
|
|
pub struct FrameState{
|
|
|
|
pub body:crate::physics::Body,
|
|
|
|
pub camera:crate::physics::PhysicsCamera,
|
|
|
|
pub time:PhysicsTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Simulation{
|
|
|
|
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
|
|
|
|
physics:crate::physics::PhysicsContext,
|
|
|
|
}
|
|
|
|
impl Simulation{
|
|
|
|
pub const fn new(
|
|
|
|
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
|
|
|
|
physics:crate::physics::PhysicsContext,
|
|
|
|
)->Self{
|
|
|
|
Self{
|
|
|
|
timer,
|
|
|
|
physics,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
|
|
|
|
FrameState{
|
|
|
|
body:self.physics.camera_body(),
|
|
|
|
camera:self.physics.camera(),
|
|
|
|
time:self.timer.time(time),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-15 21:41:44 -08:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Recording{
|
|
|
|
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
|
|
|
|
}
|
|
|
|
impl Recording{
|
|
|
|
fn clear(&mut self){
|
|
|
|
self.instructions.clear();
|
|
|
|
}
|
|
|
|
}
|
2025-01-15 01:01:24 -08:00
|
|
|
pub struct Replay{
|
|
|
|
last_instruction_id:usize,
|
2025-01-15 21:41:44 -08:00
|
|
|
recording:Recording,
|
2025-01-15 01:01:24 -08:00
|
|
|
simulation:Simulation,
|
|
|
|
}
|
|
|
|
impl Replay{
|
|
|
|
pub const fn new(
|
2025-01-15 21:41:44 -08:00
|
|
|
recording:Recording,
|
2025-01-15 01:01:24 -08:00
|
|
|
simulation:Simulation,
|
|
|
|
)->Self{
|
|
|
|
Self{
|
|
|
|
last_instruction_id:0,
|
2025-01-15 21:41:44 -08:00
|
|
|
recording,
|
2025-01-15 01:01:24 -08:00
|
|
|
simulation,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-15 23:44:42 -08:00
|
|
|
#[derive(Clone,Copy,Hash,PartialEq,Eq)]
|
|
|
|
struct BotId(u32);
|
|
|
|
//#[derive(Clone,Copy,Hash,PartialEq,Eq)]
|
|
|
|
//struct PlayerId(u32);
|
|
|
|
|
|
|
|
enum ViewState{
|
|
|
|
Play,
|
|
|
|
//Spectate(PlayerId),
|
|
|
|
Replay(BotId),
|
|
|
|
}
|
|
|
|
|
2025-01-15 01:01:24 -08:00
|
|
|
pub struct Session{
|
|
|
|
user_settings:UserSettings,
|
|
|
|
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator,
|
2025-01-15 23:44:42 -08:00
|
|
|
view_state:ViewState,
|
2025-01-15 01:01:24 -08:00
|
|
|
//gui:GuiState
|
|
|
|
simulation:Simulation,
|
2025-01-15 21:41:44 -08:00
|
|
|
// below fields not included in lite session
|
|
|
|
recording:Recording,
|
2025-01-15 23:44:42 -08:00
|
|
|
//players:HashMap<PlayerId,Simulation>,
|
|
|
|
replays:HashMap<BotId,Replay>,
|
2025-01-15 01:01:24 -08:00
|
|
|
}
|
|
|
|
impl Session{
|
|
|
|
pub fn new(
|
|
|
|
user_settings:UserSettings,
|
|
|
|
simulation:Simulation,
|
|
|
|
)->Self{
|
|
|
|
Self{
|
|
|
|
user_settings,
|
|
|
|
mouse_interpolator:MouseInterpolator::new(),
|
|
|
|
simulation,
|
2025-01-15 23:44:42 -08:00
|
|
|
view_state:ViewState::Play,
|
2025-01-15 21:41:44 -08:00
|
|
|
recording:Default::default(),
|
2025-01-15 23:44:42 -08:00
|
|
|
replays:HashMap::new(),
|
2025-01-15 01:01:24 -08:00
|
|
|
}
|
|
|
|
}
|
2025-01-15 21:41:44 -08:00
|
|
|
fn clear_recording(&mut self){
|
|
|
|
self.recording.clear();
|
|
|
|
}
|
2025-01-15 01:01:24 -08:00
|
|
|
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
|
|
|
|
self.simulation.physics.generate_models(map);
|
|
|
|
}
|
2025-01-15 23:44:42 -08:00
|
|
|
pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{
|
|
|
|
match &self.view_state{
|
|
|
|
ViewState::Play=>Some(self.simulation.get_frame_state(time)),
|
|
|
|
ViewState::Replay(bot_id)=>self.replays.get(bot_id).map(|replay|
|
|
|
|
replay.simulation.get_frame_state(time)
|
|
|
|
),
|
|
|
|
}
|
2025-01-15 01:01:24 -08:00
|
|
|
}
|
|
|
|
pub fn user_settings(&self)->&UserSettings{
|
|
|
|
&self.user_settings
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mouseinterpolator consumes RawInputInstruction
|
|
|
|
// mouseinterpolator emits PhysicsInputInstruction
|
|
|
|
// mouseinterpolator consumes DoStep to move on to the next emitted instruction
|
|
|
|
// Session comsumes SessionInstruction -> forwards RawInputInstruction to mouseinterpolator
|
|
|
|
// Session consumes DoStep -> forwards DoStep to mouseinterpolator
|
|
|
|
// Session emits DoStep
|
|
|
|
|
|
|
|
impl InstructionConsumer<Instruction<'_>> for Session{
|
|
|
|
type TimeInner=SessionTimeInner;
|
|
|
|
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Self::TimeInner>){
|
2025-01-15 03:03:07 -08:00
|
|
|
// repetitive procedure macro
|
2025-01-15 01:01:24 -08:00
|
|
|
macro_rules! run_mouse_interpolator_instruction{
|
|
|
|
($instruction:expr)=>{
|
|
|
|
self.mouse_interpolator.process_instruction(TimedInstruction{
|
|
|
|
time:ins.time,
|
|
|
|
instruction:TimedInstruction{
|
|
|
|
time:self.simulation.timer.time(ins.time),
|
|
|
|
instruction:$instruction,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
2025-01-15 03:03:07 -08:00
|
|
|
|
|
|
|
// process any timeouts that occured since the last instruction
|
|
|
|
self.process_exhaustive(ins.time);
|
|
|
|
|
2025-01-15 01:01:24 -08:00
|
|
|
match ins.instruction{
|
|
|
|
// send it down to MouseInterpolator with two timestamps, SessionTime and PhysicsTime
|
|
|
|
Instruction::Input(SessionInputInstruction::Mouse(pos))=>{
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::MoveMouse(pos));
|
|
|
|
},
|
|
|
|
Instruction::Input(SessionInputInstruction::SetControl(set_control_instruction))=>{
|
2025-01-15 21:09:08 -08:00
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::SetControl(set_control_instruction));
|
2025-01-15 01:01:24 -08:00
|
|
|
},
|
|
|
|
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndRestart))=>{
|
2025-01-15 21:41:44 -08:00
|
|
|
self.clear_recording();
|
2025-01-15 21:09:08 -08:00
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Restart));
|
2025-01-15 01:01:24 -08:00
|
|
|
},
|
|
|
|
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(mode_id,spawn_id)))=>{
|
2025-01-15 21:41:44 -08:00
|
|
|
self.clear_recording();
|
2025-01-15 21:09:08 -08:00
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id)));
|
2025-01-15 01:01:24 -08:00
|
|
|
},
|
2025-01-16 00:09:56 -08:00
|
|
|
Instruction::Input(SessionInputInstruction::Misc(misc_instruction))=>{
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(misc_instruction));
|
2025-01-15 01:01:24 -08:00
|
|
|
},
|
2025-01-15 22:59:59 -08:00
|
|
|
Instruction::Control(SessionControlInstruction::SetPaused(paused))=>{
|
2025-01-15 01:01:24 -08:00
|
|
|
// don't flush the buffered instructions in the mouse interpolator
|
|
|
|
// until the mouse is confirmed to be not moving at a later time
|
|
|
|
// what if they pause for 5ms lmao
|
|
|
|
_=self.simulation.timer.set_paused(ins.time,paused);
|
2025-01-15 22:59:59 -08:00
|
|
|
},
|
|
|
|
Instruction::Control(SessionControlInstruction::CopyRecordingIntoReplayAndSpectate)=>{
|
2025-01-15 23:44:42 -08:00
|
|
|
// Bind: B
|
|
|
|
|
|
|
|
// pause simulation
|
|
|
|
_=self.simulation.timer.set_paused(ins.time,true);
|
|
|
|
|
|
|
|
// create recording
|
|
|
|
let mut recording=Recording::default();
|
|
|
|
recording.instructions.extend(self.recording.instructions.iter().cloned());
|
|
|
|
|
|
|
|
// create timer starting at first instruction (or zero if the list is empty)
|
|
|
|
let timer=Timer::unpaused(ins.time,recording.instructions.first().map_or(PhysicsTime::ZERO,|ins|ins.time));
|
|
|
|
|
|
|
|
// create default physics state
|
|
|
|
let simulation=Simulation::new(timer,Default::default());
|
|
|
|
|
|
|
|
// invent a new bot id and insert the replay
|
|
|
|
let bot_id=BotId(self.replays.len() as u32);
|
|
|
|
self.replays.insert(bot_id,Replay::new(
|
|
|
|
recording,
|
|
|
|
simulation,
|
|
|
|
));
|
|
|
|
|
|
|
|
// begin spectate
|
|
|
|
self.view_state=ViewState::Replay(bot_id);
|
2025-01-15 22:59:59 -08:00
|
|
|
},
|
|
|
|
Instruction::Control(SessionControlInstruction::StopSpectate)=>{
|
2025-01-15 23:44:42 -08:00
|
|
|
_=self.simulation.timer.set_paused(ins.time,false);
|
|
|
|
self.view_state=ViewState::Play;
|
2025-01-15 22:59:59 -08:00
|
|
|
},
|
|
|
|
Instruction::Playback(_)=>{
|
2025-01-15 23:44:42 -08:00
|
|
|
println!("[session] todo: Playback instructions");
|
2025-01-15 22:59:59 -08:00
|
|
|
},
|
2025-01-15 01:01:24 -08:00
|
|
|
Instruction::ChangeMap(complete_map)=>{
|
2025-01-15 21:41:44 -08:00
|
|
|
self.clear_recording();
|
2025-01-15 01:01:24 -08:00
|
|
|
self.change_map(complete_map);
|
|
|
|
// ResetAndSpawn
|
2025-01-15 21:09:08 -08:00
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
|
|
|
|
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Spawn(ModeId::MAIN,StageId::FIRST)));
|
2025-01-15 01:01:24 -08:00
|
|
|
},
|
|
|
|
};
|
2025-01-15 03:03:07 -08:00
|
|
|
|
|
|
|
// process all emitted output instructions
|
2025-01-15 01:01:24 -08:00
|
|
|
self.process_exhaustive(ins.time);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl InstructionConsumer<StepInstruction> for Session{
|
|
|
|
type TimeInner=SessionTimeInner;
|
|
|
|
fn process_instruction(&mut self,ins:TimedInstruction<StepInstruction,Self::TimeInner>){
|
2025-01-15 03:22:19 -08:00
|
|
|
let time=self.simulation.timer.time(ins.time);
|
|
|
|
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){
|
2025-01-15 21:41:44 -08:00
|
|
|
//record
|
|
|
|
self.recording.instructions.push(instruction.clone());
|
2025-01-15 01:01:24 -08:00
|
|
|
self.simulation.physics.run_input_instruction(instruction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl InstructionEmitter<StepInstruction> for Session{
|
|
|
|
type TimeInner=SessionTimeInner;
|
|
|
|
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::TimeInner>>{
|
|
|
|
self.mouse_interpolator.next_instruction(time_limit)
|
|
|
|
}
|
|
|
|
}
|