Compare commits

..

28 Commits

Author SHA1 Message Date
f52399a931 Session::debug_raycast_print_model_id_if_changed 2025-03-11 17:45:26 -07:00
7be7d40c8a physics: PhysicsData::trace_ray 2025-03-11 17:45:26 -07:00
78c53ae429 physics: set style on reset and spawn 2025-03-11 17:09:29 -07:00
e794b2649c bsp_loader: implement ladders and water 2025-03-11 16:41:46 -07:00
50f6fe5bd8 common: fix source styles 2025-03-11 15:26:45 -07:00
fc3681f41f it: read_entire_file use built in 2025-03-11 14:07:24 -07:00
a2369b4211 rbx_loader: never do &Vec + deconstruct Face2 (style) 2025-03-09 23:47:23 -07:00
bfea49ffae it: measure simulate speed 2025-03-08 17:39:47 -08:00
8c7063d340 common: aabb: make function inline-able 2025-03-08 14:34:54 -08:00
651150760c common: aabb: use midpoint for center 2025-03-08 14:34:54 -08:00
3d203e2da9 fixed_wide: add midpoint 2025-03-08 14:34:54 -08:00
3798f438cf use as_bits_mut to optimize sqrt 2025-03-08 13:58:56 -08:00
4ace8317fe update deps 2025-03-08 13:55:14 -08:00
ab4a9bb922 bsp_loader: generate prop matrix with euler angles 2025-03-02 18:23:47 -08:00
a9ef07ce78 update vbsp 2025-03-02 18:23:47 -08:00
561e41c760 common: delete unused ModesUpdate 2025-02-28 13:15:55 -08:00
9896a09576 common: delete updatable trait 2025-02-28 13:15:55 -08:00
82840fa6cb refactor type safety for modes data normalization 2025-02-28 13:15:55 -08:00
2c87cb71df common: gameplay_modes: rename internal field on ModeBuilder 2025-02-28 12:37:49 -08:00
709f622547 common: don't use .0 for newtypes 2025-02-28 12:10:16 -08:00
b20264b382 common: move ModesBuilder to common 2025-02-28 11:08:38 -08:00
17cce2d702 silence lints 2025-02-28 10:50:53 -08:00
a2b2ddc9ce bsp_loader: use texture_info function 2025-02-26 15:29:23 -08:00
cb57d893e0 update vbsp fork 2025-02-26 15:26:56 -08:00
9e8d66cec1 map-tool: gimme_them_textures iterate over a shorter list of prop model paths 2025-02-26 12:00:51 -08:00
39fe833198 map-tool: grab brush models 2025-02-26 12:00:51 -08:00
e7688a95fd bsp_loader: generate physics from brushes 2025-02-26 12:00:51 -08:00
d2cc98b04d deps: fork vbsp 2025-02-26 11:59:59 -08:00
22 changed files with 740 additions and 587 deletions

