Compare commits

...

13 Commits

Author SHA1 Message Date
1a328c2a0f fix paths 2025-01-22 05:30:34 -08:00
de5b4e4214 move required limits function to graphics 2025-01-22 05:30:23 -08:00
e8e81eede7 add licenses to modules 2025-01-22 05:23:35 -08:00
f7bc750f02 use session and settings in graphics 2025-01-22 05:23:21 -08:00
49d8a3863c create session+settings modules 2025-01-22 05:23:07 -08:00
febb31e0ac fix paths 2025-01-22 04:55:33 -08:00
1ee0a3cc63 manage module dependencies 2025-01-22 04:55:33 -08:00
6a6b72d0e2 create graphic module 2025-01-22 04:18:35 -08:00
db7be2987f create physics module 2025-01-22 04:18:23 -08:00
267c91d005 replace .map_or(None, with .and_then( 2025-01-21 12:59:34 -08:00
32b361b122 use Range to express time range 2025-01-21 12:50:00 -08:00
f5c3209c7c readme: try it out 2025-01-21 12:20:08 -08:00
2eb74f2788 Revert "bodge surfs"
This reverts commit 65b49d27261754903b2e39194c80ac096afc1da2.
2025-01-21 12:20:08 -08:00
32 changed files with 214 additions and 78 deletions

55
Cargo.lock generated

@ -2253,19 +2253,17 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
name = "strafe-client" name = "strafe-client"
version = "0.11.0" version = "0.11.0"
dependencies = [ dependencies = [
"arrayvec",
"bytemuck",
"configparser",
"ddsfile",
"glam", "glam",
"id",
"parking_lot", "parking_lot",
"pollster", "pollster",
"replace_with",
"strafesnet_bsp_loader", "strafesnet_bsp_loader",
"strafesnet_common", "strafesnet_common",
"strafesnet_deferred_loader", "strafesnet_deferred_loader",
"strafesnet_graphics",
"strafesnet_physics",
"strafesnet_rbx_loader", "strafesnet_rbx_loader",
"strafesnet_session",
"strafesnet_settings",
"strafesnet_snf", "strafesnet_snf",
"wgpu", "wgpu",
"winit", "winit",
@ -2303,6 +2301,30 @@ dependencies = [
"vbsp", "vbsp",
] ]
[[package]]
name = "strafesnet_graphics"
version = "0.1.0"
dependencies = [
"bytemuck",
"ddsfile",
"glam",
"id",
"strafesnet_common",
"strafesnet_session",
"strafesnet_settings",
"wgpu",
]
[[package]]
name = "strafesnet_physics"
version = "0.1.0"
dependencies = [
"arrayvec",
"glam",
"id",
"strafesnet_common",
]
[[package]] [[package]]
name = "strafesnet_rbx_loader" name = "strafesnet_rbx_loader"
version = "0.5.2" version = "0.5.2"
@ -2319,6 +2341,27 @@ dependencies = [
"strafesnet_common", "strafesnet_common",
] ]
[[package]]
name = "strafesnet_session"
version = "0.1.0"
dependencies = [
"glam",
"replace_with",
"strafesnet_common",
"strafesnet_physics",
"strafesnet_settings",
"strafesnet_snf",
]
[[package]]
name = "strafesnet_settings"
version = "0.1.0"
dependencies = [
"configparser",
"glam",
"strafesnet_common",
]
[[package]] [[package]]
name = "strafesnet_snf" name = "strafesnet_snf"
version = "0.2.0" version = "0.2.0"

@ -1,5 +1,9 @@
[workspace] [workspace]
members = [ members = [
"engine/graphics",
"engine/physics",
"engine/session",
"engine/settings",
"lib/bsp_loader", "lib/bsp_loader",
"lib/common", "lib/common",
"lib/deferred_loader", "lib/deferred_loader",

@ -3,6 +3,9 @@
# Strafe Project # Strafe Project
Monorepo for working on projects related to strafe client. Monorepo for working on projects related to strafe client.
## Try it out
See [releases](https://git.itzana.me/StrafesNET/strafe-project/releases) for downloads.
## How to build and run ## How to build and run
1. Have rust and git installed 1. Have rust and git installed
2. `git clone https://git.itzana.me/StrafesNET/strafe-project` 2. `git clone https://git.itzana.me/StrafesNET/strafe-project`

@ -0,0 +1,14 @@
[package]
name = "strafesnet_graphics"
version = "0.1.0"
edition = "2021"
[dependencies]
bytemuck = { version = "1.13.1", features = ["derive"] }
ddsfile = "0.5.1"
glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
strafesnet_session = { path = "../session", registry = "strafesnet" }
strafesnet_settings = { path = "../settings", registry = "strafesnet" }
wgpu = "24.0.0"

8
engine/graphics/LICENSE Normal file

@ -0,0 +1,8 @@
/*******************************************************
* Copyright (C) 2023-2024 Rhys Lloyd <krakow20@gmail.com>
*
* This file is part of the StrafesNET bhop/surf client.
*
* StrafesNET can not be copied and/or distributed
* without the express permission of Rhys Lloyd
*******************************************************/

@ -1,9 +1,15 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_settings::settings;
use strafesnet_session::session;
use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId}; use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId};
use wgpu::{util::DeviceExt,AstcBlock,AstcChannel}; use wgpu::{util::DeviceExt,AstcBlock,AstcChannel};
use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex}; use crate::model::{self as model_graphics,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex};
pub fn required_limits()->wgpu::Limits{
wgpu::Limits::default()
}
struct Indices{ struct Indices{
count:u32, count:u32,
@ -136,7 +142,7 @@ impl GraphicsState{
pub fn clear(&mut self){ pub fn clear(&mut self){
self.models.clear(); self.models.clear();
} }
pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ pub fn load_user_settings(&mut self,user_settings:&settings::UserSettings){
self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2();
} }
pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){ pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){
@ -448,7 +454,7 @@ impl GraphicsState{
//.into_iter() the modeldata vec so entities can be /moved/ to models.entities //.into_iter() the modeldata vec so entities can be /moved/ to models.entities
let mut model_count=0; let mut model_count=0;
let mut instance_count=0; let mut instance_count=0;
let uniform_buffer_binding_size=crate::setup::required_limits().max_uniform_buffer_binding_size as usize; let uniform_buffer_binding_size=required_limits().max_uniform_buffer_binding_size as usize;
let chunk_size=uniform_buffer_binding_size/MODEL_BUFFER_SIZE_BYTES; let chunk_size=uniform_buffer_binding_size/MODEL_BUFFER_SIZE_BYTES;
self.models.reserve(models.len()); self.models.reserve(models.len());
for model in models.into_iter(){ for model in models.into_iter(){
@ -608,7 +614,7 @@ impl GraphicsState{
// Create the render pipeline // Create the render pipeline
let shader=device.create_shader_module(wgpu::ShaderModuleDescriptor{ let shader=device.create_shader_module(wgpu::ShaderModuleDescriptor{
label:None, label:None,
source:wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), source:wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../../../strafe-client/src/shader.wgsl"))),
}); });
//load textures //load textures
@ -636,10 +642,10 @@ impl GraphicsState{
wgpu::TextureFormat::Astc{ wgpu::TextureFormat::Astc{
block:AstcBlock::B4x4, block:AstcBlock::B4x4,
channel:AstcChannel::UnormSrgb, channel:AstcChannel::UnormSrgb,
}=>&include_bytes!("../images/astc.dds")[..], }=>&include_bytes!("../../../strafe-client/images/astc.dds")[..],
wgpu::TextureFormat::Etc2Rgb8UnormSrgb=>&include_bytes!("../images/etc2.dds")[..], wgpu::TextureFormat::Etc2Rgb8UnormSrgb=>&include_bytes!("../../../strafe-client/images/etc2.dds")[..],
wgpu::TextureFormat::Bc1RgbaUnormSrgb=>&include_bytes!("../images/bc1.dds")[..], wgpu::TextureFormat::Bc1RgbaUnormSrgb=>&include_bytes!("../../../strafe-client/images/bc1.dds")[..],
wgpu::TextureFormat::Bgra8UnormSrgb=>&include_bytes!("../images/bgra.dds")[..], wgpu::TextureFormat::Bgra8UnormSrgb=>&include_bytes!("../../../strafe-client/images/bgra.dds")[..],
_=>unreachable!(), _=>unreachable!(),
}; };
@ -682,7 +688,7 @@ impl GraphicsState{
//squid //squid
let squid_texture_view={ let squid_texture_view={
let bytes=include_bytes!("../images/squid.dds"); let bytes=include_bytes!("../../../strafe-client/images/squid.dds");
let image=ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap(); let image=ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap();
@ -864,7 +870,7 @@ impl GraphicsState{
&mut self, &mut self,
device:&wgpu::Device, device:&wgpu::Device,
config:&wgpu::SurfaceConfiguration, config:&wgpu::SurfaceConfiguration,
user_settings:&crate::settings::UserSettings, user_settings:&settings::UserSettings,
){ ){
self.depth_view=Self::create_depth_texture(config,device); self.depth_view=Self::create_depth_texture(config,device);
self.camera.screen_size=glam::uvec2(config.width,config.height); self.camera.screen_size=glam::uvec2(config.width,config.height);
@ -875,7 +881,7 @@ impl GraphicsState{
view:&wgpu::TextureView, view:&wgpu::TextureView,
device:&wgpu::Device, device:&wgpu::Device,
queue:&wgpu::Queue, queue:&wgpu::Queue,
frame_state:crate::session::FrameState, frame_state:session::FrameState,
){ ){
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input //TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input

@ -0,0 +1,2 @@
pub mod model;
pub mod graphics;

10
engine/physics/Cargo.toml Normal file

@ -0,0 +1,10 @@
[package]
name = "strafesnet_physics"
version = "0.1.0"
edition = "2021"
[dependencies]
arrayvec = "0.7.6"
glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }

8
engine/physics/LICENSE Normal file

@ -0,0 +1,8 @@
/*******************************************************
* Copyright (C) 2023-2024 Rhys Lloyd <krakow20@gmail.com>
*
* This file is part of the StrafesNET bhop/surf client.
*
* StrafesNET can not be copied and/or distributed
* without the express permission of Rhys Lloyd
*******************************************************/

@ -82,7 +82,7 @@ impl<T> Body<T>
// a*dt + v // a*dt + v
self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity
} }
pub fn advance_time_ratio_dt(&mut self,dt:crate::model_physics::GigaTime){ pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){
self.position=self.extrapolated_position_ratio_dt(dt); self.position=self.extrapolated_position_ratio_dt(dt);
self.velocity=self.extrapolated_velocity_ratio_dt(dt); self.velocity=self.extrapolated_velocity_ratio_dt(dt);
self.time+=dt.into(); self.time+=dt.into();

@ -1,4 +1,4 @@
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge}; use crate::model::{GigaTime,FEV,MeshQuery,DirectedEdge};
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3}; use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
use crate::physics::{Time,Body}; use crate::physics::{Time,Body};

@ -0,0 +1,6 @@
mod body;
mod push_solve;
mod face_crawler;
mod model;
pub mod physics;

@ -1,5 +1,6 @@
use std::borrow::{Borrow,Cow}; use std::borrow::{Borrow,Cow};
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use core::ops::Range;
use strafesnet_common::integer::vec3::Vector3; use strafesnet_common::integer::vec3::Vector3;
use strafesnet_common::model::{self,MeshId,PolygonIter}; use strafesnet_common::model::{self,MeshId,PolygonIter};
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio}; use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
@ -718,8 +719,8 @@ impl MinkowskiMesh<'_>{
// //
// Most of the calculation time is just calculating the starting point // Most of the calculation time is just calculating the starting point
// for the "actual" crawling algorithm below (predict_collision_{in|out}). // for the "actual" crawling algorithm below (predict_collision_{in|out}).
fn closest_fev_not_inside(&self,mut infinity_body:Body,start_time:Time,)->Option<FEV<MinkowskiMesh>>{ fn closest_fev_not_inside(&self,mut infinity_body:Body,start_time:Time)->Option<FEV<MinkowskiMesh>>{
infinity_body.infinity_dir().map_or(None,|dir|{ infinity_body.infinity_dir().and_then(|dir|{
let infinity_fev=self.infinity_fev(-dir,infinity_body.position); let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
//a line is simpler to solve than a parabola //a line is simpler to solve than a parabola
infinity_body.velocity=dir; infinity_body.velocity=dir;
@ -729,23 +730,23 @@ impl MinkowskiMesh<'_>{
infinity_fev.crawl(self,&infinity_body,Time::MIN/4,start_time).miss() infinity_fev.crawl(self,&infinity_body,Time::MIN/4,start_time).miss()
}) })
} }
pub fn predict_collision_in(&self,relative_body:&Body,start_time:Time,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_in(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
self.closest_fev_not_inside(relative_body.clone(),start_time).map_or(None,|fev|{ self.closest_fev_not_inside(relative_body.clone(),start_time).and_then(|fev|{
//continue forwards along the body parabola //continue forwards along the body parabola
fev.crawl(self,relative_body,start_time,time_limit).hit() fev.crawl(self,relative_body,start_time,time_limit).hit()
}) })
} }
pub fn predict_collision_out(&self,relative_body:&Body,start_time:Time,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
//create an extrapolated body at time_limit //create an extrapolated body at time_limit
let infinity_body=-relative_body.clone(); let infinity_body=-relative_body.clone();
self.closest_fev_not_inside(infinity_body,-time_limit).map_or(None,|fev|{ self.closest_fev_not_inside(infinity_body,-time_limit).and_then(|fev|{
//continue backwards along the body parabola //continue backwards along the body parabola
fev.crawl(self,&infinity_body,-time_limit,-start_time).hit() fev.crawl(self,&infinity_body,-time_limit,-start_time).hit()
//no need to test -time<time_limit because of the first step //no need to test -time<time_limit because of the first step
.map(|(face,time)|(face,-time)) .map(|(face,time)|(face,-time))
}) })
} }
pub fn predict_collision_face_out(&self,relative_body:&Body,start_time:Time,time_limit:Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{ pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
//no algorithm needed, there is only one state and two cases (Edge,None) //no algorithm needed, there is only one state and two cases (Edge,None)
//determine when it passes an edge ("sliding off" case) //determine when it passes an edge ("sliding off" case)
let start_time={ let start_time={

@ -1,5 +1,5 @@
use std::collections::{HashMap,HashSet}; use std::collections::{HashMap,HashSet};
use crate::model_physics::{self,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;
use strafesnet_common::run; use strafesnet_common::run;
@ -28,9 +28,9 @@ use gameplay::ModeState;
// or the only bots which fail are ones exploiting a surgically patched bug. // or the only bots which fail are ones exploiting a surgically patched bug.
#[derive(Clone,Copy,Hash,Debug,id::Id,Eq,PartialEq,Ord,PartialOrd)] #[derive(Clone,Copy,Hash,Debug,id::Id,Eq,PartialEq,Ord,PartialOrd)]
pub struct PhysicsVersion(u32); pub struct PhysicsVersion(u32);
pub const VERSION:PhysicsVersion=PhysicsVersion(1); pub const VERSION:PhysicsVersion=PhysicsVersion(2);
const LATEST_COMPATIBLE_VERSION:[u32;1+VERSION.0 as usize]=const{ const LATEST_COMPATIBLE_VERSION:[u32;1+VERSION.0 as usize]=const{
let compat=[0,1]; let compat=[0,1,2];
let mut input_version=0; let mut input_version=0;
while input_version<compat.len(){ while input_version<compat.len(){
@ -836,7 +836,7 @@ impl TouchingState{
//detect face slide off //detect face slide off
let model_mesh=models.contact_mesh(contact); let model_mesh=models.contact_mesh(contact);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time,collector.time(),contact.face_id).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
@ -850,7 +850,7 @@ impl TouchingState{
//detect model collision in reverse //detect model collision in reverse
let model_mesh=models.intersect_mesh(intersect); let model_mesh=models.intersect_mesh(intersect);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_out(&relative_body,start_time,collector.time()).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_out(&relative_body,start_time..collector.time()).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
@ -1185,9 +1185,9 @@ impl PhysicsData{
//no checks are needed because of the time limits. //no checks are needed because of the time limits.
let model_mesh=data.models.mesh(convex_mesh_id); let model_mesh=data.models.mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_in(relative_body,state.time,collector.time()) collector.collect(minkowski.predict_collision_in(relative_body,state.time..collector.time())
//temp (?) code to avoid collision loops //temp (?) code to avoid collision loops
.map_or(None,|(face,dt)|{ .and_then(|(face,dt)|{
// this must be rounded to avoid the infinite loop when hitting the start zone // this must be rounded to avoid the infinite loop when hitting the start zone
let time=relative_body.time+dt.into(); let time=relative_body.time+dt.into();
(state.time<time).then_some((time,face,dt)) (state.time<time).then_some((time,face,dt))
@ -1294,7 +1294,7 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
let mut culled=false; let mut culled=false;
touching.contacts.retain(|contact|{ touching.contacts.retain(|contact|{
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,contact);
let r=(n.dot(v)>>52).is_positive(); let r=n.dot(v).is_positive();
if r{ if r{
culled=true; culled=true;
} }
@ -1922,7 +1922,7 @@ mod test{
let hitbox_mesh=h1.transformed_mesh(); let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh(); let platform_mesh=h0.transformed_mesh();
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO,Time::from_secs(10)); let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO..Time::from_secs(10));
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision"); assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
} }
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){ fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
@ -1940,7 +1940,7 @@ mod test{
let hitbox_mesh=h1.transformed_mesh(); let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh(); let platform_mesh=h0.transformed_mesh();
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO,Time::from_secs(10)); let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO..Time::from_secs(10));
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision"); assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
} }
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){ fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){

12
engine/session/Cargo.toml Normal file

@ -0,0 +1,12 @@
[package]
name = "strafesnet_session"
version = "0.1.0"
edition = "2021"
[dependencies]
glam = "0.29.0"
replace_with = "0.1.7"
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
strafesnet_physics = { path = "../physics", registry = "strafesnet" }
strafesnet_settings = { path = "../settings", registry = "strafesnet" }
strafesnet_snf = { path = "../../lib/snf", registry = "strafesnet" }

8
engine/session/LICENSE Normal file

@ -0,0 +1,8 @@
/*******************************************************
* Copyright (C) 2023-2024 Rhys Lloyd <krakow20@gmail.com>
*
* This file is part of the StrafesNET bhop/surf client.
*
* StrafesNET can not be copied and/or distributed
* without the express permission of Rhys Lloyd
*******************************************************/

@ -0,0 +1,2 @@
mod mouse_interpolator;
pub mod session;

@ -14,8 +14,8 @@ use strafesnet_common::timer::{Scaled,Timer};
use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime}; use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime};
use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction}; use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction};
use crate::physics::{PhysicsContext,PhysicsData}; use strafesnet_physics::physics::{self,PhysicsContext,PhysicsData};
use crate::settings::UserSettings; use strafesnet_settings::settings::UserSettings;
pub enum Instruction<'a>{ pub enum Instruction<'a>{
Input(SessionInputInstruction), Input(SessionInputInstruction),
@ -57,19 +57,19 @@ pub enum SessionPlaybackInstruction{
} }
pub struct FrameState{ pub struct FrameState{
pub body:crate::physics::Body, pub body:physics::Body,
pub camera:crate::physics::PhysicsCamera, pub camera:physics::PhysicsCamera,
pub time:PhysicsTime, pub time:PhysicsTime,
} }
pub struct Simulation{ pub struct Simulation{
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>, timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsState, physics:physics::PhysicsState,
} }
impl Simulation{ impl Simulation{
pub const fn new( pub const fn new(
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>, timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsState, physics:physics::PhysicsState,
)->Self{ )->Self{
Self{ Self{
timer, timer,
@ -153,7 +153,7 @@ pub struct Session{
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator, mouse_interpolator:crate::mouse_interpolator::MouseInterpolator,
view_state:ViewState, view_state:ViewState,
//gui:GuiState //gui:GuiState
geometry_shared:crate::physics::PhysicsData, geometry_shared:physics::PhysicsData,
simulation:Simulation, simulation:Simulation,
// below fields not included in lite session // below fields not included in lite session
recording:Recording, recording:Recording,
@ -299,7 +299,7 @@ impl InstructionConsumer<Instruction<'_>> for Session{
std::thread::spawn(move ||{ std::thread::spawn(move ||{
std::fs::create_dir_all("replays").unwrap(); std::fs::create_dir_all("replays").unwrap();
let file=std::fs::File::create(file_name).unwrap(); let file=std::fs::File::create(file_name).unwrap();
strafesnet_snf::bot::write_bot(std::io::BufWriter::new(file),crate::physics::VERSION.get(),replay.recording.instructions).unwrap(); strafesnet_snf::bot::write_bot(std::io::BufWriter::new(file),physics::VERSION.get(),replay.recording.instructions).unwrap();
println!("Finished writing bot file!"); println!("Finished writing bot file!");
}); });
}, },

@ -0,0 +1,9 @@
[package]
name = "strafesnet_settings"
version = "0.1.0"
edition = "2021"
[dependencies]
configparser = "3.0.2"
glam = "0.29.0"
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }

8
engine/settings/LICENSE Normal file

@ -0,0 +1,8 @@
/*******************************************************
* Copyright (C) 2023-2024 Rhys Lloyd <krakow20@gmail.com>
*
* This file is part of the StrafesNET bhop/surf client.
*
* StrafesNET can not be copied and/or distributed
* without the express permission of Rhys Lloyd
*******************************************************/

@ -0,0 +1 @@
pub mod settings;

@ -15,19 +15,17 @@ source = ["dep:strafesnet_deferred_loader", "dep:strafesnet_bsp_loader"]
roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"] roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"]
[dependencies] [dependencies]
arrayvec = "0.7.6"
bytemuck = { version = "1.13.1", features = ["derive"] }
configparser = "3.0.2"
ddsfile = "0.5.1"
glam = "0.29.0" glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" }
parking_lot = "0.12.1" parking_lot = "0.12.1"
pollster = "0.4.0" pollster = "0.4.0"
replace_with = "0.1.7"
strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true } strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true }
strafesnet_common = { path = "../lib/common", registry = "strafesnet" } strafesnet_common = { path = "../lib/common", registry = "strafesnet" }
strafesnet_deferred_loader = { path = "../lib/deferred_loader", features = ["legacy"], registry = "strafesnet", optional = true } strafesnet_deferred_loader = { path = "../lib/deferred_loader", features = ["legacy"], registry = "strafesnet", optional = true }
strafesnet_graphics = { path = "../engine/graphics", registry = "strafesnet" }
strafesnet_physics = { path = "../engine/physics", registry = "strafesnet" }
strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet", optional = true } strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet", optional = true }
strafesnet_session = { path = "../engine/session", registry = "strafesnet" }
strafesnet_settings = { path = "../engine/settings", registry = "strafesnet" }
strafesnet_snf = { path = "../lib/snf", registry = "strafesnet", optional = true } strafesnet_snf = { path = "../lib/snf", registry = "strafesnet", optional = true }
wgpu = "24.0.0" wgpu = "24.0.0"
winit = "0.30.7" winit = "0.30.7"

@ -1,7 +1,11 @@
use strafesnet_graphics::graphics;
use strafesnet_session::session;
use strafesnet_settings::settings;
pub enum Instruction{ pub enum Instruction{
Render(crate::session::FrameState), Render(session::FrameState),
//UpdateModel(crate::graphics::GraphicsModelUpdate), //UpdateModel(graphics::GraphicsModelUpdate),
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings), Resize(winit::dpi::PhysicalSize<u32>,settings::UserSettings),
ChangeMap(strafesnet_common::map::CompleteMap), ChangeMap(strafesnet_common::map::CompleteMap),
} }
@ -15,7 +19,7 @@ WorkerDescription{
//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order //up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order
pub fn new( pub fn new(
mut graphics:crate::graphics::GraphicsState, mut graphics:graphics::GraphicsState,
mut config:wgpu::SurfaceConfiguration, mut config:wgpu::SurfaceConfiguration,
surface:wgpu::Surface, surface:wgpu::Surface,
device:wgpu::Device, device:wgpu::Device,

@ -1,20 +1,10 @@
mod body;
mod file; mod file;
mod setup; mod setup;
mod window; mod window;
mod worker; mod worker;
mod physics;
mod session;
mod graphics;
mod settings;
mod push_solve;
mod face_crawler;
mod compat_worker; mod compat_worker;
mod model_physics;
mod model_graphics;
mod physics_worker; mod physics_worker;
mod graphics_worker; mod graphics_worker;
mod mouse_interpolator;
const TITLE:&'static str=concat!("Strafe Client v",env!("CARGO_PKG_VERSION")); const TITLE:&'static str=concat!("Strafe Client v",env!("CARGO_PKG_VERSION"));

@ -1,6 +1,7 @@
use crate::graphics_worker::Instruction as GraphicsInstruction; use crate::graphics_worker::Instruction as GraphicsInstruction;
use crate::session::{ use strafesnet_settings::settings;
Session,Simulation,SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction, use strafesnet_session::session::{
Session,Simulation,SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction,ImplicitModeInstruction,
Instruction as SessionInstruction, Instruction as SessionInstruction,
}; };
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer}; use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
@ -20,9 +21,9 @@ pub enum Instruction{
pub fn new<'a>( pub fn new<'a>(
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>, mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
user_settings:crate::settings::UserSettings, user_settings:settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{ )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
let physics=crate::physics::PhysicsState::default(); let physics=strafesnet_physics::physics::PhysicsState::default();
let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO); let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO);
let simulation=Simulation::new(timer,physics); let simulation=Simulation::new(timer,physics);
let mut session=Session::new( let mut session=Session::new(
@ -67,7 +68,7 @@ pub fn new<'a>(
}, },
Instruction::ChangeMap(complete_map)=>{ Instruction::ChangeMap(complete_map)=>{
run_session_instruction!(ins.time,SessionInstruction::ChangeMap(&complete_map)); run_session_instruction!(ins.time,SessionInstruction::ChangeMap(&complete_map));
run_session_instruction!(ins.time,SessionInstruction::Input(SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId::MAIN,strafesnet_common::gameplay_modes::StageId::FIRST)))); run_session_instruction!(ins.time,SessionInstruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId::MAIN,strafesnet_common::gameplay_modes::StageId::FIRST))));
run_graphics_worker_instruction!(GraphicsInstruction::ChangeMap(complete_map)); run_graphics_worker_instruction!(GraphicsInstruction::ChangeMap(complete_map));
}, },
Instruction::LoadReplay(bot)=>{ Instruction::LoadReplay(bot)=>{

@ -17,9 +17,6 @@ fn required_downlevel_capabilities()->wgpu::DownlevelCapabilities{
..wgpu::DownlevelCapabilities::default() ..wgpu::DownlevelCapabilities::default()
} }
} }
pub fn required_limits()->wgpu::Limits{
wgpu::Limits::default()
}
struct SetupContextPartial1{ struct SetupContextPartial1{
backends:wgpu::Backends, backends:wgpu::Backends,
@ -130,7 +127,7 @@ impl<'a> SetupContextPartial3<'a>{
let required_features=required_features(); let required_features=required_features();
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface. // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
let needed_limits=required_limits().using_resolution(self.adapter.limits()); let needed_limits=strafesnet_graphics::graphics::required_limits().using_resolution(self.adapter.limits());
let trace_dir=std::env::var("WGPU_TRACE"); let trace_dir=std::env::var("WGPU_TRACE");
let (device, queue)=pollster::block_on(self.adapter let (device, queue)=pollster::block_on(self.adapter

@ -3,7 +3,8 @@ use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInn
use strafesnet_common::physics::{MiscInstruction,SetControlInstruction}; use strafesnet_common::physics::{MiscInstruction,SetControlInstruction};
use crate::file::LoadFormat; use crate::file::LoadFormat;
use crate::physics_worker::Instruction as PhysicsWorkerInstruction; use crate::physics_worker::Instruction as PhysicsWorkerInstruction;
use crate::session::{SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction}; use strafesnet_session::session::{self,SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction};
use strafesnet_settings::settings;
pub enum Instruction{ pub enum Instruction{
Resize(winit::dpi::PhysicalSize<u32>), Resize(winit::dpi::PhysicalSize<u32>),
@ -150,7 +151,7 @@ impl WindowContext<'_>{
"R"|"r"=>s.then(||{ "R"|"r"=>s.then(||{
//mouse needs to be reset since the position is absolute //mouse needs to be reset since the position is absolute
self.mouse_pos=glam::DVec2::ZERO; self.mouse_pos=glam::DVec2::ZERO;
SessionInstructionSubset::Input(SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndRestart)) SessionInstructionSubset::Input(SessionInputInstruction::Mode(session::ImplicitModeInstruction::ResetAndRestart))
}), }),
"F"|"f"=>input_misc!(PracticeFly,s), "F"|"f"=>input_misc!(PracticeFly,s),
"B"|"b"=>session_ctrl!(CopyRecordingIntoReplayAndSpectate,s), "B"|"b"=>session_ctrl!(CopyRecordingIntoReplayAndSpectate,s),
@ -210,9 +211,9 @@ pub fn worker<'a>(
setup_context:crate::setup::SetupContext<'a>, setup_context:crate::setup::SetupContext<'a>,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{ )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
// WindowContextSetup::new // WindowContextSetup::new
let user_settings=crate::settings::read_user_settings(); let user_settings=settings::read_user_settings();
let mut graphics=crate::graphics::GraphicsState::new(&setup_context.device,&setup_context.queue,&setup_context.config); let mut graphics=strafesnet_graphics::graphics::GraphicsState::new(&setup_context.device,&setup_context.queue,&setup_context.config);
graphics.load_user_settings(&user_settings); graphics.load_user_settings(&user_settings);
//WindowContextSetup::into_context //WindowContextSetup::into_context

@ -176,7 +176,7 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
#[cfg(test)] #[cfg(test)]
mod test{ mod test{
use super::{thread,QRWorker}; use super::{thread,QRWorker};
type Body=crate::physics::Body; type Body=strafesnet_physics::physics::Body;
use strafesnet_common::{integer,instruction}; use strafesnet_common::{integer,instruction};
#[test]//How to run this test with printing: cargo test --release -- --nocapture #[test]//How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() { fn test_worker() {