|
|
|
|
@@ -18,19 +18,19 @@ use gameplay::ModeState;
|
|
|
|
|
//
|
|
|
|
|
// When replaying a bot, use the exact physics version which it was recorded with.
|
|
|
|
|
//
|
|
|
|
|
// When validating a new bot, use the latest compatible physics version
|
|
|
|
|
// from the compatiblity matrix, since it may include bugfixes
|
|
|
|
|
// for things like clipping through walls with surgical precision
|
|
|
|
|
// i.e. without breaking bots which don't exploit the bug.
|
|
|
|
|
// When validating a new bot, ignore the version and use the latest version,
|
|
|
|
|
// and overwrite the version in the file.
|
|
|
|
|
//
|
|
|
|
|
// Compatible physics versions should be determined
|
|
|
|
|
// empirically at development time via leaderboard resimulation.
|
|
|
|
|
//
|
|
|
|
|
// Compatible physics versions should be determined empirically via leaderboard resimulation.
|
|
|
|
|
// Compatible physics versions should result in an identical leaderboard state,
|
|
|
|
|
// or the only bots which fail are ones exploiting a surgically patched bug.
|
|
|
|
|
#[derive(Clone,Copy,Hash,Debug,id::Id,Eq,PartialEq,Ord,PartialOrd)]
|
|
|
|
|
pub struct PhysicsVersion(u32);
|
|
|
|
|
pub const VERSION:PhysicsVersion=PhysicsVersion(0);
|
|
|
|
|
pub const VERSION:PhysicsVersion=PhysicsVersion(1);
|
|
|
|
|
const LATEST_COMPATIBLE_VERSION:[u32;1+VERSION.0 as usize]=const{
|
|
|
|
|
let compat=[0];
|
|
|
|
|
let compat=[0,1];
|
|
|
|
|
|
|
|
|
|
let mut input_version=0;
|
|
|
|
|
while input_version<compat.len(){
|
|
|
|
|
@@ -71,7 +71,7 @@ pub enum InternalInstruction{
|
|
|
|
|
// Water,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone,Debug,Default)]
|
|
|
|
|
#[derive(Clone,Debug)]
|
|
|
|
|
pub struct InputState{
|
|
|
|
|
mouse:MouseState,
|
|
|
|
|
next_mouse:MouseState,
|
|
|
|
|
@@ -79,10 +79,15 @@ pub struct InputState{
|
|
|
|
|
}
|
|
|
|
|
impl InputState{
|
|
|
|
|
fn set_next_mouse(&mut self,next_mouse:MouseState){
|
|
|
|
|
// would this be correct?
|
|
|
|
|
// if self.next_mouse.time==next_mouse.time{
|
|
|
|
|
// self.next_mouse=next_mouse;
|
|
|
|
|
// }else{
|
|
|
|
|
//I like your functions magic language
|
|
|
|
|
self.mouse=std::mem::replace(&mut self.next_mouse,next_mouse);
|
|
|
|
|
//equivalently:
|
|
|
|
|
//(self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){
|
|
|
|
|
(self.next_mouse,self.mouse)=(next_mouse,mouse);
|
|
|
|
|
@@ -104,6 +109,15 @@ impl InputState{
|
|
|
|
|
((dm*t)/dt).as_ivec2()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl Default for InputState{
|
|
|
|
|
fn default()->Self{
|
|
|
|
|
Self{
|
|
|
|
|
mouse:MouseState{pos:Default::default(),time:Time::ZERO-Time::EPSILON*2},
|
|
|
|
|
next_mouse:MouseState{pos:Default::default(),time:Time::ZERO-Time::EPSILON},
|
|
|
|
|
controls:Default::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[derive(Clone,Debug)]
|
|
|
|
|
enum JumpDirection{
|
|
|
|
|
Exactly(Planar64Vec3),
|
|
|
|
|
@@ -186,17 +200,17 @@ fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCol
|
|
|
|
|
let normal=contact_normal(models,hitbox_mesh,contact);
|
|
|
|
|
let gravity=touching.base_acceleration(models,style,camera,input_state);
|
|
|
|
|
let control_dir=style.get_y_control_dir(camera,input_state.controls);
|
|
|
|
|
let mut target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal);
|
|
|
|
|
touching.constrain_velocity(models,hitbox_mesh,&mut target_velocity);
|
|
|
|
|
(gravity,target_velocity)
|
|
|
|
|
let target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal);
|
|
|
|
|
let target_velocity_clipped=touching.constrain_velocity(models,hitbox_mesh,target_velocity);
|
|
|
|
|
(gravity,target_velocity_clipped)
|
|
|
|
|
}
|
|
|
|
|
fn ladder_things(ladder_settings:&gameplay_style::LadderSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){
|
|
|
|
|
let normal=contact_normal(models,hitbox_mesh,contact);
|
|
|
|
|
let gravity=touching.base_acceleration(models,style,camera,input_state);
|
|
|
|
|
let control_dir=style.get_y_control_dir(camera,input_state.controls);
|
|
|
|
|
let mut target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal);
|
|
|
|
|
touching.constrain_velocity(models,hitbox_mesh,&mut target_velocity);
|
|
|
|
|
(gravity,target_velocity)
|
|
|
|
|
let target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal);
|
|
|
|
|
let target_velocity_clipped=touching.constrain_velocity(models,hitbox_mesh,target_velocity);
|
|
|
|
|
(gravity,target_velocity_clipped)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
@@ -520,20 +534,20 @@ enum MoveState{
|
|
|
|
|
}
|
|
|
|
|
impl MoveState{
|
|
|
|
|
//call this after state.move_state is changed
|
|
|
|
|
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn apply_enum(&self,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
match self{
|
|
|
|
|
MoveState::Fly=>body.acceleration=vec3::ZERO,
|
|
|
|
|
MoveState::Air=>{
|
|
|
|
|
//calculate base acceleration
|
|
|
|
|
let a=touching.base_acceleration(models,style,camera,input_state);
|
|
|
|
|
//set_acceleration clips according to contacts
|
|
|
|
|
set_acceleration(body,touching,models,hitbox_mesh,a);
|
|
|
|
|
set_acceleration_cull(body,touching,models,hitbox_mesh,a);
|
|
|
|
|
},
|
|
|
|
|
_=>(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//function to coerce &mut self into &self
|
|
|
|
|
fn apply_to_body(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn apply_to_body(&self,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
match self{
|
|
|
|
|
MoveState::Air=>(),
|
|
|
|
|
MoveState::Water=>(),
|
|
|
|
|
@@ -541,14 +555,14 @@ impl MoveState{
|
|
|
|
|
//set velocity according to current control state
|
|
|
|
|
let v=style.get_propulsion_control_dir(camera,input_state.controls)*80;
|
|
|
|
|
//set_velocity clips velocity according to current touching state
|
|
|
|
|
set_velocity(body,touching,models,hitbox_mesh,v);
|
|
|
|
|
set_velocity_cull(body,touching,models,hitbox_mesh,v);
|
|
|
|
|
},
|
|
|
|
|
MoveState::Walk(walk_state)
|
|
|
|
|
|MoveState::Ladder(walk_state)
|
|
|
|
|
=>{
|
|
|
|
|
//accelerate towards walk target or do nothing
|
|
|
|
|
let a=walk_state.target.acceleration();
|
|
|
|
|
set_acceleration(body,touching,models,hitbox_mesh,a);
|
|
|
|
|
set_acceleration_cull(body,touching,models,hitbox_mesh,a);
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -611,20 +625,20 @@ impl MoveState{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//lmao idk this is convenient
|
|
|
|
|
fn apply_enum_and_input_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn apply_enum_and_input_and_body(&mut self,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
self.apply_enum(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
self.apply_input(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
}
|
|
|
|
|
fn apply_enum_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn apply_enum_and_body(&mut self,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
self.apply_enum(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
}
|
|
|
|
|
fn apply_input_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn apply_input_and_body(&mut self,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
self.apply_input(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
}
|
|
|
|
|
fn set_move_state(&mut self,move_state:MoveState,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
fn set_move_state(&mut self,move_state:MoveState,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
|
|
|
|
*self=move_state;
|
|
|
|
|
//this function call reads the above state that was just set
|
|
|
|
|
self.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
|
|
|
|
@@ -793,7 +807,7 @@ impl TouchingState{
|
|
|
|
|
//TODO: add water
|
|
|
|
|
a
|
|
|
|
|
}
|
|
|
|
|
fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:&mut Planar64Vec3){
|
|
|
|
|
fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:Planar64Vec3)->Planar64Vec3{
|
|
|
|
|
let contacts=self.contacts.iter().map(|contact|{
|
|
|
|
|
let n=contact_normal(models,hitbox_mesh,contact);
|
|
|
|
|
crate::push_solve::Contact{
|
|
|
|
|
@@ -802,9 +816,9 @@ impl TouchingState{
|
|
|
|
|
normal:n,
|
|
|
|
|
}
|
|
|
|
|
}).collect();
|
|
|
|
|
*velocity=crate::push_solve::push_solve(&contacts,*velocity);
|
|
|
|
|
crate::push_solve::push_solve(&contacts,velocity)
|
|
|
|
|
}
|
|
|
|
|
fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:&mut Planar64Vec3){
|
|
|
|
|
fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:Planar64Vec3)->Planar64Vec3{
|
|
|
|
|
let contacts=self.contacts.iter().map(|contact|{
|
|
|
|
|
let n=contact_normal(models,hitbox_mesh,contact);
|
|
|
|
|
crate::push_solve::Contact{
|
|
|
|
|
@@ -813,7 +827,7 @@ impl TouchingState{
|
|
|
|
|
normal:n,
|
|
|
|
|
}
|
|
|
|
|
}).collect();
|
|
|
|
|
*acceleration=crate::push_solve::push_solve(&contacts,*acceleration);
|
|
|
|
|
crate::push_solve::push_solve(&contacts,acceleration)
|
|
|
|
|
}
|
|
|
|
|
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){
|
|
|
|
|
// let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time);
|
|
|
|
|
@@ -916,10 +930,10 @@ impl PhysicsState{
|
|
|
|
|
self.move_state.cull_velocity(velocity,&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
|
|
|
|
}
|
|
|
|
|
fn set_move_state(&mut self,data:&PhysicsData,move_state:MoveState){
|
|
|
|
|
self.move_state.set_move_state(move_state,&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
|
|
|
|
self.move_state.set_move_state(move_state,&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
|
|
|
|
}
|
|
|
|
|
fn apply_input_and_body(&mut self,data:&PhysicsData){
|
|
|
|
|
self.move_state.apply_input_and_body(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
|
|
|
|
self.move_state.apply_input_and_body(&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
|
|
|
|
}
|
|
|
|
|
//state mutated on collision:
|
|
|
|
|
//Accelerator
|
|
|
|
|
@@ -1280,19 +1294,17 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
|
|
|
|
|
let mut culled=false;
|
|
|
|
|
touching.contacts.retain(|contact|{
|
|
|
|
|
let n=contact_normal(models,hitbox_mesh,contact);
|
|
|
|
|
let r=n.dot(v).is_positive();
|
|
|
|
|
let r=(n.dot(v)>>52).is_positive();
|
|
|
|
|
if r{
|
|
|
|
|
culled=true;
|
|
|
|
|
println!("set_velocity_cull contact={:?}",contact);
|
|
|
|
|
}
|
|
|
|
|
!r
|
|
|
|
|
});
|
|
|
|
|
set_velocity(body,touching,models,hitbox_mesh,v);
|
|
|
|
|
culled
|
|
|
|
|
}
|
|
|
|
|
fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut v:Planar64Vec3){
|
|
|
|
|
touching.constrain_velocity(models,hitbox_mesh,&mut v);
|
|
|
|
|
body.velocity=v;
|
|
|
|
|
fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,v:Planar64Vec3){
|
|
|
|
|
body.velocity=touching.constrain_velocity(models,hitbox_mesh,v);;
|
|
|
|
|
}
|
|
|
|
|
fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3)->bool{
|
|
|
|
|
//This is not correct but is better than what I have
|
|
|
|
|
@@ -1302,16 +1314,14 @@ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&Phys
|
|
|
|
|
let r=n.dot(a).is_positive();
|
|
|
|
|
if r{
|
|
|
|
|
culled=true;
|
|
|
|
|
println!("set_acceleration_cull contact={:?}",contact);
|
|
|
|
|
}
|
|
|
|
|
!r
|
|
|
|
|
});
|
|
|
|
|
set_acceleration(body,touching,models,hitbox_mesh,a);
|
|
|
|
|
culled
|
|
|
|
|
}
|
|
|
|
|
fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut a:Planar64Vec3){
|
|
|
|
|
touching.constrain_acceleration(models,hitbox_mesh,&mut a);
|
|
|
|
|
body.acceleration=a;
|
|
|
|
|
fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3){
|
|
|
|
|
body.acceleration=touching.constrain_acceleration(models,hitbox_mesh,a);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn teleport(
|
|
|
|
|
@@ -1331,7 +1341,7 @@ fn teleport(
|
|
|
|
|
time:Time,
|
|
|
|
|
){
|
|
|
|
|
set_position(point,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
|
|
|
|
|
set_acceleration(body,touching,models,hitbox_mesh,style.gravity);
|
|
|
|
|
set_acceleration_cull(body,touching,models,hitbox_mesh,style.gravity);
|
|
|
|
|
}
|
|
|
|
|
enum TeleportToSpawnError{
|
|
|
|
|
NoModel,
|
|
|
|
|
@@ -1512,18 +1522,15 @@ fn collision_start_contact(
|
|
|
|
|
time:Time,
|
|
|
|
|
){
|
|
|
|
|
let incident_velocity=body.velocity;
|
|
|
|
|
//add to touching
|
|
|
|
|
touching.insert(Collision::Contact(contact));
|
|
|
|
|
//clip v
|
|
|
|
|
set_velocity(body,touching,models,hitbox_mesh,incident_velocity);
|
|
|
|
|
let clipped_velocity=touching.constrain_velocity(models,hitbox_mesh,incident_velocity);
|
|
|
|
|
let mut allow_jump=true;
|
|
|
|
|
let model_id=contact.model_id.into();
|
|
|
|
|
let mut allow_run_teleport_behaviour=not_spawn_at(mode,model_id);
|
|
|
|
|
match &attr.contacting.contact_behaviour{
|
|
|
|
|
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
|
|
|
|
Some(gameplay_attributes::ContactingBehaviour::Surf)=>(),
|
|
|
|
|
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
|
|
|
|
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
|
|
|
|
|
let reflected_velocity=body.velocity+((body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).fix_1();
|
|
|
|
|
let reflected_velocity=body.velocity+((clipped_velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).fix_1();
|
|
|
|
|
set_velocity(body,touching,models,hitbox_mesh,reflected_velocity);
|
|
|
|
|
},
|
|
|
|
|
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>
|
|
|
|
|
@@ -1762,19 +1769,19 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|
|
|
|
|MoveState::Fly
|
|
|
|
|
=>println!("ReachWalkTargetVelocity fired for non-walking MoveState"),
|
|
|
|
|
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{
|
|
|
|
|
match &walk_state.target{
|
|
|
|
|
//velocity is already handled by advance_time
|
|
|
|
|
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
|
|
|
|
//which means that gravity can be fully cancelled
|
|
|
|
|
//ignore moving platforms for now
|
|
|
|
|
let target=core::mem::replace(&mut walk_state.target,TransientAcceleration::Reached);
|
|
|
|
|
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
|
|
|
|
|
// check what the target was to see if it was invalid
|
|
|
|
|
match target{
|
|
|
|
|
//you are not supposed to reach a walk target which is already reached!
|
|
|
|
|
TransientAcceleration::Reached=>unreachable!(),
|
|
|
|
|
TransientAcceleration::Reachable{acceleration:_,time:_}=>{
|
|
|
|
|
//velocity is already handled by advance_time
|
|
|
|
|
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
|
|
|
|
//which means that gravity can be fully cancelled
|
|
|
|
|
//ignore moving platforms for now
|
|
|
|
|
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
|
|
|
|
|
walk_state.target=TransientAcceleration::Reached;
|
|
|
|
|
},
|
|
|
|
|
TransientAcceleration::Reached=>println!("Invalid walk target: Reached"),
|
|
|
|
|
TransientAcceleration::Reachable{..}=>(),
|
|
|
|
|
//you are not supposed to reach an unreachable walk target!
|
|
|
|
|
TransientAcceleration::Unreachable{acceleration:_}=>unreachable!(),
|
|
|
|
|
TransientAcceleration::Unreachable{..}=>println!("Invalid walk target: Unreachable"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1833,7 +1840,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|
|
|
|
let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact);
|
|
|
|
|
let booster_option=data.models.contact_attr(walk_state.contact.model_id).general.booster.as_ref();
|
|
|
|
|
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option);
|
|
|
|
|
state.cull_velocity(&data,jumped_velocity);
|
|
|
|
|
state.cull_velocity(data,jumped_velocity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
b_refresh_walk_target=false;
|
|
|
|
|
@@ -2145,22 +2152,23 @@ mod test{
|
|
|
|
|
Deterministic,
|
|
|
|
|
NonDeterministic,
|
|
|
|
|
}
|
|
|
|
|
#[allow(unused)]
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
enum DetErr{
|
|
|
|
|
enum ReplayError{
|
|
|
|
|
Load(file::LoadError),
|
|
|
|
|
IO(std::io::Error),
|
|
|
|
|
}
|
|
|
|
|
impl From<file::LoadError> for DetErr{
|
|
|
|
|
impl From<file::LoadError> for ReplayError{
|
|
|
|
|
fn from(value:file::LoadError)->Self{
|
|
|
|
|
Self::Load(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl From<std::io::Error> for DetErr{
|
|
|
|
|
impl From<std::io::Error> for ReplayError{
|
|
|
|
|
fn from(value:std::io::Error)->Self{
|
|
|
|
|
Self::IO(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn run_bot_on_map(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->DeterminismResult{
|
|
|
|
|
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->DeterminismResult{
|
|
|
|
|
// create default physics state
|
|
|
|
|
let mut physics_deterministic=PhysicsState::default();
|
|
|
|
|
// create a second physics state
|
|
|
|
|
@@ -2169,6 +2177,8 @@ mod test{
|
|
|
|
|
// invent a new bot id and insert the replay
|
|
|
|
|
println!("simulating...");
|
|
|
|
|
|
|
|
|
|
let mut non_idle_count=0;
|
|
|
|
|
|
|
|
|
|
for (i,ins) in bot.instructions.into_iter().enumerate(){
|
|
|
|
|
let state_deterministic=physics_deterministic.clone();
|
|
|
|
|
let state_filtered=physics_filtered.clone();
|
|
|
|
|
@@ -2176,14 +2186,16 @@ mod test{
|
|
|
|
|
match ins{
|
|
|
|
|
strafesnet_common::instruction::TimedInstruction{instruction:strafesnet_common::physics::Instruction::Idle,..}=>(),
|
|
|
|
|
other=>{
|
|
|
|
|
non_idle_count+=1;
|
|
|
|
|
// run
|
|
|
|
|
PhysicsContext::run_input_instruction(&mut physics_filtered,&physics_data,other.clone());
|
|
|
|
|
// check if position matches
|
|
|
|
|
let b0=physics_deterministic.camera_body();
|
|
|
|
|
let b1=physics_filtered.camera_body();
|
|
|
|
|
if b0.position!=b1.position{
|
|
|
|
|
println!("instruction #{i}={:?}",other);
|
|
|
|
|
println!("desync at instruction #{}",i);
|
|
|
|
|
println!("non idle instructions completed={non_idle_count}");
|
|
|
|
|
println!("instruction #{i}={:?}",other);
|
|
|
|
|
println!("deterministic state0:\n{state_deterministic:?}");
|
|
|
|
|
println!("filtered state0:\n{state_filtered:?}");
|
|
|
|
|
println!("deterministic state1:\n{:?}",physics_deterministic);
|
|
|
|
|
@@ -2203,13 +2215,13 @@ mod test{
|
|
|
|
|
}
|
|
|
|
|
DeterminismResult::Deterministic
|
|
|
|
|
}
|
|
|
|
|
type LeSend=Result<Option<DeterminismResult>,file::LoadError>;
|
|
|
|
|
fn do_thread<'a>(s:&'a std::thread::Scope<'a,'_>,file_path:std::path::PathBuf,send:std::sync::mpsc::Sender<LeSend>,physics_data:&'a PhysicsData){
|
|
|
|
|
type ThreadResult=Result<Option<DeterminismResult>,file::LoadError>;
|
|
|
|
|
fn do_thread<'a>(s:&'a std::thread::Scope<'a,'_>,file_path:std::path::PathBuf,send:std::sync::mpsc::Sender<ThreadResult>,physics_data:&'a PhysicsData){
|
|
|
|
|
s.spawn(move ||{
|
|
|
|
|
let result=match file::load(file_path.as_path()){
|
|
|
|
|
Ok(file::LoadFormat::Bot(bot))=>{
|
|
|
|
|
println!("Running {:?}",file_path.file_stem());
|
|
|
|
|
Ok(Some(run_bot_on_map(bot,physics_data)))
|
|
|
|
|
Ok(Some(segment_determinism(bot,physics_data)))
|
|
|
|
|
},
|
|
|
|
|
Ok(_)=>{
|
|
|
|
|
println!("Provided bot file is not a bot file!");
|
|
|
|
|
@@ -2230,8 +2242,8 @@ mod test{
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_determinism()->Result<(),DetErr>{
|
|
|
|
|
let thread_limit=std::thread::available_parallelism().unwrap().get();
|
|
|
|
|
fn test_determinism()->Result<(),ReplayError>{
|
|
|
|
|
let thread_limit=std::thread::available_parallelism()?.get();
|
|
|
|
|
println!("loading map file..");
|
|
|
|
|
let file::LoadFormat::Map(map)=file::load("../tools/bhop_maps/5692113331.snfm")? else{
|
|
|
|
|
panic!("Provided map file is not a map file!");
|
|
|
|
|
@@ -2278,7 +2290,7 @@ mod test{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("done.");
|
|
|
|
|
Ok::<_,DetErr>(thread_results)
|
|
|
|
|
Ok::<_,ReplayError>(thread_results)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
// tally results
|
|
|
|
|
@@ -2287,14 +2299,14 @@ mod test{
|
|
|
|
|
deterministic:u32,
|
|
|
|
|
nondeterministic:u32,
|
|
|
|
|
invalid:u32,
|
|
|
|
|
failed:u32,
|
|
|
|
|
error:u32,
|
|
|
|
|
}
|
|
|
|
|
let Totals{deterministic,nondeterministic,invalid,failed}=thread_results.into_iter().fold(Totals::default(),|mut totals,result|{
|
|
|
|
|
let Totals{deterministic,nondeterministic,invalid,error}=thread_results.into_iter().fold(Totals::default(),|mut totals,result|{
|
|
|
|
|
match result{
|
|
|
|
|
Ok(Some(DeterminismResult::Deterministic))=>totals.deterministic+=1,
|
|
|
|
|
Ok(Some(DeterminismResult::NonDeterministic))=>totals.nondeterministic+=1,
|
|
|
|
|
Ok(None)=>totals.invalid+=1,
|
|
|
|
|
Err(_)=>totals.failed+=1,
|
|
|
|
|
Err(_)=>totals.error+=1,
|
|
|
|
|
}
|
|
|
|
|
totals
|
|
|
|
|
});
|
|
|
|
|
@@ -2302,11 +2314,11 @@ mod test{
|
|
|
|
|
println!("deterministic={deterministic}");
|
|
|
|
|
println!("nondeterministic={nondeterministic}");
|
|
|
|
|
println!("invalid={invalid}");
|
|
|
|
|
println!("failed={failed}");
|
|
|
|
|
println!("error={error}");
|
|
|
|
|
|
|
|
|
|
assert!(nondeterministic==0);
|
|
|
|
|
assert!(invalid==0);
|
|
|
|
|
assert!(failed==0);
|
|
|
|
|
assert!(error==0);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|