Compare commits

..

41 Commits

Author SHA1 Message Date
a139b5a409 update deps (aggressive) 2024-07-22 12:58:36 -07:00
1b71bda9c7 wgpu 22.0.0 2024-07-22 12:33:39 -07:00
5f1b162775 use strafesnet registry 2024-03-30 13:39:06 -07:00
ed27ee060b pretty polygon fanning from vbsp code 2024-03-18 21:08:39 -07:00
5c107c1e5f print texture load error 2024-03-18 20:50:39 -07:00
3834194156 update deps 2024-03-13 13:57:21 -07:00
d0432188b5 include meshes symbolic link 2024-03-13 11:48:34 -07:00
9ff946c325 implement roblox mesh loading 2024-03-13 11:48:34 -07:00
63b2b1676c update deps 2024-03-13 11:48:34 -07:00
b7fc7d2a12 update deps 2024-03-02 13:19:01 -08:00
f273bbf4a9 update strafesnet deps 2024-03-02 13:18:56 -08:00
cc19917025 note 2024-02-22 02:17:20 -08:00
cc58f23512 the bug 2024-02-22 01:30:17 -08:00
b6e7ce4e25 fixup double references 2024-02-22 01:29:29 -08:00
b0d8f2e09a not-working 2024-02-22 00:57:09 -08:00
9b4b09798b notes about fixes 2024-02-21 22:58:40 -08:00
5a55aefb4f unused argument 2024-02-21 22:28:33 -08:00
8f606b7cfc ad-hoc fixups 2024-02-21 22:07:40 -08:00
fad7ed393b implicitly apply_to_body in apply_enum 2024-02-21 22:07:21 -08:00
d2a864e2c1 rename MoveState::changed to apply_enum 2024-02-21 22:07:05 -08:00
f027594ab4 before and after not needed currently 2024-02-21 05:24:12 -08:00
0026b92a72 hide hints 2024-02-21 05:20:40 -08:00
4dc706b783 notes 2024-02-21 05:20:40 -08:00
9bf3f55191 it works?? 2024-02-21 05:17:13 -08:00
eb34cce746 flyin 2024-02-21 04:12:08 -08:00
623a2d2a4f be pedantic about public private 2024-02-21 04:11:31 -08:00
aadcca91ea Debug 2024-02-21 03:23:50 -08:00
04c70ba0bc more private 2024-02-21 02:52:20 -08:00
865c086f14 actually implement this function 2024-02-21 02:48:49 -08:00
619049c11f unneeded 2024-02-21 02:48:41 -08:00
2acefe9a1a want water here 2024-02-21 02:41:19 -08:00
2496d71afa compiler 2024-02-21 02:32:00 -08:00
8ffbca204b wishful thinking 2024-02-21 02:31:50 -08:00
05e2f67e36 don't write duplicate code 2024-02-21 02:17:13 -08:00
b955407b06 delete get_move_state function 2024-02-21 01:32:29 -08:00
fb47b09925 wip 2024-02-21 01:32:03 -08:00
dbd08729d7 wip 2024-02-21 00:31:44 -08:00
c895a66ad6 wip 2024-02-20 22:27:31 -08:00
efdb4c97be edit jumped_velocity garbage 2024-02-18 01:52:29 -08:00
9396623f0c wip - notably remove camera interpolation for walking and rocket 2024-02-18 01:52:29 -08:00
c55156bb92 wip 2024-02-18 01:52:29 -08:00
19 changed files with 283 additions and 546 deletions

1
.gitignore vendored

@ -1 +1,2 @@
/target
/textures

156
Cargo.lock generated