493
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,4 +1,5 @@
use std::collections::{HashMap,HashSet}; use std::collections::{HashMap,HashSet};
use crate::model::DirectedEdge;
use crate::model::{self as model_physics,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId}; use crate::model::{self as model_physics,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId};
use strafesnet_common::bvh; use strafesnet_common::bvh;
use strafesnet_common::map; use strafesnet_common::map;
@ -280,7 +281,8 @@ impl PhysicsCamera{
.clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT); .clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT);
mat3::from_rotation_yx(ax,ay) mat3::from_rotation_yx(ax,ay)
} }
fn rotation(&self)->Planar64Mat3{ #[inline]
pub fn rotation(&self)->Planar64Mat3{
self.get_rotation(self.clamped_mouse_pos) self.get_rotation(self.clamped_mouse_pos)
} }
fn simulate_move_rotation(&self,mouse_delta:glam::IVec2)->Planar64Mat3{ fn simulate_move_rotation(&self,mouse_delta:glam::IVec2)->Planar64Mat3{
@ -980,12 +982,37 @@ impl PhysicsContext<'_>{
} }
} }
impl PhysicsData{ impl PhysicsData{
pub fn trace_ray(&self,ray:strafesnet_common::ray::Ray)->Option<ModelId>{
let (_time,convex_mesh_id)=self.bvh.sample_ray(&ray,Time::ZERO,Time::MAX/4,|&model,ray|{
let mesh=self.models.mesh(model);
// brute force trace every face
mesh.faces().filter_map(|face_id|{
let (n,d)=mesh.face_nd(face_id);
// trace ray onto face
// n.(o+d*t)==n.p
// n.o + n.d * t == n.p
// t == (n.p - n.o)/n.d
let nd=n.dot(ray.direction);
if nd.is_zero(){
return None;
}
let t=(d-n.dot(ray.origin))/nd;
// check if point of intersection is behind face edges
// *2 because average of 2 vertices
let p=ray.extrapolate(t)*2;
mesh.face_edges(face_id).iter().all(|&directed_edge_id|{
let edge_n=mesh.directed_edge_n(directed_edge_id);
let cross_n=edge_n.cross(n);
let &[vert0,vert1]=mesh.edge_verts(directed_edge_id.as_undirected()).as_ref();
cross_n.dot(p)<cross_n.dot(mesh.vert(vert0)+mesh.vert(vert1))
}).then(||t)
}).min().map(Into::into)
})?;
Some(convex_mesh_id.model_id.into())
}
/// use with caution, this is the only non-instruction way to mess with physics /// use with caution, this is the only non-instruction way to mess with physics
pub fn generate_models(&mut self,map:&map::CompleteMap){ pub fn generate_models(&mut self,map:&map::CompleteMap){
let mut modes=map.modes.clone(); let mut modes=map.modes.clone().denormalize();
for mode in &mut modes.modes{
mode.denormalize_data();
}
let mut used_contact_attributes=Vec::new(); let mut used_contact_attributes=Vec::new();
let mut used_intersect_attributes=Vec::new(); let mut used_intersect_attributes=Vec::new();
@ -1814,14 +1841,18 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}, },
Instruction::Mode(ModeInstruction::Restart(mode_id))=>{ Instruction::Mode(ModeInstruction::Restart(mode_id))=>{
//teleport to mode start zone //teleport to mode start zone
let mut spawn_point=vec3::ZERO;
let mode=data.modes.get_mode(mode_id); let mode=data.modes.get_mode(mode_id);
let spawn_point=mode.and_then(|mode| if let Some(mode)=mode{
// set style
state.style=mode.get_style().clone();
//TODO: spawn at the bottom of the start zone plus the hitbox size //TODO: spawn at the bottom of the start zone plus the hitbox size
//TODO: set camera andles to face the same way as the start zone //TODO: set camera angles to face the same way as the start zone
data.models.get_model_transform(mode.get_start().into()).map(|transform| if let Some(transform)=data.models.get_model_transform(mode.get_start()){
transform.vertex.translation // NOTE: this value may be 0,0,0 on source maps
) spawn_point=transform.vertex.translation;
).unwrap_or(vec3::ZERO); }
}
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time); set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::Air);
@ -1831,6 +1862,9 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
Instruction::Mode(ModeInstruction::Spawn(mode_id,stage_id))=>{ Instruction::Mode(ModeInstruction::Spawn(mode_id,stage_id))=>{
//spawn at a particular stage //spawn at a particular stage
if let Some(mode)=data.modes.get_mode(mode_id){ if let Some(mode)=data.modes.get_mode(mode_id){
// set style
state.style=mode.get_style().clone();
// teleport to stage in mode
if let Some(stage)=mode.get_stage(stage_id){ if let Some(stage)=mode.get_stage(stage_id){
let _=teleport_to_spawn( let _=teleport_to_spawn(
stage.spawn(), stage.spawn(),

@ -161,6 +161,7 @@ pub struct Session{
recording:Recording, recording:Recording,
//players:HashMap<PlayerId,Simulation>, //players:HashMap<PlayerId,Simulation>,
replays:HashMap<BotId,Replay>, replays:HashMap<BotId,Replay>,
last_ray_hit:Option<strafesnet_common::model::ModelId>,
} }
impl Session{ impl Session{
pub fn new( pub fn new(
@ -177,6 +178,7 @@ impl Session{
view_state:ViewState::Play, view_state:ViewState::Play,
recording:Default::default(), recording:Default::default(),
replays:HashMap::new(), replays:HashMap::new(),
last_ray_hit:None,
} }
} }
fn clear_recording(&mut self){ fn clear_recording(&mut self){
@ -194,6 +196,19 @@ impl Session{
), ),
} }
} }
pub fn debug_raycast_print_model_id_if_changed(&mut self,time:SessionTime){
if let Some(frame_state)=self.get_frame_state(time){
let ray=strafesnet_common::ray::Ray{
origin:frame_state.body.extrapolated_position(self.simulation.timer.time(time)),
direction:-frame_state.camera.rotation().z_axis,
};
let model_id=self.geometry_shared.trace_ray(ray);
if model_id!=self.last_ray_hit{
println!("hit={model_id:?}");
self.last_ray_hit=model_id;
}
}
}
pub fn user_settings(&self)->&UserSettings{ pub fn user_settings(&self)->&UserSettings{
&self.user_settings &self.user_settings
} }

@ -1,5 +1,7 @@
use std::{io::{Cursor,Read},path::Path}; use std::io::Cursor;
use std::path::Path;
use std::time::Instant;
use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext}; use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext};
@ -37,9 +39,7 @@ impl From<strafesnet_snf::bot::Error> for ReplayError{
} }
fn read_entire_file(path:impl AsRef<Path>)->Result<Cursor<Vec<u8>>,std::io::Error>{ fn read_entire_file(path:impl AsRef<Path>)->Result<Cursor<Vec<u8>>,std::io::Error>{
let mut file=std::fs::File::open(path)?; let data=std::fs::read(path)?;
let mut data=Vec::new();
file.read_to_end(&mut data)?;
Ok(Cursor::new(data)) Ok(Cursor::new(data))
} }
@ -72,7 +72,12 @@ enum DeterminismResult{
Deterministic, Deterministic,
NonDeterministic, NonDeterministic,
} }
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->DeterminismResult{ struct SegmentResult{
determinism:DeterminismResult,
ticks:u64,
nanoseconds:u64,
}
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->SegmentResult{
// create default physics state // create default physics state
let mut physics_deterministic=PhysicsState::default(); let mut physics_deterministic=PhysicsState::default();
// create a second physics state // create a second physics state
@ -82,6 +87,9 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
println!("simulating..."); println!("simulating...");
let mut non_idle_count=0; let mut non_idle_count=0;
let instruction_count=bot.instructions.len();
let start=Instant::now();
for (i,ins) in bot.instructions.into_iter().enumerate(){ for (i,ins) in bot.instructions.into_iter().enumerate(){
let state_deterministic=physics_deterministic.clone(); let state_deterministic=physics_deterministic.clone();
@ -97,6 +105,7 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
let b0=physics_deterministic.camera_body(); let b0=physics_deterministic.camera_body();
let b1=physics_filtered.camera_body(); let b1=physics_filtered.camera_body();
if b0.position!=b1.position{ if b0.position!=b1.position{
let nanoseconds=start.elapsed().as_nanos() as u64;
println!("desync at instruction #{}",i); println!("desync at instruction #{}",i);
println!("non idle instructions completed={non_idle_count}"); println!("non idle instructions completed={non_idle_count}");
println!("instruction #{i}={:?}",other); println!("instruction #{i}={:?}",other);
@ -104,11 +113,16 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
println!("filtered state0:\n{state_filtered:?}"); println!("filtered state0:\n{state_filtered:?}");
println!("deterministic state1:\n{:?}",physics_deterministic); println!("deterministic state1:\n{:?}",physics_deterministic);
println!("filtered state1:\n{:?}",physics_filtered); println!("filtered state1:\n{:?}",physics_filtered);
return DeterminismResult::NonDeterministic; return SegmentResult{
determinism:DeterminismResult::NonDeterministic,
ticks:1+i as u64+non_idle_count,
nanoseconds,
};
} }
}, },
} }
} }
let nanoseconds=start.elapsed().as_nanos() as u64;
match physics_deterministic.get_finish_time(){ match physics_deterministic.get_finish_time(){
Some(time)=>println!("[with idle] finish time:{}",time), Some(time)=>println!("[with idle] finish time:{}",time),
None=>println!("[with idle] simulation did not end in finished state"), None=>println!("[with idle] simulation did not end in finished state"),
@ -117,9 +131,14 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
Some(time)=>println!("[filtered] finish time:{}",time), Some(time)=>println!("[filtered] finish time:{}",time),
None=>println!("[filtered] simulation did not end in finished state"), None=>println!("[filtered] simulation did not end in finished state"),
} }
DeterminismResult::Deterministic SegmentResult{
determinism:DeterminismResult::Deterministic,
ticks:instruction_count as u64+non_idle_count,
nanoseconds,
}
} }
type ThreadResult=Result<Option<DeterminismResult>,ReplayError>; type ThreadResult=Result<Option<SegmentResult>,ReplayError>;
fn read_and_run(file_path:std::path::PathBuf,physics_data:&PhysicsData)->ThreadResult{ fn read_and_run(file_path:std::path::PathBuf,physics_data:&PhysicsData)->ThreadResult{
let data=read_entire_file(file_path.as_path())?; let data=read_entire_file(file_path.as_path())?;
let bot=strafesnet_snf::read_bot(data)?.read_all()?; let bot=strafesnet_snf::read_bot(data)?.read_all()?;
@ -198,10 +217,18 @@ fn test_determinism()->Result<(),ReplayError>{
invalid:u32, invalid:u32,
error:u32, error:u32,
} }
let mut nanoseconds=0;
let mut ticks=0;
let Totals{deterministic,nondeterministic,invalid,error}=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{ match result{
Ok(Some(DeterminismResult::Deterministic))=>totals.deterministic+=1, Ok(Some(segment_result))=>{
Ok(Some(DeterminismResult::NonDeterministic))=>totals.nondeterministic+=1, ticks+=segment_result.ticks;
nanoseconds+=segment_result.nanoseconds;
match segment_result.determinism{
DeterminismResult::Deterministic=>totals.deterministic+=1,
DeterminismResult::NonDeterministic=>totals.nondeterministic+=1,
}
},
Ok(None)=>totals.invalid+=1, Ok(None)=>totals.invalid+=1,
Err(_)=>totals.error+=1, Err(_)=>totals.error+=1,
} }
@ -212,6 +239,7 @@ fn test_determinism()->Result<(),ReplayError>{
println!("nondeterministic={nondeterministic}"); println!("nondeterministic={nondeterministic}");
println!("invalid={invalid}"); println!("invalid={invalid}");
println!("error={error}"); println!("error={error}");
println!("average ticks/s per core: {}",ticks*1_000_000_000/nanoseconds);
assert!(nondeterministic==0); assert!(nondeterministic==0);
assert!(invalid==0); assert!(invalid==0);

@ -13,7 +13,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
glam = "0.30.0" glam = "0.30.0"
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
vbsp = { version = "0.7.0-codegen5", registry = "strafesnet", default-features = false } vbsp = "0.8.0"
vbsp-entities = { version = "0.1.0", registry = "strafesnet", default-features = false, features = ["css"]} vbsp-entities-css = "0.6.0"
vmdl = "0.2.0" vmdl = "0.2.0"
vpk = "0.2.0" vpk = "0.3.0"

@ -240,7 +240,7 @@ pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,Brus
// The so-called tumor brushes have TRIGGER bit set // The so-called tumor brushes have TRIGGER bit set
// but also ignore visleaf hint and skip sides // but also ignore visleaf hint and skip sides
const TUMOR:vbsp::TextureFlags=vbsp::TextureFlags::HINT.union(vbsp::TextureFlags::SKIP).union(vbsp::TextureFlags::TRIGGER); const TUMOR:vbsp::TextureFlags=vbsp::TextureFlags::HINT.union(vbsp::TextureFlags::SKIP).union(vbsp::TextureFlags::TRIGGER);
if let Some(texture_info)=bsp.textures_info.get(side.texture_info as usize){ if let Some(texture_info)=bsp.texture_info(side.texture_info as usize){
if texture_info.flags.intersects(TUMOR){ if texture_info.flags.intersects(TUMOR){
return None; return None;
} }

@ -1,11 +1,12 @@
use std::borrow::Cow; use std::borrow::Cow;
use vbsp_entities::css::Entity; use vbsp_entities_css::Entity;
use strafesnet_common::{map,model,integer,gameplay_attributes}; use strafesnet_common::{map,model,integer,gameplay_attributes as attr};
use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader}; use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
use strafesnet_common::gameplay_modes::{NormalizedMode,NormalizedModes,Mode,Stage};
use crate::valve_transform; use crate::valve_transform;
@ -41,7 +42,7 @@ fn add_brush<'a>(
model:&'a str, model:&'a str,
origin:vbsp::Vector, origin:vbsp::Vector,
rendercolor:vbsp::Color, rendercolor:vbsp::Color,
attributes:gameplay_attributes::CollisionAttributesId, attributes:attr::CollisionAttributesId,
){ ){
let transform=integer::Planar64Affine3::from_translation( let transform=integer::Planar64Affine3::from_translation(
valve_transform(origin.into()) valve_transform(origin.into())
@ -82,29 +83,60 @@ pub fn convert<'a>(
)->PartialMap1{ )->PartialMap1{
let bsp=bsp.as_ref(); let bsp=bsp.as_ref();
//figure out real attributes later //figure out real attributes later
let mut unique_attributes=Vec::new(); let unique_attributes=vec![
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration); attr::CollisionAttributes::Decoration,
unique_attributes.push(gameplay_attributes::CollisionAttributes::contact_default()); attr::CollisionAttributes::contact_default(),
unique_attributes.push(gameplay_attributes::CollisionAttributes::intersect_default()); attr::CollisionAttributes::intersect_default(),
const ATTRIBUTE_DECORATION:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0); // ladder
const ATTRIBUTE_CONTACT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(1); attr::CollisionAttributes::Contact(
const ATTRIBUTE_INTERSECT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(2); attr::ContactAttributes{
contacting:attr::ContactingAttributes{
contact_behaviour:Some(attr::ContactingBehaviour::Ladder(
attr::ContactingLadder{sticky:true}
))
},
general:attr::GeneralAttributes::default(),
}
),
// water
attr::CollisionAttributes::Intersect(
attr::IntersectAttributes{
intersecting:attr::IntersectingAttributes{
water:Some(attr::IntersectingWater{
viscosity:integer::Planar64::ONE,
density:integer::Planar64::ONE,
velocity:integer::vec3::ZERO,
}),
},
general:attr::GeneralAttributes::default(),
}
),
];
const ATTRIBUTE_DECORATION:attr::CollisionAttributesId=attr::CollisionAttributesId::new(0);
const ATTRIBUTE_CONTACT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(1);
const ATTRIBUTE_INTERSECT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(2);
const ATTRIBUTE_LADDER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(3);
const ATTRIBUTE_WATER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(4);
//declare all prop models to Loader //declare all prop models to Loader
let mut prop_models=bsp.static_props().map(|prop|{ let mut prop_models=bsp.static_props().map(|prop|{
const DEG_TO_RAD:f32=std::f32::consts::TAU/360.0;
//get or create mesh_id //get or create mesh_id
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model()); let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
let placement=prop.as_prop_placement();
model::Model{ model::Model{
mesh:mesh_id, mesh:mesh_id,
attributes:ATTRIBUTE_DECORATION, attributes:ATTRIBUTE_DECORATION,
transform:integer::Planar64Affine3::new( transform:integer::Planar64Affine3::new(
integer::mat3::try_from_f32_array_2d(( integer::mat3::try_from_f32_array_2d(
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
//TODO: figure this out //TODO: figure this out
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into())) glam::Mat3A::from_euler(
).to_cols_array_2d()).unwrap(), glam::EulerRot::XYZ,
valve_transform(placement.origin.into()), prop.angles.pitch*DEG_TO_RAD,
prop.angles.yaw*DEG_TO_RAD,
prop.angles.roll*DEG_TO_RAD
).to_cols_array_2d()
).unwrap(),
valve_transform(prop.origin.into()),
), ),
color:glam::Vec4::ONE, color:glam::Vec4::ONE,
} }
@ -181,7 +213,7 @@ pub fn convert<'a>(
for raw_ent in &bsp.entities{ for raw_ent in &bsp.entities{
match raw_ent.parse(){ match raw_ent.parse(){
Ok(Entity::Cycler(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION), Ok(Entity::Cycler(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::EnvSprite(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.parse().unwrap_or(WHITE),ATTRIBUTE_DECORATION), Ok(Entity::EnvSprite(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncBreakable(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION), Ok(Entity::FuncBreakable(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncBrush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION), Ok(Entity::FuncBrush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION), Ok(Entity::FuncButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
@ -232,9 +264,27 @@ pub fn convert<'a>(
// physics models // physics models
for brush in &bsp.brushes{ for brush in &bsp.brushes{
if !brush.flags.contains(vbsp::BrushFlags::SOLID){ const RELEVANT:vbsp::BrushFlags=
vbsp::BrushFlags::SOLID
.union(vbsp::BrushFlags::PLAYERCLIP)
.union(vbsp::BrushFlags::WATER)
.union(vbsp::BrushFlags::MOVEABLE)
.union(vbsp::BrushFlags::LADDER);
if !brush.flags.intersects(RELEVANT){
continue; continue;
} }
let is_ladder=brush.flags.contains(vbsp::BrushFlags::LADDER);
let is_water=brush.flags.contains(vbsp::BrushFlags::WATER);
let attributes=match (is_ladder,is_water){
(true,false)=>ATTRIBUTE_LADDER_DEFAULT,
(false,true)=>ATTRIBUTE_WATER_DEFAULT,
(false,false)=>ATTRIBUTE_CONTACT_DEFAULT,
(true,true)=>{
// water ladder? wtf
println!("brush is a water ladder o_o defaulting to ladder");
ATTRIBUTE_LADDER_DEFAULT
}
};
let mesh_result=crate::brush::brush_to_mesh(bsp,brush); let mesh_result=crate::brush::brush_to_mesh(bsp,brush);
match mesh_result{ match mesh_result{
Ok(mesh)=>{ Ok(mesh)=>{
@ -242,7 +292,7 @@ pub fn convert<'a>(
world_meshes.push(mesh); world_meshes.push(mesh);
world_models.push(model::Model{ world_models.push(model::Model{
mesh:mesh_id, mesh:mesh_id,
attributes:ATTRIBUTE_CONTACT_DEFAULT, attributes,
transform:integer::Planar64Affine3::new( transform:integer::Planar64Affine3::new(
integer::mat3::identity(), integer::mat3::identity(),
integer::vec3::ZERO, integer::vec3::ZERO,
@ -268,15 +318,15 @@ pub fn convert<'a>(
color:glam::Vec4::W, color:glam::Vec4::W,
}); });
let first_stage=strafesnet_common::gameplay_modes::Stage::empty(model_id); let first_stage=Stage::empty(model_id);
let main_mode=strafesnet_common::gameplay_modes::Mode::new( let main_mode=Mode::new(
strafesnet_common::gameplay_style::StyleModifiers::source_bhop(), strafesnet_common::gameplay_style::StyleModifiers::source_bhop(),
model_id, model_id,
std::collections::HashMap::new(), std::collections::HashMap::new(),
vec![first_stage], vec![first_stage],
std::collections::HashMap::new(), std::collections::HashMap::new(),
); );
modes_list.push(main_mode); modes_list.push(NormalizedMode::new(main_mode));
} }
PartialMap1{ PartialMap1{
@ -284,17 +334,17 @@ pub fn convert<'a>(
world_meshes, world_meshes,
prop_models, prop_models,
world_models, world_models,
modes:strafesnet_common::gameplay_modes::Modes::new(modes_list), modes:NormalizedModes::new(modes_list),
} }
} }
//partially constructed map types //partially constructed map types
pub struct PartialMap1{ pub struct PartialMap1{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, attributes:Vec<attr::CollisionAttributes>,
prop_models:Vec<model::Model>, prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>, world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>, world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes, modes:NormalizedModes,
} }
impl PartialMap1{ impl PartialMap1{
pub fn add_prop_meshes<'a>( pub fn add_prop_meshes<'a>(
@ -312,12 +362,12 @@ impl PartialMap1{
} }
} }
pub struct PartialMap2{ pub struct PartialMap2{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, attributes:Vec<attr::CollisionAttributes>,
prop_meshes:Vec<(model::MeshId,model::Mesh)>, prop_meshes:Vec<(model::MeshId,model::Mesh)>,
prop_models:Vec<model::Model>, prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>, world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>, world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes, modes:NormalizedModes,
} }
impl PartialMap2{ impl PartialMap2{
pub fn add_render_configs_and_textures( pub fn add_render_configs_and_textures(

@ -7,46 +7,57 @@ pub struct Aabb{
} }
impl Default for Aabb{ impl Default for Aabb{
#[inline]
fn default()->Self{ fn default()->Self{
Self{min:vec3::MAX,max:vec3::MIN} Self{min:vec3::MAX,max:vec3::MIN}
} }
} }
impl Aabb{ impl Aabb{
#[inline]
pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{ pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{
Self{min,max} Self{min,max}
} }
#[inline]
pub const fn max(&self)->Planar64Vec3{ pub const fn max(&self)->Planar64Vec3{
self.max self.max
} }
#[inline]
pub const fn min(&self)->Planar64Vec3{ pub const fn min(&self)->Planar64Vec3{
self.min self.min
} }
#[inline]
pub fn grow(&mut self,point:Planar64Vec3){ pub fn grow(&mut self,point:Planar64Vec3){
self.min=self.min.min(point); self.min=self.min.min(point);
self.max=self.max.max(point); self.max=self.max.max(point);
} }
#[inline]
pub fn join(&mut self,aabb:&Aabb){ pub fn join(&mut self,aabb:&Aabb){
self.min=self.min.min(aabb.min); self.min=self.min.min(aabb.min);
self.max=self.max.max(aabb.max); self.max=self.max.max(aabb.max);
} }
#[inline]
pub fn inflate(&mut self,hs:Planar64Vec3){ pub fn inflate(&mut self,hs:Planar64Vec3){
self.min-=hs; self.min-=hs;
self.max+=hs; self.max+=hs;
} }
#[inline]
pub fn contains(&self,point:Planar64Vec3)->bool{ pub fn contains(&self,point:Planar64Vec3)->bool{
let bvec=self.min.lt(point)&point.lt(self.max); let bvec=self.min.lt(point)&point.lt(self.max);
bvec.all() bvec.all()
} }
#[inline]
pub fn intersects(&self,aabb:&Aabb)->bool{ pub fn intersects(&self,aabb:&Aabb)->bool{
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max); let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max);
bvec.all() bvec.all()
} }
#[inline]
pub fn size(&self)->Planar64Vec3{ pub fn size(&self)->Planar64Vec3{
self.max-self.min self.max-self.min
} }
#[inline]
pub fn center(&self)->Planar64Vec3{ pub fn center(&self)->Planar64Vec3{
self.min+(self.max-self.min)>>1 self.min.map_zip(self.max,|(min,max)|min.midpoint(max))
} }
//probably use floats for area & volume because we don't care about precision //probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{ // pub fn area_weight(&self)->f32{

@ -1,7 +1,6 @@
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use crate::model::ModelId; use crate::model::ModelId;
use crate::gameplay_style; use crate::gameplay_style;
use crate::updatable::Updatable;
#[derive(Clone)] #[derive(Clone)]
pub struct StageElement{ pub struct StageElement{
@ -128,18 +127,6 @@ impl Stage{
self.unordered_checkpoints.contains(&model_id) self.unordered_checkpoints.contains(&model_id)
} }
} }
#[derive(Default)]
pub struct StageUpdate{
//other behaviour models of this stage can have
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
}
impl Updatable<StageUpdate> for Stage{
fn update(&mut self,update:StageUpdate){
self.ordered_checkpoints.extend(update.ordered_checkpoints);
self.unordered_checkpoints.extend(update.unordered_checkpoints);
}
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)] #[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub enum Zone{ pub enum Zone{
@ -211,34 +198,47 @@ impl Mode{
pub fn push_stage(&mut self,stage:Stage){ pub fn push_stage(&mut self,stage:Stage){
self.stages.push(stage) self.stages.push(stage)
} }
pub fn get_stage_mut(&mut self,stage:StageId)->Option<&mut Stage>{ pub fn get_stage_mut(&mut self,StageId(stage_id):StageId)->Option<&mut Stage>{
self.stages.get_mut(stage.0 as usize) self.stages.get_mut(stage_id as usize)
} }
pub fn get_spawn_model_id(&self,stage:StageId)->Option<ModelId>{ pub fn get_spawn_model_id(&self,StageId(stage_id):StageId)->Option<ModelId>{
self.stages.get(stage.0 as usize).map(|s|s.spawn) self.stages.get(stage_id as usize).map(|s|s.spawn)
} }
pub fn get_zone(&self,model_id:ModelId)->Option<&Zone>{ pub fn get_zone(&self,model_id:ModelId)->Option<&Zone>{
self.zones.get(&model_id) self.zones.get(&model_id)
} }
pub fn get_stage(&self,stage_id:StageId)->Option<&Stage>{ pub fn get_stage(&self,StageId(stage_id):StageId)->Option<&Stage>{
self.stages.get(stage_id.0 as usize) self.stages.get(stage_id as usize)
} }
pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{ pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{
self.elements.get(&model_id) self.elements.get(&model_id)
} }
}
#[derive(Clone)]
pub struct NormalizedMode(Mode);
impl NormalizedMode{
pub fn new(mode:Mode)->Self{
Self(mode)
}
pub fn into_inner(self)->Mode{
let Self(mode)=self;
mode
}
//TODO: put this in the SNF //TODO: put this in the SNF
pub fn denormalize_data(&mut self){ pub fn denormalize(self)->Mode{
let NormalizedMode(mut mode)=self;
//expand and index normalized data //expand and index normalized data
self.zones.insert(self.start,Zone::Start); mode.zones.insert(mode.start,Zone::Start);
for (stage_id,stage) in self.stages.iter().enumerate(){ for (stage_id,stage) in mode.stages.iter().enumerate(){
self.elements.insert(stage.spawn,StageElement{ mode.elements.insert(stage.spawn,StageElement{
stage_id:StageId(stage_id as u32), stage_id:StageId(stage_id as u32),
force:false, force:false,
behaviour:StageElementBehaviour::SpawnAt, behaviour:StageElementBehaviour::SpawnAt,
jump_limit:None, jump_limit:None,
}); });
for (_,&model) in &stage.ordered_checkpoints{ for (_,&model) in &stage.ordered_checkpoints{
self.elements.insert(model,StageElement{ mode.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32), stage_id:StageId(stage_id as u32),
force:false, force:false,
behaviour:StageElementBehaviour::Checkpoint, behaviour:StageElementBehaviour::Checkpoint,
@ -246,7 +246,7 @@ impl Mode{
}); });
} }
for &model in &stage.unordered_checkpoints{ for &model in &stage.unordered_checkpoints{
self.elements.insert(model,StageElement{ mode.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32), stage_id:StageId(stage_id as u32),
force:false, force:false,
behaviour:StageElementBehaviour::Checkpoint, behaviour:StageElementBehaviour::Checkpoint,
@ -254,53 +254,13 @@ impl Mode{
}); });
} }
} }
} mode
}
//this would be nice as a macro
#[derive(Default)]
pub struct ModeUpdate{
zones:HashMap<ModelId,Zone>,
stages:HashMap<StageId,StageUpdate>,
//mutually exlusive stage element behaviour
elements:HashMap<ModelId,StageElement>,
}
impl Updatable<ModeUpdate> for Mode{
fn update(&mut self,update:ModeUpdate){
self.zones.extend(update.zones);
for (stage,stage_update) in update.stages{
if let Some(stage)=self.stages.get_mut(stage.0 as usize){
stage.update(stage_update);
}
}
self.elements.extend(update.elements);
}
}
impl ModeUpdate{
pub fn zone(model_id:ModelId,zone:Zone)->Self{
let mut mu=Self::default();
mu.zones.insert(model_id,zone);
mu
}
pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
let mut mu=Self::default();
mu.stages.insert(stage_id,stage_update);
mu
}
pub fn element(model_id:ModelId,element:StageElement)->Self{
let mut mu=Self::default();
mu.elements.insert(model_id,element);
mu
}
pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){
for (_,stage_element) in self.elements.iter_mut(){
stage_element.stage_id=f(stage_element.stage_id);
}
} }
} }
#[derive(Default,Clone)] #[derive(Default,Clone)]
pub struct Modes{ pub struct Modes{
pub modes:Vec<Mode>, modes:Vec<Mode>,
} }
impl Modes{ impl Modes{
pub const fn new(modes:Vec<Mode>)->Self{ pub const fn new(modes:Vec<Mode>)->Self{
@ -314,19 +274,182 @@ impl Modes{
pub fn push_mode(&mut self,mode:Mode){ pub fn push_mode(&mut self,mode:Mode){
self.modes.push(mode) self.modes.push(mode)
} }
pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{ pub fn get_mode(&self,ModeId(mode_id):ModeId)->Option<&Mode>{
self.modes.get(mode.0 as usize) self.modes.get(mode_id as usize)
} }
} }
pub struct ModesUpdate{
modes:HashMap<ModeId,ModeUpdate>, #[derive(Clone)]
pub struct NormalizedModes{
modes:Vec<NormalizedMode>,
} }
impl Updatable<ModesUpdate> for Modes{ impl NormalizedModes{
fn update(&mut self,update:ModesUpdate){ pub fn new(modes:Vec<NormalizedMode>)->Self{
for (mode,mode_update) in update.modes{ Self{modes}
if let Some(mode)=self.modes.get_mut(mode.0 as usize){ }
mode.update(mode_update); pub fn len(&self)->usize{
} self.modes.len()
}
pub fn denormalize(self)->Modes{
Modes{
modes:self.modes.into_iter().map(NormalizedMode::denormalize).collect(),
} }
} }
} }
impl IntoIterator for NormalizedModes{
type Item=<Vec<NormalizedMode> as IntoIterator>::Item;
type IntoIter=<Vec<NormalizedMode> as IntoIterator>::IntoIter;
fn into_iter(self)->Self::IntoIter{
self.modes.into_iter()
}
}
#[derive(Default)]
pub struct StageUpdate{
//other behaviour models of this stage can have
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
}
impl StageUpdate{
fn apply_to(self,stage:&mut Stage){
stage.ordered_checkpoints.extend(self.ordered_checkpoints);
stage.unordered_checkpoints.extend(self.unordered_checkpoints);
}
}
//this would be nice as a macro
#[derive(Default)]
pub struct ModeUpdate{
zones:Option<(ModelId,Zone)>,
stages:Option<(StageId,StageUpdate)>,
//mutually exlusive stage element behaviour
elements:Option<(ModelId,StageElement)>,
}
impl ModeUpdate{
fn apply_to(self,mode:&mut Mode){
mode.zones.extend(self.zones);
if let Some((StageId(stage_id),stage_update))=self.stages{
if let Some(stage)=mode.stages.get_mut(stage_id as usize){
stage_update.apply_to(stage);
}
}
mode.elements.extend(self.elements);
}
}
impl ModeUpdate{
pub fn zone(model_id:ModelId,zone:Zone)->Self{
Self{
zones:Some((model_id,zone)),
stages:None,
elements:None,
}
}
pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
Self{
zones:None,
stages:Some((stage_id,stage_update)),
elements:None,
}
}
pub fn element(model_id:ModelId,element:StageElement)->Self{
Self{
zones:None,
stages:None,
elements:Some((model_id,element)),
}
}
pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){
for (_,stage_element) in self.elements.iter_mut(){
stage_element.stage_id=f(stage_element.stage_id);
}
}
}
struct ModeBuilder{
mode:Mode,
stage_id_map:HashMap<StageId,StageId>,
}
#[derive(Default)]
pub struct ModesBuilder{
modes:HashMap<ModeId,Mode>,
stages:HashMap<ModeId,HashMap<StageId,Stage>>,
mode_updates:Vec<(ModeId,ModeUpdate)>,
stage_updates:Vec<(ModeId,StageId,StageUpdate)>,
}
impl ModesBuilder{
pub fn build_normalized(mut self)->NormalizedModes{
//collect modes and stages into contiguous arrays
let mut unique_modes:Vec<(ModeId,Mode)>
=self.modes.into_iter().collect();
unique_modes.sort_by_key(|&(mode_id,_)|mode_id);
let (mut modes,mode_id_map):(Vec<ModeBuilder>,HashMap<ModeId,ModeId>)
=unique_modes.into_iter().enumerate()
.map(|(final_mode_id,(builder_mode_id,mut mode))|{
(
ModeBuilder{
stage_id_map:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{
let mut unique_stages:Vec<(StageId,Stage)>
=stages.into_iter().collect();
unique_stages.sort_by_key(|&(StageId(stage_id),_)|stage_id);
unique_stages.into_iter().enumerate()
.map(|(final_stage_id,(builder_stage_id,stage))|{
mode.push_stage(stage);
(builder_stage_id,StageId::new(final_stage_id as u32))
}).collect()
}),
mode,
},
(
builder_mode_id,
ModeId::new(final_mode_id as u32)
)
)
}).unzip();
//TODO: failure messages or errors or something
//push stage updates
for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{
if let Some(final_mode_id)=mode_id_map.get(&builder_mode_id){
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
if let Some(&final_stage_id)=mode.stage_id_map.get(&builder_stage_id){
if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){
stage_update.apply_to(stage);
}
}
}
}
}
//push mode updates
for (builder_mode_id,mut mode_update) in self.mode_updates{
if let Some(final_mode_id)=mode_id_map.get(&builder_mode_id){
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
//map stage id on stage elements
mode_update.map_stage_element_ids(|stage_id|
//walk down one stage id at a time until a stage is found
//TODO use better logic like BTreeMap::upper_bound instead of walking
// final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id))
// .value().copied().unwrap_or(StageId::FIRST)
(0..=stage_id.get()).rev().find_map(|builder_stage_id|
//map the stage element to that stage
mode.stage_id_map.get(&StageId::new(builder_stage_id)).copied()
).unwrap_or(StageId::FIRST)
);
mode_update.apply_to(&mut mode.mode);
}
}
}
NormalizedModes::new(modes.into_iter().map(|mode_builder|NormalizedMode(mode_builder.mode)).collect())
}
pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode){
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
}
pub fn insert_stage(&mut self,mode_id:ModeId,stage_id:StageId,stage:Stage){
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
}
pub fn push_mode_update(&mut self,mode_id:ModeId,mode_update:ModeUpdate){
self.mode_updates.push((mode_id,mode_update));
}
// fn push_stage_update(&mut self,mode_id:ModeId,stage_id:StageId,stage_update:StageUpdate){
// self.stage_updates.push((mode_id,stage_id,stage_update));
// }
}

@ -63,7 +63,7 @@ impl JumpImpulse{
velocity:Planar64Vec3, velocity:Planar64Vec3,
jump_dir:Planar64Vec3, jump_dir:Planar64Vec3,
gravity:&Planar64Vec3, gravity:&Planar64Vec3,
mass:Planar64, _mass:Planar64,
)->Planar64Vec3{ )->Planar64Vec3{
match self{ match self{
&JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()), &JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()),
@ -78,7 +78,7 @@ impl JumpImpulse{
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1() velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
}, },
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(), &JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(),
&JumpImpulse::Energy(energy)=>{ &JumpImpulse::Energy(_energy)=>{
//calculate energy //calculate energy
//let e=gravity.dot(velocity); //let e=gravity.dot(velocity);
//add //add
@ -355,7 +355,7 @@ pub struct LadderSettings{
pub dot:Planar64, pub dot:Planar64,
} }
impl LadderSettings{ impl LadderSettings{
pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{ pub const fn accel(&self,_target_diff:Planar64Vec3,_gravity:Planar64Vec3)->Planar64{
//TODO: fallible ladder accel //TODO: fallible ladder accel
self.accelerate.accel self.accelerate.accel
} }
@ -529,12 +529,12 @@ impl StyleModifiers{
pub fn source_bhop()->Self{ pub fn source_bhop()->Self{
Self{ Self{
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown, controls_mask:Controls::all(),
controls_mask_state:Controls::all(), controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:Some(Planar64::raw(150<<28)*100), air_accel_limit:Some(Planar64::raw((150<<28)*100)),
mv:(Planar64::raw(30)*VALVE_SCALE).fix_1(), mv:Planar64::raw(30<<28),
tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(), tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
@ -570,12 +570,12 @@ impl StyleModifiers{
} }
pub fn source_surf()->Self{ pub fn source_surf()->Self{
Self{ Self{
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown, controls_mask:Controls::all(),
controls_mask_state:Controls::all(), controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()), air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()),
mv:(int(30)*VALVE_SCALE).fix_1(), mv:Planar64::raw(30<<28),
tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(), tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{

@ -9,7 +9,6 @@ pub mod timer;
pub mod integer; pub mod integer;
pub mod physics; pub mod physics;
pub mod session; pub mod session;
pub mod updatable;
pub mod instruction; pub mod instruction;
pub mod gameplay_attributes; pub mod gameplay_attributes;
pub mod gameplay_modes; pub mod gameplay_modes;

@ -4,7 +4,7 @@ use crate::gameplay_attributes;
//this is a temporary struct to try to get the code running again //this is a temporary struct to try to get the code running again
//TODO: use snf::map::Region to update the data in physics and graphics instead of this //TODO: use snf::map::Region to update the data in physics and graphics instead of this
pub struct CompleteMap{ pub struct CompleteMap{
pub modes:gameplay_modes::Modes, pub modes:gameplay_modes::NormalizedModes,
pub attributes:Vec<gameplay_attributes::CollisionAttributes>, pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
pub meshes:Vec<model::Mesh>, pub meshes:Vec<model::Mesh>,
pub models:Vec<model::Model>, pub models:Vec<model::Model>,

@ -1,57 +0,0 @@
// This whole thing should be a drive macro
pub trait Updatable<Updater>{
fn update(&mut self,update:Updater);
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
struct InnerId(u32);
#[derive(Clone)]
struct Inner{
id:InnerId,
enabled:bool,
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
struct OuterId(u32);
struct Outer{
id:OuterId,
inners:std::collections::HashMap<InnerId,Inner>,
}
enum Update<I,U>{
Insert(I),
Update(U),
Remove
}
struct InnerUpdate{
//#[updatable(Update)]
enabled:Option<bool>,
}
struct OuterUpdate{
//#[updatable(Insert,Update,Remove)]
inners:std::collections::HashMap<InnerId,Update<Inner,InnerUpdate>>,
//#[updatable(Update)]
//inners:std::collections::HashMap<InnerId,InnerUpdate>,
}
impl Updatable<InnerUpdate> for Inner{
fn update(&mut self,update:InnerUpdate){
if let Some(enabled)=update.enabled{
self.enabled=enabled;
}
}
}
impl Updatable<OuterUpdate> for Outer{
fn update(&mut self,update:OuterUpdate){
for (id,up) in update.inners{
match up{
Update::Insert(new_inner)=>self.inners.insert(id,new_inner),
Update::Update(inner_update)=>self.inners.get_mut(&id).map(|inner|{
let old=inner.clone();
inner.update(inner_update);
old
}),
Update::Remove=>self.inners.remove(&id),
};
}
}
}

@ -14,7 +14,7 @@ wide-mul=[]
zeroes=["dep:arrayvec"] zeroes=["dep:arrayvec"]
[dependencies] [dependencies]
bnum = "0.12.0" bnum = "0.13.0"
arrayvec = { version = "0.7.6", optional = true } arrayvec = { version = "0.7.6", optional = true }
paste = "1.0.15" paste = "1.0.15"
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true } ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }

@ -33,6 +33,14 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
self.bits self.bits
} }
#[inline] #[inline]
pub const fn as_bits(&self)->&BInt<N>{
&self.bits
}
#[inline]
pub const fn as_bits_mut(&mut self)->&mut BInt<N>{
&mut self.bits
}
#[inline]
pub const fn raw_digit(value:i64)->Self{ pub const fn raw_digit(value:i64)->Self{
let mut digits=[0u64;N]; let mut digits=[0u64;N];
digits[0]=value.abs() as u64; digits[0]=value.abs() as u64;
@ -56,6 +64,10 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
pub const fn abs(self)->Self{ pub const fn abs(self)->Self{
Self::from_bits(self.bits.abs()) Self::from_bits(self.bits.abs())
} }
#[inline]
pub const fn midpoint(self,other:Self)->Self{
Self::from_bits(self.bits.midpoint(other.bits))
}
} }
impl<const F:usize> Fixed<1,F>{ impl<const F:usize> Fixed<1,F>{
/// My old code called this function everywhere so let's provide it /// My old code called this function everywhere so let's provide it
@ -788,13 +800,10 @@ macro_rules! impl_not_const_generic{
let wide_self=self.[<fix_ $_2n>](); let wide_self=self.[<fix_ $_2n>]();
//descend down the bits and check if flipping each bit would push the square over the input value //descend down the bits and check if flipping each bit would push the square over the input value
for shift in (0..=max_shift).rev(){ for shift in (0..=max_shift).rev(){
let new_result={ result.as_bits_mut().as_bits_mut().set_bit(shift,true);
let mut bits=result.to_bits().to_bits(); if wide_self<result.[<wide_mul_ $n _ $n>](result){
bits.set_bit(shift,true); // put it back lol
Self::from_bits(BInt::from_bits(bits)) result.as_bits_mut().as_bits_mut().set_bit(shift,false);
};
if new_result.[<wide_mul_ $n _ $n>](new_result)<=wide_self{
result=new_result;
} }
} }
result result

@ -84,13 +84,13 @@ where
fn ingest_faces2_lods3( fn ingest_faces2_lods3(
polygon_groups:&mut Vec<PolygonGroup>, polygon_groups:&mut Vec<PolygonGroup>,
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>, vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
faces:&Vec<rbx_mesh::mesh::Face2>, faces:&[rbx_mesh::mesh::Face2],
lods:&Vec<rbx_mesh::mesh::Lod3> lods:&[rbx_mesh::mesh::Lod3],
){ ){
//faces have to be split into polygon groups based on lod //faces have to be split into polygon groups based on lod
polygon_groups.extend(lods.windows(2).map(|lod_pair| polygon_groups.extend(lods.windows(2).map(|lod_pair|
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|face| PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|rbx_mesh::mesh::Face2(v0,v1,v2)|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]] vec![vertex_id_map[&v0],vertex_id_map[&v1],vertex_id_map[&v2]]
).collect())) ).collect()))
)) ))
} }

@ -4,12 +4,11 @@ use crate::primitives;
use strafesnet_common::aabb::Aabb; use strafesnet_common::aabb::Aabb;
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::model; use strafesnet_common::model;
use strafesnet_common::gameplay_modes; use strafesnet_common::gameplay_modes::{NormalizedModes,Mode,ModeId,ModeUpdate,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone};
use strafesnet_common::gameplay_style; use strafesnet_common::gameplay_style;
use strafesnet_common::gameplay_attributes as attr; use strafesnet_common::gameplay_attributes as attr;
use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3}; use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
use strafesnet_common::model::RenderConfigId; use strafesnet_common::model::RenderConfigId;
use strafesnet_common::updatable::Updatable;
use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,MeshDeferredLoader}; use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,MeshDeferredLoader};
use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
@ -52,93 +51,7 @@ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_we
vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap() vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap()
) )
} }
struct ModeBuilder{
mode:gameplay_modes::Mode,
final_stage_id_from_builder_stage_id:HashMap<gameplay_modes::StageId,gameplay_modes::StageId>,
}
#[derive(Default)]
struct ModesBuilder{
modes:HashMap<gameplay_modes::ModeId,gameplay_modes::Mode>,
stages:HashMap<gameplay_modes::ModeId,HashMap<gameplay_modes::StageId,gameplay_modes::Stage>>,
mode_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::ModeUpdate)>,
stage_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::StageId,gameplay_modes::StageUpdate)>,
}
impl ModesBuilder{
fn build(mut self)->gameplay_modes::Modes{
//collect modes and stages into contiguous arrays
let mut unique_modes:Vec<(gameplay_modes::ModeId,gameplay_modes::Mode)>
=self.modes.into_iter().collect();
unique_modes.sort_by_key(|&(mode_id,_)|mode_id);
let (mut modes,final_mode_id_from_builder_mode_id):(Vec<ModeBuilder>,HashMap<gameplay_modes::ModeId,gameplay_modes::ModeId>)
=unique_modes.into_iter().enumerate()
.map(|(final_mode_id,(builder_mode_id,mut mode))|{
(
ModeBuilder{
final_stage_id_from_builder_stage_id:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{
let mut unique_stages:Vec<(gameplay_modes::StageId,gameplay_modes::Stage)>
=stages.into_iter().collect();
unique_stages.sort_by(|a,b|a.0.cmp(&b.0));
unique_stages.into_iter().enumerate()
.map(|(final_stage_id,(builder_stage_id,stage))|{
mode.push_stage(stage);
(builder_stage_id,gameplay_modes::StageId::new(final_stage_id as u32))
}).collect()
}),
mode,
},
(
builder_mode_id,
gameplay_modes::ModeId::new(final_mode_id as u32)
)
)
}).unzip();
//TODO: failure messages or errors or something
//push stage updates
for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{
if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
if let Some(&final_stage_id)=mode.final_stage_id_from_builder_stage_id.get(&builder_stage_id){
if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){
stage.update(stage_update);
}
}
}
}
}
//push mode updates
for (builder_mode_id,mut mode_update) in self.mode_updates{
if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
//map stage id on stage elements
mode_update.map_stage_element_ids(|stage_id|
//walk down one stage id at a time until a stage is found
//TODO use better logic like BTreeMap::upper_bound instead of walking
// final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id))
// .value().copied().unwrap_or(gameplay_modes::StageId::FIRST)
(0..=stage_id.get()).rev().find_map(|builder_stage_id|
//map the stage element to that stage
mode.final_stage_id_from_builder_stage_id.get(&gameplay_modes::StageId::new(builder_stage_id)).copied()
).unwrap_or(gameplay_modes::StageId::FIRST)
);
mode.mode.update(mode_update);
}
}
}
gameplay_modes::Modes::new(modes.into_iter().map(|mode_builder|mode_builder.mode).collect())
}
fn insert_mode(&mut self,mode_id:gameplay_modes::ModeId,mode:gameplay_modes::Mode){
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
}
fn insert_stage(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage:gameplay_modes::Stage){
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
}
fn push_mode_update(&mut self,mode_id:gameplay_modes::ModeId,mode_update:gameplay_modes::ModeUpdate){
self.mode_updates.push((mode_id,mode_update));
}
// fn push_stage_update(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage_update:gameplay_modes::StageUpdate){
// self.stage_updates.push((mode_id,stage_id,stage_update));
// }
}
fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{
let mut general=attr::GeneralAttributes::default(); let mut general=attr::GeneralAttributes::default();
let mut intersecting=attr::IntersectingAttributes::default(); let mut intersecting=attr::IntersectingAttributes::default();
@ -167,8 +80,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
modes_builder.insert_mode( modes_builder.insert_mode(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::Mode::empty( Mode::empty(
gameplay_style::StyleModifiers::roblox_bhop(), gameplay_style::StyleModifiers::roblox_bhop(),
model_id model_id
) )
@ -178,10 +91,10 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
modes_builder.push_mode_update( modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::ModeUpdate::zone( ModeUpdate::zone(
model_id, model_id,
gameplay_modes::Zone::Finish, Zone::Finish,
), ),
); );
}, },
@ -189,19 +102,19 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
modes_builder.push_mode_update( modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::ModeUpdate::zone( ModeUpdate::zone(
model_id, model_id,
gameplay_modes::Zone::Anticheat, Zone::Anticheat,
), ),
); );
}, },
"Platform"=>{ "Platform"=>{
modes_builder.push_mode_update( modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::ModeUpdate::element( ModeUpdate::element(
model_id, model_id,
gameplay_modes::StageElement::new(gameplay_modes::StageId::FIRST,false,gameplay_modes::StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to StageElement::new(StageId::FIRST,false,StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to
), ),
); );
}, },
@ -213,8 +126,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
modes_builder.insert_mode( modes_builder.insert_mode(
gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()), ModeId::new(captures[2].parse::<u32>().unwrap()),
gameplay_modes::Mode::empty( Mode::empty(
gameplay_style::StyleModifiers::roblox_bhop(), gameplay_style::StyleModifiers::roblox_bhop(),
model_id model_id
) )
@ -231,8 +144,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
}else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$") }else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
.captures(other){ .captures(other){
force_intersecting=true; force_intersecting=true;
let stage_id=gameplay_modes::StageId::new(captures[3].parse::<u32>().unwrap()); let stage_id=StageId::new(captures[3].parse::<u32>().unwrap());
let stage_element=gameplay_modes::StageElement::new( let stage_element=StageElement::new(
//stage_id: //stage_id:
stage_id, stage_id,
//force: //force:
@ -244,26 +157,26 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
match &captures[2]{ match &captures[2]{
"Spawn"=>{ "Spawn"=>{
modes_builder.insert_stage( modes_builder.insert_stage(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
stage_id, stage_id,
gameplay_modes::Stage::empty(model_id), Stage::empty(model_id),
); );
//TODO: let denormalize handle this //TODO: let denormalize handle this
gameplay_modes::StageElementBehaviour::SpawnAt StageElementBehaviour::SpawnAt
}, },
"SpawnAt"=>gameplay_modes::StageElementBehaviour::SpawnAt, "SpawnAt"=>StageElementBehaviour::SpawnAt,
//cancollide false so you don't hit the side //cancollide false so you don't hit the side
//NOT a decoration //NOT a decoration
"Trigger"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Trigger}, "Trigger"=>{force_can_collide=false;StageElementBehaviour::Trigger},
"Teleport"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Teleport}, "Teleport"=>{force_can_collide=false;StageElementBehaviour::Teleport},
"Platform"=>gameplay_modes::StageElementBehaviour::Platform, "Platform"=>StageElementBehaviour::Platform,
_=>panic!("regex1[2] messed up bad"), _=>panic!("regex1[2] messed up bad"),
}, },
None None
); );
modes_builder.push_mode_update( modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::ModeUpdate::element( ModeUpdate::element(
model_id, model_id,
stage_element, stage_element,
), ),
@ -272,14 +185,14 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
.captures(other){ .captures(other){
match &captures[1]{ match &captures[1]{
"Jump"=>modes_builder.push_mode_update( "Jump"=>modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN, ModeId::MAIN,
gameplay_modes::ModeUpdate::element( ModeUpdate::element(
model_id, model_id,
//jump_limit: //jump_limit:
gameplay_modes::StageElement::new( StageElement::new(
gameplay_modes::StageId::FIRST, StageId::FIRST,
false, false,
gameplay_modes::StageElementBehaviour::Check, StageElementBehaviour::Check,
Some(captures[2].parse::<u8>().unwrap()) Some(captures[2].parse::<u8>().unwrap())
) )
), ),
@ -296,13 +209,13 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
modes_builder.push_mode_update( modes_builder.push_mode_update(
gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()), ModeId::new(captures[2].parse::<u32>().unwrap()),
gameplay_modes::ModeUpdate::zone( ModeUpdate::zone(
model_id, model_id,
//zone: //zone:
match &captures[1]{ match &captures[1]{
"Finish"=>gameplay_modes::Zone::Finish, "Finish"=>Zone::Finish,
"Anticheat"=>gameplay_modes::Zone::Anticheat, "Anticheat"=>Zone::Anticheat,
_=>panic!("regex3[1] messed up bad"), _=>panic!("regex3[1] messed up bad"),
}, },
), ),
@ -312,9 +225,9 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
// .captures(other){ // .captures(other){
// match &captures[1]{ // match &captures[1]{
// "OrderedCheckpoint"=>modes_builder.push_stage_update( // "OrderedCheckpoint"=>modes_builder.push_stage_update(
// gameplay_modes::ModeId::MAIN, // ModeId::MAIN,
// gameplay_modes::StageId::new(0), // StageId::new(0),
// gameplay_modes::StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()), // StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()),
// ), // ),
// _=>panic!("regex3[1] messed up bad"), // _=>panic!("regex3[1] messed up bad"),
// } // }
@ -1009,7 +922,7 @@ impl PartialMap1<'_>{
PartialMap2{ PartialMap2{
meshes:self.primitive_meshes, meshes:self.primitive_meshes,
models, models,
modes:modes_builder.build(), modes:modes_builder.build_normalized(),
attributes:unique_attributes, attributes:unique_attributes,
} }
} }
@ -1018,7 +931,7 @@ impl PartialMap1<'_>{
pub struct PartialMap2{ pub struct PartialMap2{
meshes:Vec<model::Mesh>, meshes:Vec<model::Mesh>,
models:Vec<model::Model>, models:Vec<model::Model>,
modes:gameplay_modes::Modes, modes:NormalizedModes,
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
} }
impl PartialMap2{ impl PartialMap2{

@ -138,7 +138,7 @@ struct MapHeader{
//#[br(count=num_resources_external)] //#[br(count=num_resources_external)]
//external_resources:Vec<ResourceExternalHeader>, //external_resources:Vec<ResourceExternalHeader>,
#[br(count=num_modes)] #[br(count=num_modes)]
modes:Vec<newtypes::gameplay_modes::Mode>, modes:Vec<newtypes::gameplay_modes::NormalizedMode>,
#[br(count=num_attributes)] #[br(count=num_attributes)]
attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>, attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
#[br(count=num_render_configs)] #[br(count=num_render_configs)]
@ -181,7 +181,7 @@ fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)
pub struct StreamableMap<R:BinReaderExt>{ pub struct StreamableMap<R:BinReaderExt>{
file:crate::file::File<R>, file:crate::file::File<R>,
//this includes every platform... move the unconstrained datas to their appropriate data block? //this includes every platform... move the unconstrained datas to their appropriate data block?
modes:gameplay_modes::Modes, modes:gameplay_modes::NormalizedModes,
//this is every possible attribute... need some sort of streaming system //this is every possible attribute... need some sort of streaming system
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
//this is every possible render configuration... shaders and such... need streaming //this is every possible render configuration... shaders and such... need streaming
@ -223,7 +223,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
} }
Ok(Self{ Ok(Self{
file, file,
modes:strafesnet_common::gameplay_modes::Modes::new(modes), modes:strafesnet_common::gameplay_modes::NormalizedModes::new(modes),
attributes, attributes,
render_configs, render_configs,
bvh:strafesnet_common::bvh::generate_bvh(bvh), bvh:strafesnet_common::bvh::generate_bvh(bvh),
@ -430,13 +430,13 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
num_spacial_blocks:spacial_blocks.len() as u32, num_spacial_blocks:spacial_blocks.len() as u32,
num_resource_blocks:resource_blocks.len() as u32, num_resource_blocks:resource_blocks.len() as u32,
//num_resources_external:0, //num_resources_external:0,
num_modes:map.modes.modes.len() as u32, num_modes:map.modes.len() as u32,
num_attributes:map.attributes.len() as u32, num_attributes:map.attributes.len() as u32,
num_render_configs:map.render_configs.len() as u32, num_render_configs:map.render_configs.len() as u32,
spacial_blocks, spacial_blocks,
resource_blocks, resource_blocks,
//external_resources:Vec::new(), //external_resources:Vec::new(),
modes:map.modes.modes.into_iter().map(Into::into).collect(), modes:map.modes.into_iter().map(Into::into).collect(),
attributes:map.attributes.into_iter().map(Into::into).collect(), attributes:map.attributes.into_iter().map(Into::into).collect(),
render_configs:map.render_configs.into_iter().map(Into::into).collect(), render_configs:map.render_configs.into_iter().map(Into::into).collect(),
}; };

@ -157,7 +157,7 @@ pub struct ModeHeader{
} }
#[binrw::binrw] #[binrw::binrw]
#[brw(little)] #[brw(little)]
pub struct Mode{ pub struct NormalizedMode{
pub header:ModeHeader, pub header:ModeHeader,
pub style:super::gameplay_style::StyleModifiers, pub style:super::gameplay_style::StyleModifiers,
pub start:u32, pub start:u32,
@ -179,10 +179,10 @@ impl std::fmt::Display for ModeError{
} }
} }
impl std::error::Error for ModeError{} impl std::error::Error for ModeError{}
impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{ impl TryInto<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
type Error=ModeError; type Error=ModeError;
fn try_into(self)->Result<strafesnet_common::gameplay_modes::Mode,Self::Error>{ fn try_into(self)->Result<strafesnet_common::gameplay_modes::NormalizedMode,Self::Error>{
Ok(strafesnet_common::gameplay_modes::Mode::new( Ok(strafesnet_common::gameplay_modes::NormalizedMode::new(strafesnet_common::gameplay_modes::Mode::new(
self.style.try_into().map_err(ModeError::StyleModifier)?, self.style.try_into().map_err(ModeError::StyleModifier)?,
strafesnet_common::model::ModelId::new(self.start), strafesnet_common::model::ModelId::new(self.start),
self.zones.into_iter().map(|(model_id,zone)| self.zones.into_iter().map(|(model_id,zone)|
@ -192,12 +192,12 @@ impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
self.elements.into_iter().map(|(model_id,stage_element)| self.elements.into_iter().map(|(model_id,stage_element)|
Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?)) Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?))
).collect::<Result<_,_>>().map_err(ModeError::StageElement)?, ).collect::<Result<_,_>>().map_err(ModeError::StageElement)?,
)) )))
} }
} }
impl From<strafesnet_common::gameplay_modes::Mode> for Mode{ impl From<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
fn from(value:strafesnet_common::gameplay_modes::Mode)->Self{ fn from(value:strafesnet_common::gameplay_modes::NormalizedMode)->Self{
let (style,start,zones,stages,elements)=value.into_inner(); let (style,start,zones,stages,elements)=value.into_inner().into_inner();
Self{ Self{
header:ModeHeader{ header:ModeHeader{
zones:zones.len() as u32, zones:zones.len() as u32,

@ -25,11 +25,11 @@ strafesnet_rbx_loader = { version = "0.6.0", path = "../lib/rbx_loader", registr
strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" } strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" }
thiserror = "2.0.11" thiserror = "2.0.11"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] } tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
vbsp = { version = "0.7.0-codegen5", registry = "strafesnet", default-features = false } vbsp = "0.8.0"
vbsp-entities = { version = "0.1.0", registry = "strafesnet", default-features = false, features = ["css"]} vbsp-entities-css = "0.6.0"
vmdl = "0.2.0" vmdl = "0.2.0"
vmt-parser = "0.2.0" vmt-parser = "0.2.0"
vpk = "0.2.0" vpk = "0.3.0"
vtf = "0.3.0" vtf = "0.3.0"
#[profile.release] #[profile.release]

@ -6,7 +6,7 @@ use futures::StreamExt;
use strafesnet_bsp_loader::loader::BspFinder; use strafesnet_bsp_loader::loader::BspFinder;
use strafesnet_deferred_loader::loader::Loader; use strafesnet_deferred_loader::loader::Loader;
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader}; use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
use vbsp_entities::css::Entity; use vbsp_entities_css::Entity;
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum Commands{ pub enum Commands{

@ -77,5 +77,8 @@ pub fn new<'a>(
run_session_instruction!(ins.time,SessionInstruction::LoadReplay(bot)); run_session_instruction!(ins.time,SessionInstruction::LoadReplay(bot));
} }
} }
//whatever just do it
session.debug_raycast_print_model_id_if_changed(ins.time);
}) })
} }