@ -28,7 +28,7 @@ dependencies = [
"getrandom",
"once_cell",
"version_check",
"zerocopy 0.7.35",
"zerocopy",
]
[[package]]
@ -149,18 +149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173901312e9850391d4d7c1318c4e099fdc037d61870fca427429830efdb4e5f"
dependencies = [
"array-init",
"binrw_derive 0.13.3",
"bytemuck",
]
[[package]]
name = "binrw"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f36b7cb3ab9ff6a2858650d8dc360e783a5d14dc29594db48c56a3c233cc265"
dependencies = [
"array-init",
"binrw_derive 0.14.0",
"binrw_derive",
"bytemuck",
]
@ -177,19 +166,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "binrw_derive"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ea7a8c5c8eeffffac6d54d172444e15beffac6f817fac714460a9a9aa88da3"
dependencies = [
"either",
"owo-colors",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bit-set"
version = "0.6.0"
@ -262,9 +238,9 @@ dependencies = [
[[package]]
name = "bytemuck"
version = "1.16.3"
version = "1.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
dependencies = [
"bytemuck_derive",
]
@ -294,9 +270,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]]
name = "calloop"
@ -326,9 +302,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.7"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
dependencies = [
"jobserver",
"libc",
@ -675,9 +651,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.28.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
[[package]]
name = "glow"
@ -802,9 +778,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.3.0"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
@ -843,9 +819,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.32"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
@ -878,9 +854,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lazy-regex"
version = "3.2.0"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "576c8060ecfdf2e56995cf3274b4f2d71fa5e4fa3607c1c0b63c10180ee58741"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
@ -889,9 +865,9 @@ dependencies = [
[[package]]
name = "lazy-regex-proc_macros"
version = "3.2.0"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9efb9e65d4503df81c615dc33ff07042a9408ac7f26b45abee25566f7fbfd12c"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
@ -1040,9 +1016,9 @@ dependencies = [
[[package]]
name = "naga"
version = "22.1.0"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad"
checksum = "09eeccb9b50f4f7839b214aa3e08be467159506a986c18e0702170ccf720a453"
dependencies = [
"arrayvec",
"bit-set",
@ -1109,18 +1085,18 @@ dependencies = [
[[package]]
name = "num_enum"
version = "0.7.3"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.7.3"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@ -1460,12 +1436,9 @@ checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
[[package]]
name = "ppv-lite86"
version = "0.2.18"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
dependencies = [
"zerocopy 0.6.6",
]
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "presser"
@ -1597,11 +1570,11 @@ dependencies = [
[[package]]
name = "rbx_mesh"
version = "0.1.2"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864ead0e98afce28c960f653d6203483834890d07f87b60e2f01415530a2fe9d"
checksum = "b9f5cad033f5b4e15d13176f1f15aa1c6b9f025ce6b7ae64a5ce00f97367f2d0"
dependencies = [
"binrw 0.14.0",
"binrw",
"lazy-regex",
]
@ -1880,7 +1853,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strafe-client"
version = "0.10.2"
version = "0.9.5"
dependencies = [
"bytemuck",
"configparser",
@ -1893,16 +1866,15 @@ dependencies = [
"strafesnet_common",
"strafesnet_deferred_loader",
"strafesnet_rbx_loader",
"strafesnet_snf",
"wgpu",
"winit",
]
[[package]]
name = "strafesnet_bsp_loader"
version = "0.1.3"
version = "0.1.1"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "6d4af68c422b5f57febbaa218f44ba02d413fd25e84afff9e45e557a8caee2ce"
checksum = "fa4f0e59adc42910a08bdfeabbe8c8fdfd9295f999d642226214a3784eacf654"
dependencies = [
"glam",
"strafesnet_common",
@ -1912,9 +1884,9 @@ dependencies = [
[[package]]
name = "strafesnet_common"
version = "0.2.1"
version = "0.1.2"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "0ac1343bd9731706a962b66fac1efd1bd5ff56fd0a40f41c48e006fad91cf80c"
checksum = "0704e795010e6ca97f3d3dd6c24e76bf2d23f91186e1807ad4c7cb84ba6b8239"
dependencies = [
"bitflags 2.6.0",
"glam",
@ -1923,9 +1895,9 @@ dependencies = [
[[package]]
name = "strafesnet_deferred_loader"
version = "0.3.1"
version = "0.3.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "c3891dcbdbc20b03cf561786b810e839ae7c11dd8810fd005f2474805ee9cccc"
checksum = "737954ffff299d244b0267b8101092034935d98fff7694628dbe438151579c3a"
dependencies = [
"lazy-regex",
"strafesnet_common",
@ -1934,9 +1906,9 @@ dependencies = [
[[package]]
name = "strafesnet_rbx_loader"
version = "0.3.2"
version = "0.3.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "21ea93b0170063dd2a063a138c41e6f7a6c14a82c6553fa4ba32df65a26efc6e"
checksum = "d093481d64ce60837f8d2fbb512b935e5730932ed2aae90e94b3ba6112350176"
dependencies = [
"bytemuck",
"glam",
@ -1949,17 +1921,6 @@ dependencies = [
"strafesnet_common",
]
[[package]]
name = "strafesnet_snf"
version = "0.1.1"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "78479f73437a3f10230efd2304be0f3ef30dff98c54d93613ed1621bfd6a7da6"
dependencies = [
"binrw 0.14.0",
"id",
"strafesnet_common",
]
[[package]]
name = "strict-num"
version = "0.1.1"
@ -2055,9 +2016,9 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.8"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
[[package]]
name = "toml_edit"
@ -2139,7 +2100,7 @@ checksum = "d9267540dab0c93bb5201c40ba3b2d027e2717bf355a8f9bf25377b06a5b32f6"
dependencies = [
"ahash",
"arrayvec",
"binrw 0.13.3",
"binrw",
"bitflags 2.6.0",
"bv",
"cgmath",
@ -2166,9 +2127,9 @@ dependencies = [
[[package]]
name = "version_check"
version = "0.9.5"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vmdl"
@ -2399,9 +2360,9 @@ dependencies = [
[[package]]
name = "wgpu"
version = "22.1.0"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433"
checksum = "c87e07e87a179614940ad845397e03201847453a37b43a31a3b54eee2e6e32ce"
dependencies = [
"arrayvec",
"cfg_aliases 0.1.1",
@ -2424,9 +2385,9 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "22.1.0"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a"
checksum = "e0f191908a21968991463fcf3b42cb6c9648c0fb7fa301b8fc733bc21a9ed9bd"
dependencies = [
"arrayvec",
"bit-vec",
@ -2879,34 +2840,13 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
[[package]]
name = "zerocopy"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
dependencies = [
"byteorder 1.5.0",
"zerocopy-derive 0.6.6",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy-derive"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"zerocopy-derive",
]
[[package]]

@ -1,6 +1,6 @@
[package]
name = "strafe-client"
version = "0.10.2"
version = "0.9.5"
edition = "2021"
repository = "https://git.itzana.me/StrafesNET/strafe-client"
license = "Custom"
@ -8,29 +8,23 @@ description = "StrafesNET game client for bhop and surf."
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["snf"]
snf = ["dep:strafesnet_snf"]
source = ["dep:strafesnet_deferred_loader", "dep:strafesnet_bsp_loader"]
roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"]
[dependencies]
bytemuck = { version = "1.13.1", features = ["derive"] }
configparser = "3.0.2"
ddsfile = "0.5.1"
glam = "0.28.0"
glam = "0.25.0"
id = { version = "0.1.0", registry = "strafesnet" }
parking_lot = "0.12.1"
pollster = "0.3.0"
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true }
strafesnet_common = { version = "0.2.1", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true }
strafesnet_rbx_loader = { version = "0.3.2", registry = "strafesnet", optional = true }
strafesnet_snf = { version = "0.1.0", registry = "strafesnet", optional = true }
strafesnet_bsp_loader = { version = "0.1.1", registry = "strafesnet" }
strafesnet_common = { version = "0.1.2", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.3.0", features = ["legacy"], registry = "strafesnet" }
strafesnet_rbx_loader = { version = "0.3.0", registry = "strafesnet" }
wgpu = "22.0.0"
winit = "0.30.4"
[profile.release]
#[profile.release]
#lto = true
strip = true
codegen-units = 1
#strip = true
#codegen-units = 1

@ -2,14 +2,8 @@ use std::io::Read;
#[derive(Debug)]
pub enum ReadError{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::ReadError),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::ReadError),
#[cfg(feature="snf")]
StrafesNET(strafesnet_snf::Error),
#[cfg(feature="snf")]
StrafesNETMap(strafesnet_snf::map::Error),
Io(std::io::Error),
UnknownFileFormat,
}
@ -21,27 +15,16 @@ impl std::fmt::Display for ReadError{
impl std::error::Error for ReadError{}
pub enum DataStructure{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Dom),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")]
StrafesNET(strafesnet_common::map::CompleteMap),
Source(strafesnet_bsp_loader::Bsp)
}
pub fn read<R:Read+std::io::Seek>(input:R)->Result<DataStructure,ReadError>{
pub fn read<R:Read>(input:R)->Result<DataStructure,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..4]{
#[cfg(feature="roblox")]
b"<rob"=>Ok(DataStructure::Roblox(strafesnet_rbx_loader::read(buf).map_err(ReadError::Roblox)?)),
#[cfg(feature="source")]
b"VBSP"=>Ok(DataStructure::Source(strafesnet_bsp_loader::read(buf).map_err(ReadError::Source)?)),
#[cfg(feature="snf")]
b"SNFM"=>Ok(DataStructure::StrafesNET(
strafesnet_snf::read_map(buf).map_err(ReadError::StrafesNET)?
.into_complete_map().map_err(ReadError::StrafesNETMap)?
)),
_=>Err(ReadError::UnknownFileFormat),
}
}
@ -63,9 +46,6 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
//blocking because it's simpler...
let file=std::fs::File::open(path).map_err(LoadError::File)?;
match read(file).map_err(LoadError::ReadError)?{
#[cfg(feature="snf")]
DataStructure::StrafesNET(map)=>Ok(map),
#[cfg(feature="roblox")]
DataStructure::Roblox(dom)=>{
let mut loader=strafesnet_deferred_loader::roblox_legacy();
@ -98,7 +78,6 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
Ok(map)
},
#[cfg(feature="source")]
DataStructure::Source(bsp)=>{
let mut loader=strafesnet_deferred_loader::source_legacy();
@ -138,4 +117,4 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
Ok(map)
},
}
}
}

@ -137,22 +137,22 @@ impl PhysicsMesh{
//go go gadget debug print mesh
let data=PhysicsMeshData{
faces:vec![
Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
Face{normal:Planar64Vec3::raw( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
],
verts:vec![
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
Vert(Planar64Vec3::raw( 4294967296,-4294967296,-4294967296)),
Vert(Planar64Vec3::raw( 4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw( 4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw( 4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw(-4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296,-4294967296,-4294967296))
]
};
let mesh_topology=PhysicsMeshTopology{
@ -167,17 +167,17 @@ impl PhysicsMesh{
FaceRefs{edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(9)]}
],
edge_topology:vec![
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(0)],verts:[SubmeshVertId(0),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(5)],verts:[SubmeshVertId(1),SubmeshVertId(4)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(3)],verts:[SubmeshVertId(4),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(1)],verts:[SubmeshVertId(2),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(2)],verts:[SubmeshVertId(3),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(3)],verts:[SubmeshVertId(5),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(0)],verts:[SubmeshVertId(0),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(5)],verts:[SubmeshVertId(1),SubmeshVertId(4)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(3)],verts:[SubmeshVertId(4),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(1)],verts:[SubmeshVertId(2),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(2)],verts:[SubmeshVertId(3),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(3)],verts:[SubmeshVertId(5),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]}
],
vert_topology:vec![
@ -961,4 +961,4 @@ fn test_is_empty_volume(){
fn build_me_a_cube(){
let mesh=PhysicsMesh::unit_cube();
//println!("mesh={:?}",mesh);
}
}

@ -1,8 +1,8 @@
use std::collections::{HashMap,HashSet};
use std::collections::HashMap;
use std::collections::HashSet;
use crate::model_physics::{self,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId};
use strafesnet_common::bvh;
use strafesnet_common::map;
use strafesnet_common::run;
use strafesnet_common::aabb;
use strafesnet_common::model::{MeshId,ModelId};
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
@ -13,10 +13,8 @@ use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer
use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
use gameplay::ModeState;
//internal influence
//when the physics asks itself what happens next, this is how it's represented
#[derive(Debug)]
enum PhysicsInternalInstruction{
pub enum PhysicsInstruction {
CollisionStart(Collision),
CollisionEnd(Collision),
StrafeTick,
@ -27,11 +25,12 @@ enum PhysicsInternalInstruction{
// bool,//true = Trigger; false = teleport
// bool,//true = Force
// )
//InputInstructions conditionally activate RefreshWalkTarget (by doing what SetWalkTargetVelocity used to do and then flagging it)
Input(PhysicsInputInstruction),
SetSensitivity(Ratio64Vec2),
}
//external influence
//this is how you influence the physics from outside
#[derive(Debug)]
pub enum PhysicsInputInstruction{
pub enum PhysicsInputInstruction {
ReplaceMouse(MouseState,MouseState),
SetNextMouse(MouseState),
SetMoveRight(bool),
@ -42,21 +41,12 @@ pub enum PhysicsInputInstruction{
SetMoveForward(bool),
SetJump(bool),
SetZoom(bool),
Restart,
Spawn(gameplay_modes::ModeId,StageId),
Reset,
Idle,
//Idle: there were no input events, but the simulation is safe to advance to this timestep
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
PracticeFly,
SetSensitivity(Ratio64Vec2),
}
#[derive(Debug)]
enum PhysicsInstruction{
Internal(PhysicsInternalInstruction),
//InputInstructions conditionally activate RefreshWalkTarget
//(by doing what SetWalkTargetVelocity used to do and then flagging it)
Input(PhysicsInputInstruction),
}
#[derive(Clone,Copy,Debug,Default,Hash)]
@ -115,10 +105,7 @@ impl InputState{
&self.next_mouse
}
fn set_next_mouse(&mut self,next_mouse:MouseState){
//I like your functions magic language
self.mouse=std::mem::replace(&mut self.next_mouse,next_mouse);
//equivalently:
//(self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
(self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
}
fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){
(self.next_mouse,self.mouse)=(next_mouse,mouse);
@ -260,8 +247,8 @@ impl PhysicsModels{
&model.transform
)
}
fn model(&self,model_id:PhysicsModelId)->&PhysicsModel{
&self.models[&model_id]
fn model(&self,model_id:PhysicsModelId)->Option<&PhysicsModel>{
self.models.get(&model_id)
}
fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{
&self.attributes[&self.models[&model_id].attr_id]
@ -569,13 +556,13 @@ impl MoveState{
=>None,
}
}
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<PhysicsInstruction>>{
//check if you have a valid walk state and create an instruction
match self{
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
&TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{
time,
instruction:PhysicsInternalInstruction::ReachWalkTargetVelocity
instruction:PhysicsInstruction::ReachWalkTargetVelocity
}),
TransientAcceleration::Unreachable{acceleration:_}
|TransientAcceleration::Reached
@ -585,7 +572,7 @@ impl MoveState{
TimedInstruction{
time:strafe.next_tick(time),
//only poll the physics if there is a before and after mouse event
instruction:PhysicsInternalInstruction::StrafeTick
instruction:PhysicsInstruction::StrafeTick
}
}),
MoveState::Water=>None,//TODO
@ -779,7 +766,7 @@ impl TouchingState{
}
}
}
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
for contact in &self.contacts{
//detect face slide off
@ -788,7 +775,7 @@ impl TouchingState{
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{
TimedInstruction{
time,
instruction:PhysicsInternalInstruction::CollisionEnd(
instruction:PhysicsInstruction::CollisionEnd(
Collision::Contact(ContactCollision{convex_mesh_id:contact.convex_mesh_id,face_id:contact.face_id})
),
}
@ -801,7 +788,7 @@ impl TouchingState{
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
TimedInstruction{
time,
instruction:PhysicsInternalInstruction::CollisionEnd(
instruction:PhysicsInstruction::CollisionEnd(
Collision::Intersect(IntersectCollision{convex_mesh_id:intersect.convex_mesh_id})
),
}
@ -917,10 +904,6 @@ pub struct PhysicsState{
//gameplay_state
mode_state:ModeState,
move_state:MoveState,
//run is non optional: when you spawn in a run is created
//the run cannot be finished unless you start it by visiting
//a start zone. If you change mode, a new run is created.
run:run::Run,
}
//random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{
@ -940,12 +923,11 @@ impl Default for PhysicsState{
time:Time::ZERO,
style:StyleModifiers::default(),
touching:TouchingState::default(),
move_state:MoveState::Air,
move_state: MoveState::Air,
camera:PhysicsCamera::default(),
input_state:InputState::default(),
world:WorldState{},
mode_state:ModeState::default(),
run:run::Run::new(),
}
}
}
@ -960,16 +942,15 @@ impl Default for PhysicsData{
}
}
impl PhysicsState{
impl PhysicsState {
fn clear(&mut self){
self.touching.clear();
}
fn reset_to_default(&mut self){
let mut new_state=Self::default();
new_state.camera.sensitivity=self.camera.sensitivity;
*self=new_state;
fn advance_time(&mut self, time: Time){
self.body.advance_time(time);
self.time=time;
}
fn next_move_instruction(&self)->Option<TimedInstruction<PhysicsInternalInstruction>>{
fn next_move_instruction(&self)->Option<TimedInstruction<PhysicsInstruction>>{
self.move_state.next_move_instruction(&self.style.strafe,self.time)
}
//lmao idk this is convenient
@ -1042,39 +1023,31 @@ pub struct PhysicsContext{
state:PhysicsState,//this captures the entire state of the physics.
data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state.
}
//the physics consumes the generic PhysicsInstruction, but can only emit the more narrow PhysicsInternalInstruction
impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsContext{
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction>){
atomic_state_update(&mut self.state,&self.data,ins)
}
}
impl instruction::InstructionEmitter<PhysicsInternalInstruction> for PhysicsContext{
impl instruction::InstructionEmitter<PhysicsInstruction> for PhysicsContext{
//this little next instruction function can cache its return value and invalidate the cached value by watching the State.
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
next_instruction_internal(&self.state,&self.data,time_limit)
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
literally_next_instruction_but_with_context(&self.state,&self.data,time_limit)
}
}
impl PhysicsContext{
pub fn clear(&mut self){
self.state.clear();
}
//TODO: remove non-standard interfaces to process_instruction
pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){
self.run_input_instruction(TimedInstruction{
self.process_instruction(TimedInstruction{
time:self.state.time,
instruction:PhysicsInputInstruction::SetSensitivity(user_settings.calculate_sensitivity()),
});
}
pub fn restart(&mut self){
self.run_input_instruction(TimedInstruction{
time:self.state.time,
instruction:PhysicsInputInstruction::Restart,
instruction:PhysicsInstruction::SetSensitivity(user_settings.calculate_sensitivity()),
});
}
pub fn spawn(&mut self){
self.run_input_instruction(TimedInstruction{
self.process_instruction(TimedInstruction{
time:self.state.time,
instruction:PhysicsInputInstruction::Spawn(gameplay_modes::ModeId::MAIN,StageId::FIRST),
instruction:PhysicsInstruction::Input(PhysicsInputInstruction::Reset),
});
}
pub const fn output(&self)->PhysicsOutputState{
@ -1090,9 +1063,6 @@ impl PhysicsContext{
}
pub fn generate_models(&mut self,map:&map::CompleteMap){
self.data.modes=map.modes.clone();
for mode in &mut self.data.modes.modes{
mode.denormalize_data();
}
let mut used_attributes=Vec::new();
let mut physics_attr_id_from_model_attr_id=HashMap::<CollisionAttributesId,PhysicsAttributesId>::new();
let mut used_meshes=Vec::new();
@ -1160,19 +1130,16 @@ impl PhysicsContext{
}
//tickless gaming
fn run_internal_exhaustive(&mut self,time_limit:Time){
fn run(&mut self,time_limit:Time){
//prepare is ommitted - everything is done via instructions.
while let Some(instruction)=self.next_instruction(time_limit){//collect
//process
self.process_instruction(TimedInstruction{
time:instruction.time,
instruction:PhysicsInstruction::Internal(instruction.instruction),
});
self.process_instruction(instruction);
//write hash lol
}
}
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction>){
self.run_internal_exhaustive(instruction.time);
self.run(instruction.time);
self.process_instruction(TimedInstruction{
time:instruction.time,
instruction:PhysicsInstruction::Input(instruction.instruction),
@ -1180,8 +1147,7 @@ impl PhysicsContext{
}
}
//this is the one who asks
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
//JUST POLLING!!! NO MUTATION
let mut collector = instruction::InstructionCollector::new(time_limit);
@ -1195,7 +1161,7 @@ impl PhysicsContext{
aabb.inflate(data.hitbox_mesh.halfsize);
//common body
let relative_body=VirtualBody::relative(&Body::default(),&state.body).body(state.time);
data.bvh.the_tester(&aabb,&mut |&convex_mesh_id|{
data.bvh.the_tester(&aabb,&mut |convex_mesh_id|{
//no checks are needed because of the time limits.
let model_mesh=data.models.mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
@ -1203,7 +1169,7 @@ impl PhysicsContext{
//temp (?) code to avoid collision loops
.map_or(None,|(face,time)|if time==state.time{None}else{Some((face,time))})
.map(|(face,time)|{
TimedInstruction{time,instruction:PhysicsInternalInstruction::CollisionStart(match data.models.attr(convex_mesh_id.model_id){
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match data.models.attr(convex_mesh_id.model_id){
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{convex_mesh_id,face_id:face}),
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{convex_mesh_id}),
})}
@ -1274,7 +1240,7 @@ fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,sty
MoveState::Air
}
fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,mode:&gameplay_modes::Mode,models:&PhysicsModels,stage_id:gameplay_modes::StageId)->Option<MoveState>{
let model=models.model(mode.get_spawn_model_id(stage_id)?.into());
let model=models.model(mode.get_spawn_model_id(stage_id)?.into()).unwrap();
let point=model.transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
Some(teleport(body,touching,models,style,hitbox_mesh,point))
}
@ -1330,8 +1296,8 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,models
}
match wormhole{
&Some(gameplay_attributes::Wormhole{destination_model})=>{
let origin_model=models.model(convex_mesh_id.model_id);
let destination_model=models.model(destination_model.into());
let origin_model=models.model(convex_mesh_id.model_id).unwrap();
let destination_model=models.model(destination_model.into()).unwrap();
//ignore the transform for now
Some(teleport(body,touching,models,style,hitbox_mesh,body.position-origin_model.transform.vertex.translation+destination_model.transform.vertex.translation))
}
@ -1339,18 +1305,27 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,models
}
}
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction>){
let should_advance_body=match ins.instruction{
PhysicsInternalInstruction::CollisionStart(_)
|PhysicsInternalInstruction::CollisionEnd(_)
|PhysicsInternalInstruction::StrafeTick
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>true,
};
if should_advance_body{
state.body.advance_time(state.time);
}
fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction>){
match &ins.instruction{
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|PhysicsInstruction::Input(PhysicsInputInstruction::ReplaceMouse(_,_))
|PhysicsInstruction::StrafeTick=>(),
_=>println!("{}|{:?}",ins.time,ins.instruction),
}
//selectively update body
match &ins.instruction{
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)=>state.time=ins.time,//idle simply updates time
PhysicsInstruction::Input(_)
|PhysicsInstruction::ReachWalkTargetVelocity
|PhysicsInstruction::CollisionStart(_)
|PhysicsInstruction::CollisionEnd(_)
|PhysicsInstruction::StrafeTick
|PhysicsInstruction::SetSensitivity(_)
=>state.advance_time(ins.time),
}
match ins.instruction{
PhysicsInternalInstruction::CollisionStart(collision)=>{
PhysicsInstruction::CollisionStart(collision)=>{
let convex_mesh_id=collision.convex_mesh_id();
match (data.models.attr(convex_mesh_id.model_id),&collision){
(PhysicsCollisionAttributes::Contact{contacting,general},&Collision::Contact(contact))=>{
@ -1434,32 +1409,17 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
//doing input_and_body to refresh the walk state if you hit a wall while accelerating
state.apply_enum_and_input_and_body(data);
},
(PhysicsCollisionAttributes::Intersect{intersecting:_,general},Collision::Intersect(_intersect))=>{
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
state.touching.insert(collision);
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){
let zone=mode.get_zone(convex_mesh_id.model_id.into());
match zone{
Some(gameplay_modes::Zone::Start)=>{
println!("@@@@ Starting new run!");
state.run=run::Run::new();
},
Some(gameplay_modes::Zone::Finish)=>{
match state.run.finish(state.time){
Ok(())=>println!("@@@@ Finished run time={}",state.run.time(state.time)),
Err(e)=>println!("@@@@ Run Finish error:{e:?}"),
}
},
Some(gameplay_modes::Zone::Anticheat)=>state.run.flag(run::FlagReason::Anticheat),
None=>(),
}
run_teleport_behaviour(&general.wormhole,&data.models,mode,&state.style,&data.hitbox_mesh,&mut state.mode_state,&mut state.touching,&mut state.body,convex_mesh_id);
}
},
_=>panic!("invalid pair"),
}
},
PhysicsInternalInstruction::CollisionEnd(collision)=>{
PhysicsInstruction::CollisionEnd(collision)=>{
match (data.models.attr(collision.convex_mesh_id().model_id),&collision){
(PhysicsCollisionAttributes::Contact{contacting:_,general:_},&Collision::Contact(contact))=>{
state.touching.remove(&collision);//remove contact before calling contact_constrain_acceleration
@ -1476,23 +1436,11 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
},
(PhysicsCollisionAttributes::Intersect{intersecting: _,general:_},Collision::Intersect(_))=>{
state.touching.remove(&collision);
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){
let zone=mode.get_zone(collision.convex_mesh_id().model_id.into());
match zone{
Some(gameplay_modes::Zone::Start)=>{
match state.run.start(state.time){
Ok(())=>println!("@@@@ Started run"),
Err(e)=>println!("@@@@ Run Start error:{e:?}"),
}
},
_=>(),
}
}
},
_=>panic!("invalid pair"),
}
},
PhysicsInternalInstruction::StrafeTick=>{
PhysicsInstruction::StrafeTick=>{
//TODO make this less huge
if let Some(strafe_settings)=&state.style.strafe{
let controls=state.input_state.controls;
@ -1510,7 +1458,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
}
}
}
PhysicsInternalInstruction::ReachWalkTargetVelocity=>{
PhysicsInstruction::ReachWalkTargetVelocity=>{
match &mut state.move_state{
MoveState::Air
|MoveState::Water
@ -1534,46 +1482,10 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
}
}
},
}
}
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInputInstruction>){
let should_advance_body=match ins.instruction{
//the body may as well be a quantum wave function
//as far as these instruction are concerned (they don't care where it is)
PhysicsInputInstruction::SetSensitivity(..)
|PhysicsInputInstruction::Restart
|PhysicsInputInstruction::Spawn(..)
|PhysicsInputInstruction::SetZoom(..)
|PhysicsInputInstruction::Idle=>false,
//these controls only update the body if you are on the ground
PhysicsInputInstruction::SetNextMouse(..)
|PhysicsInputInstruction::ReplaceMouse(..)
|PhysicsInputInstruction::SetMoveForward(..)
|PhysicsInputInstruction::SetMoveLeft(..)
|PhysicsInputInstruction::SetMoveBack(..)
|PhysicsInputInstruction::SetMoveRight(..)
|PhysicsInputInstruction::SetMoveUp(..)
|PhysicsInputInstruction::SetMoveDown(..)
|PhysicsInputInstruction::SetJump(..)=>{
match &state.move_state{
MoveState::Fly
|MoveState::Water
|MoveState::Walk(_)
|MoveState::Ladder(_)=>true,
MoveState::Air=>false,
}
},
//the body must be updated unconditionally
PhysicsInputInstruction::PracticeFly=>true,
};
if should_advance_body{
state.body.advance_time(state.time);
}
//TODO: UNTAB
PhysicsInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity,
PhysicsInstruction::Input(input_instruction)=>{
let mut b_refresh_walk_target=true;
match ins.instruction{
PhysicsInputInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity,
match input_instruction{
PhysicsInputInstruction::SetNextMouse(m)=>{
state.camera.move_mouse(state.input_state.mouse_delta());
state.input_state.set_next_mouse(m);
@ -1589,6 +1501,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
PhysicsInputInstruction::SetMoveUp(s)=>state.input_state.set_control(Controls::MoveUp,s),
PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s),
PhysicsInputInstruction::SetJump(s)=>{
b_refresh_walk_target=false;
state.input_state.set_control(Controls::Jump,s);
if let Some(walk_state)=state.move_state.get_walk_state(){
if let Some(jump_settings)=&state.style.jump{
@ -1597,32 +1510,23 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
state.cull_velocity(&data,jumped_velocity);
}
}
b_refresh_walk_target=false;
},
PhysicsInputInstruction::SetZoom(s)=>{
state.input_state.set_control(Controls::Zoom,s);
b_refresh_walk_target=false;
},
PhysicsInputInstruction::Restart=>{
//totally reset physics state
state.reset_to_default();
//spawn at start zone
let spawn_point=data.modes.get_mode(state.mode_state.get_mode_id()).map(|mode|
PhysicsInputInstruction::Reset=>{
//it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
state.mode_state.clear();
state.mode_state.set_stage_id(gameplay_modes::StageId::FIRST);
let spawn_point=data.modes.get_mode(state.mode_state.get_mode_id()).and_then(|mode|
//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
data.models.model(mode.get_start().into()).transform.vertex.translation
data.models.model(mode.get_start().into()).map(|model|model.transform.vertex.translation)
).unwrap_or(Planar64Vec3::ZERO);
set_position(&mut state.body,&mut state.touching,spawn_point);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
state.set_move_state(data,MoveState::Air);
b_refresh_walk_target=false;
}
PhysicsInputInstruction::Spawn(mode_id,stage_id)=>{
//spawn at a particular stage
if let Some(mode)=data.modes.get_mode(mode_id){
teleport_to_spawn(&mut state.body,&mut state.touching,&state.style,&data.hitbox_mesh,mode,&data.models,stage_id);
}
b_refresh_walk_target=false;
},
PhysicsInputInstruction::PracticeFly=>{
match &state.move_state{
@ -1635,33 +1539,13 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}
b_refresh_walk_target=false;
},
PhysicsInputInstruction::Idle=>{
//literally idle!
b_refresh_walk_target=false;
},
PhysicsInputInstruction::Idle=>{b_refresh_walk_target=false;},//literally idle!
}
if b_refresh_walk_target{
state.apply_input_and_body(data);
state.cull_velocity(data,state.body.velocity);
}
}
fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction>){
match &ins.instruction{
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|PhysicsInstruction::Input(PhysicsInputInstruction::ReplaceMouse(_,_))
|PhysicsInstruction::Internal(PhysicsInternalInstruction::StrafeTick)
|PhysicsInstruction::Internal(PhysicsInternalInstruction::ReachWalkTargetVelocity)=>(),
_=>println!("{}|{:?}",ins.time,ins.instruction),
}
if ins.time<state.time{
println!("@@@@ Time travel warning! {:?}",ins);
}
state.time=ins.time;
match ins.instruction{
PhysicsInstruction::Internal(instruction)=>atomic_internal_instruction(state,data,TimedInstruction{time:ins.time,instruction}),
PhysicsInstruction::Input(instruction)=>atomic_input_instruction(state,data,TimedInstruction{time:ins.time,instruction}),
},
}
}
@ -1879,4 +1763,4 @@ mod test{
Time::ZERO
),None);
}
}
}

@ -1,8 +1,6 @@
use crate::physics::{MouseState,PhysicsInputInstruction};
use strafesnet_common::integer::Time;
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::timer::{Scaled,Timer,TimerState};
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
#[derive(Debug)]
pub enum InputInstruction{
MoveMouse(glam::IVec2),
@ -14,8 +12,7 @@ pub enum InputInstruction{
MoveForward(bool),
Jump(bool),
Zoom(bool),
Restart,
Spawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
Reset,
PracticeFly,
}
pub enum Instruction{
@ -24,163 +21,111 @@ pub enum Instruction{
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
GenerateModels(strafesnet_common::map::CompleteMap),
ClearModels,
SetPaused(bool),
//Graphics(crate::graphics_worker::Instruction),
}
pub struct MouseInterpolator{
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
last_mouse_time:Time,//this value is pre-transformed to simulation time
mouse_blocking:bool,
timer:Timer<Scaled>,
}
impl MouseInterpolator{
fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,m:glam::IVec2){
if self.mouse_blocking{
//tell the game state which is living in the past about its future
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}),
});
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos},
MouseState{time:self.timer.time(ins.time),pos:m}
),
});
//delay physics execution until we have an interpolation target
self.mouse_blocking=true;
}
self.last_mouse_time=self.timer.time(ins.time);
}
/// returns the mapped physics input instruction
/// may or may not mutate internal state XD!
fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->Option<PhysicsInputInstruction>{
match &ins.instruction{
Instruction::Input(input_instruction)=>match input_instruction{
&InputInstruction::MoveMouse(m)=>{
if !self.timer.is_paused(){
self.push_mouse_instruction(physics,ins,m);
}
None
pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{
let mut mouse_blocking=true;
let mut last_mouse_time=physics.get_next_mouse().time;
let mut timeline=std::collections::VecDeque::new();
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
if if let Some(phys_input)=match &ins.instruction{
Instruction::Input(input_instruction)=>match input_instruction{
&InputInstruction::MoveMouse(m)=>{
if mouse_blocking{
//tell the game state which is living in the past about its future
timeline.push_front(TimedInstruction{
time:last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}),
});
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
timeline.push_front(TimedInstruction{
time:last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:last_mouse_time,pos:physics.get_next_mouse().pos},
MouseState{time:ins.time,pos:m}
),
});
//delay physics execution until we have an interpolation target
mouse_blocking=true;
}
last_mouse_time=ins.time;
None
},
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)),
InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset),
InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly),
},
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)),
&InputInstruction::Spawn(mode_id,stage_id)=>Some(PhysicsInputInstruction::Spawn(mode_id,stage_id)),
InputInstruction::Restart=>Some(PhysicsInputInstruction::Restart),
InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly),
},
//do these really need to idle the physics?
//sending None dumps the instruction queue
Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle),
Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle),
Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle),
Instruction::Render=>Some(PhysicsInputInstruction::Idle),
&Instruction::SetPaused(paused)=>{
if let Err(e)=self.timer.set_paused(ins.time,paused){
println!("Cannot pause: {e}");
}
Some(PhysicsInputInstruction::Idle)
},
}
}
fn update_mouse_blocking(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->bool{
if self.mouse_blocking{
//assume the mouse has stopped moving after 10ms.
//shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if Time::from_millis(10)<self.timer.time(ins.time)-physics.get_next_mouse().time{
//push an event to extrapolate no movement from
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:physics.get_next_mouse().pos}),
Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle),
Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle),
Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle),
Instruction::Render=>Some(PhysicsInputInstruction::Idle),
}{
//non-mouse event
timeline.push_back(TimedInstruction{
time:ins.time,
instruction:phys_input,
});
self.last_mouse_time=self.timer.time(ins.time);
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
self.mouse_blocking=false;
true
if mouse_blocking{
//assume the mouse has stopped moving after 10ms.
//shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if Time::from_millis(10)<ins.time-physics.get_next_mouse().time{
//push an event to extrapolate no movement from
timeline.push_front(TimedInstruction{
time:last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:physics.get_next_mouse().pos}),
});
last_mouse_time=ins.time;
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
mouse_blocking=false;
true
}else{
false
}
}else{
//keep this up to date so that it can be used as a known-timestamp
//that the mouse was not moving when the mouse starts moving again
last_mouse_time=ins.time;
true
}
}else{
false
//mouse event
true
}{
//empty queue
while let Some(instruction)=timeline.pop_front(){
physics.run_input_instruction(instruction);
}
}
}else{
//keep this up to date so that it can be used as a known-timestamp
//that the mouse was Timer<Scaled>not moving when the mouse starts moving again
self.last_mouse_time=self.timer.time(ins.time);
true
}
}
/// returns whether or not to empty the instruction queue
fn handle_physics_input(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,phys_input_option:Option<PhysicsInputInstruction>)->bool{
if let Some(phys_input)=phys_input_option{
//non-mouse event
self.timeline.push_back(TimedInstruction{
time:self.timer.time(ins.time),
instruction:phys_input,
});
//this returns the bool for us
self.update_mouse_blocking(physics,ins)
}else{
//mouse event
true
}
}
fn empty_queue(&mut self,physics:&mut crate::physics::PhysicsContext){
while let Some(ins)=self.timeline.pop_front(){
physics.run_input_instruction(ins);
}
}
fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>){
let physics_input_option=self.map_instruction(physics,ins);
let should_empty_queue=self.handle_physics_input(physics,ins,physics_input_option);
if should_empty_queue{
self.empty_queue(physics);
}
}
}
pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{
let mut interpolator=MouseInterpolator{
mouse_blocking:true,
last_mouse_time:physics.get_next_mouse().time,
timeline:std::collections::VecDeque::new(),
timer:Timer::from_state(Scaled::identity(),false),
};
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
interpolator.handle_instruction(&mut physics,&ins);
match ins.instruction{
Instruction::Render=>{
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),interpolator.timer.time(ins.time),physics.get_next_mouse().pos)).unwrap();
},
Instruction::Resize(size,user_settings)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
},
Instruction::GenerateModels(map)=>{
physics.generate_models(&map);
//important!
//bots will not work properly without this exact restart + spawn setup
//reset the physics state to start a new run on the new map
physics.restart();
//generate a spawn event so bots work properly on the first run
//no run started so does not invalidate the run
physics.spawn();
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
},
Instruction::ClearModels=>{
physics.clear();
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
},
_=>(),
}
})
}
match ins.instruction{
Instruction::Render=>{
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap();
},
Instruction::Resize(size,user_settings)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
},
Instruction::GenerateModels(map)=>{
physics.generate_models(&map);
physics.spawn();
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
},
Instruction::ClearModels=>{
physics.clear();
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
},
_=>(),
}
})
}

@ -217,8 +217,9 @@ pub fn setup_and_start(title:String){
//the thread that spawns the physics thread
let mut window_thread=window.into_worker(setup_context);
if let Some(arg)=std::env::args().nth(1){
let path=std::path::PathBuf::from(arg);
let args:Vec<String>=std::env::args().collect();
if args.len()==2{
let path=std::path::PathBuf::from(&args[1]);
window_thread.send(TimedInstruction{
time:integer::Time::ZERO,
instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)),

@ -35,12 +35,8 @@ impl WindowContext<'_>{
Err(e)=>println!("Failed to load map: {e}"),
}
},
winit::event::WindowEvent::Focused(state)=>{
winit::event::WindowEvent::Focused(_state)=>{
//pause unpause
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::SetPaused(!state),
}).unwrap();
//recalculate pressed keys on focus
},
winit::event::WindowEvent::KeyboardInput{
@ -111,11 +107,7 @@ impl WindowContext<'_>{
"e"=>Some(InputInstruction::MoveUp(s)),
"q"=>Some(InputInstruction::MoveDown(s)),
"z"=>Some(InputInstruction::Zoom(s)),
"r"=>if s{
//mouse needs to be reset since the position is absolute
self.mouse=crate::physics::MouseState::default();
Some(InputInstruction::Restart)
}else{None},
"r"=>if s{Some(InputInstruction::Reset)}else{None},
"f"=>if s{Some(InputInstruction::PracticeFly)}else{None},
_=>None,
},
@ -241,4 +233,4 @@ impl<'a> WindowContextSetup<'a>{
}
})
}
}
}

@ -190,7 +190,7 @@ mod test{
for _ in 0..5 {
let task = instruction::TimedInstruction{
time:integer::Time::ZERO,
instruction:physics::PhysicsInputInstruction::Idle,
instruction:physics::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();
}
@ -204,7 +204,7 @@ mod test{
// Send a new task
let task = instruction::TimedInstruction{
time:integer::Time::ZERO,
instruction:physics::PhysicsInputInstruction::Idle,
instruction:physics::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();

@ -1 +1 @@
mangohud ../target/release/strafe-client bhop_maps/5692113331.snfm
mangohud ../target/release/strafe-client bhop_maps/5692113331.rbxm

@ -1 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/bhop_snfm
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/bhop_all/

@ -1 +1 @@
cargo build --release --target x86_64-pc-windows-gnu --all-features
cargo build --release --target x86_64-pc-windows-gnu

@ -1 +0,0 @@
mangohud ../target/release/strafe-client bhop_maps/5692124338.snfm

1
tools/meshes Symbolic link

@ -0,0 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/meshes/

@ -1 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/surf_snfm
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/surf_all/

1
tools/textures Symbolic link

@ -0,0 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/textures/dds/

@ -1 +1 @@
mangohud ../target/release/strafe-client bhop_maps/5692152916.snfm
mangohud ../target/release/strafe-client bhop_maps/5692152916.rbxm

@ -1 +1 @@
mangohud ../target/release/strafe-client surf_maps/5692145408.snfm
mangohud ../target/release/strafe-client surf_maps/5692145408.rbxm