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 /target
/textures

156
Cargo.lock generated

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

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

@ -2,14 +2,8 @@ use std::io::Read;
#[derive(Debug)] #[derive(Debug)]
pub enum ReadError{ pub enum ReadError{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::ReadError), Roblox(strafesnet_rbx_loader::ReadError),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::ReadError), Source(strafesnet_bsp_loader::ReadError),
#[cfg(feature="snf")]
StrafesNET(strafesnet_snf::Error),
#[cfg(feature="snf")]
StrafesNETMap(strafesnet_snf::map::Error),
Io(std::io::Error), Io(std::io::Error),
UnknownFileFormat, UnknownFileFormat,
} }
@ -21,27 +15,16 @@ impl std::fmt::Display for ReadError{
impl std::error::Error for ReadError{} impl std::error::Error for ReadError{}
pub enum DataStructure{ pub enum DataStructure{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Dom), Roblox(strafesnet_rbx_loader::Dom),
#[cfg(feature="source")] Source(strafesnet_bsp_loader::Bsp)
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")]
StrafesNET(strafesnet_common::map::CompleteMap),
} }
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 mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?; let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..4]{ match &peek[0..4]{
#[cfg(feature="roblox")]
b"<rob"=>Ok(DataStructure::Roblox(strafesnet_rbx_loader::read(buf).map_err(ReadError::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)?)), 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), _=>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... //blocking because it's simpler...
let file=std::fs::File::open(path).map_err(LoadError::File)?; let file=std::fs::File::open(path).map_err(LoadError::File)?;
match read(file).map_err(LoadError::ReadError)?{ match read(file).map_err(LoadError::ReadError)?{
#[cfg(feature="snf")]
DataStructure::StrafesNET(map)=>Ok(map),
#[cfg(feature="roblox")]
DataStructure::Roblox(dom)=>{ DataStructure::Roblox(dom)=>{
let mut loader=strafesnet_deferred_loader::roblox_legacy(); 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) Ok(map)
}, },
#[cfg(feature="source")]
DataStructure::Source(bsp)=>{ DataStructure::Source(bsp)=>{
let mut loader=strafesnet_deferred_loader::source_legacy(); 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) Ok(map)
}, },
} }
} }

@ -137,22 +137,22 @@ impl PhysicsMesh{
//go go gadget debug print mesh //go go gadget debug print mesh
let data=PhysicsMeshData{ let data=PhysicsMeshData{
faces:vec![ faces:vec![
Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)} Face{normal:Planar64Vec3::raw( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
], ],
verts:vec![ verts:vec![
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296,-4294967296)), Vert(Planar64Vec3::raw( 4294967296,-4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296,-4294967296)), Vert(Planar64Vec3::raw( 4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296, 4294967296)), Vert(Planar64Vec3::raw( 4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296, 4294967296)), Vert(Planar64Vec3::raw( 4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296,-4294967296)), Vert(Planar64Vec3::raw(-4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296, 4294967296)), Vert(Planar64Vec3::raw(-4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296, 4294967296)), Vert(Planar64Vec3::raw(-4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296)) Vert(Planar64Vec3::raw(-4294967296,-4294967296,-4294967296))
] ]
}; };
let mesh_topology=PhysicsMeshTopology{ 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)]} FaceRefs{edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(9)]}
], ],
edge_topology:vec![ edge_topology:vec![
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]}, 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(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]}, 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(4),SubmeshFaceId(0)],verts:[SubmeshVertId(0),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(5)],verts:[SubmeshVertId(1),SubmeshVertId(4)]}, 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(1),SubmeshFaceId(3)],verts:[SubmeshVertId(4),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(1)],verts:[SubmeshVertId(2),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(4),SubmeshFaceId(2)],verts:[SubmeshVertId(3),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(3)],verts:[SubmeshVertId(5),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(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]}, EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]} EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]}
], ],
vert_topology:vec![ vert_topology:vec![
@ -961,4 +961,4 @@ fn test_is_empty_volume(){
fn build_me_a_cube(){ fn build_me_a_cube(){
let mesh=PhysicsMesh::unit_cube(); let mesh=PhysicsMesh::unit_cube();
//println!("mesh={:?}",mesh); //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 crate::model_physics::{self,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::aabb; use strafesnet_common::aabb;
use strafesnet_common::model::{MeshId,ModelId}; use strafesnet_common::model::{MeshId,ModelId};
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId}; 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 strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
use gameplay::ModeState; use gameplay::ModeState;
//internal influence
//when the physics asks itself what happens next, this is how it's represented
#[derive(Debug)] #[derive(Debug)]
enum PhysicsInternalInstruction{ pub enum PhysicsInstruction {
CollisionStart(Collision), CollisionStart(Collision),
CollisionEnd(Collision), CollisionEnd(Collision),
StrafeTick, StrafeTick,
@ -27,11 +25,12 @@ enum PhysicsInternalInstruction{
// bool,//true = Trigger; false = teleport // bool,//true = Trigger; false = teleport
// bool,//true = Force // 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)] #[derive(Debug)]
pub enum PhysicsInputInstruction{ pub enum PhysicsInputInstruction {
ReplaceMouse(MouseState,MouseState), ReplaceMouse(MouseState,MouseState),
SetNextMouse(MouseState), SetNextMouse(MouseState),
SetMoveRight(bool), SetMoveRight(bool),
@ -42,21 +41,12 @@ pub enum PhysicsInputInstruction{
SetMoveForward(bool), SetMoveForward(bool),
SetJump(bool), SetJump(bool),
SetZoom(bool), SetZoom(bool),
Restart, Reset,
Spawn(gameplay_modes::ModeId,StageId),
Idle, Idle,
//Idle: there were no input events, but the simulation is safe to advance to this timestep //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 //for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation. //to be 1 instruction ahead to generate the next state for interpolation.
PracticeFly, 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)] #[derive(Clone,Copy,Debug,Default,Hash)]
@ -115,10 +105,7 @@ impl InputState{
&self.next_mouse &self.next_mouse
} }
fn set_next_mouse(&mut self,next_mouse:MouseState){ fn set_next_mouse(&mut self,next_mouse:MouseState){
//I like your functions magic language (self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
self.mouse=std::mem::replace(&mut self.next_mouse,next_mouse);
//equivalently:
//(self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
} }
fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){ fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){
(self.next_mouse,self.mouse)=(next_mouse,mouse); (self.next_mouse,self.mouse)=(next_mouse,mouse);
@ -260,8 +247,8 @@ impl PhysicsModels{
&model.transform &model.transform
) )
} }
fn model(&self,model_id:PhysicsModelId)->&PhysicsModel{ fn model(&self,model_id:PhysicsModelId)->Option<&PhysicsModel>{
&self.models[&model_id] self.models.get(&model_id)
} }
fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{ fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{
&self.attributes[&self.models[&model_id].attr_id] &self.attributes[&self.models[&model_id].attr_id]
@ -569,13 +556,13 @@ impl MoveState{
=>None, =>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 //check if you have a valid walk state and create an instruction
match self{ match self{
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{ MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
&TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{ &TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{
time, time,
instruction:PhysicsInternalInstruction::ReachWalkTargetVelocity instruction:PhysicsInstruction::ReachWalkTargetVelocity
}), }),
TransientAcceleration::Unreachable{acceleration:_} TransientAcceleration::Unreachable{acceleration:_}
|TransientAcceleration::Reached |TransientAcceleration::Reached
@ -585,7 +572,7 @@ impl MoveState{
TimedInstruction{ TimedInstruction{
time:strafe.next_tick(time), time:strafe.next_tick(time),
//only poll the physics if there is a before and after mouse event //only poll the physics if there is a before and after mouse event
instruction:PhysicsInternalInstruction::StrafeTick instruction:PhysicsInstruction::StrafeTick
} }
}), }),
MoveState::Water=>None,//TODO 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); let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
for contact in &self.contacts{ for contact in &self.contacts{
//detect face slide off //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)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time, time,
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:PhysicsInstruction::CollisionEnd(
Collision::Contact(ContactCollision{convex_mesh_id:contact.convex_mesh_id,face_id:contact.face_id}) 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)|{ collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time, time,
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:PhysicsInstruction::CollisionEnd(
Collision::Intersect(IntersectCollision{convex_mesh_id:intersect.convex_mesh_id}) Collision::Intersect(IntersectCollision{convex_mesh_id:intersect.convex_mesh_id})
), ),
} }
@ -917,10 +904,6 @@ pub struct PhysicsState{
//gameplay_state //gameplay_state
mode_state:ModeState, mode_state:ModeState,
move_state:MoveState, 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 //random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{ pub struct PhysicsData{
@ -940,12 +923,11 @@ impl Default for PhysicsState{
time:Time::ZERO, time:Time::ZERO,
style:StyleModifiers::default(), style:StyleModifiers::default(),
touching:TouchingState::default(), touching:TouchingState::default(),
move_state:MoveState::Air, move_state: MoveState::Air,
camera:PhysicsCamera::default(), camera:PhysicsCamera::default(),
input_state:InputState::default(), input_state:InputState::default(),
world:WorldState{}, world:WorldState{},
mode_state:ModeState::default(), mode_state:ModeState::default(),
run:run::Run::new(),
} }
} }
} }
@ -960,16 +942,15 @@ impl Default for PhysicsData{
} }
} }
impl PhysicsState{ impl PhysicsState {
fn clear(&mut self){ fn clear(&mut self){
self.touching.clear(); self.touching.clear();
} }
fn reset_to_default(&mut self){ fn advance_time(&mut self, time: Time){
let mut new_state=Self::default(); self.body.advance_time(time);
new_state.camera.sensitivity=self.camera.sensitivity; self.time=time;
*self=new_state;
} }
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) self.move_state.next_move_instruction(&self.style.strafe,self.time)
} }
//lmao idk this is convenient //lmao idk this is convenient
@ -1042,39 +1023,31 @@ pub struct PhysicsContext{
state:PhysicsState,//this captures the entire state of the physics. 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. 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{ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsContext{
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction>){ fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction>){
atomic_state_update(&mut self.state,&self.data,ins) 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. //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>>{ fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
next_instruction_internal(&self.state,&self.data,time_limit) literally_next_instruction_but_with_context(&self.state,&self.data,time_limit)
} }
} }
impl PhysicsContext{ impl PhysicsContext{
pub fn clear(&mut self){ pub fn clear(&mut self){
self.state.clear(); self.state.clear();
} }
//TODO: remove non-standard interfaces to process_instruction
pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ 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, time:self.state.time,
instruction:PhysicsInputInstruction::SetSensitivity(user_settings.calculate_sensitivity()), instruction:PhysicsInstruction::SetSensitivity(user_settings.calculate_sensitivity()),
});
}
pub fn restart(&mut self){
self.run_input_instruction(TimedInstruction{
time:self.state.time,
instruction:PhysicsInputInstruction::Restart,
}); });
} }
pub fn spawn(&mut self){ pub fn spawn(&mut self){
self.run_input_instruction(TimedInstruction{ self.process_instruction(TimedInstruction{
time:self.state.time, time:self.state.time,
instruction:PhysicsInputInstruction::Spawn(gameplay_modes::ModeId::MAIN,StageId::FIRST), instruction:PhysicsInstruction::Input(PhysicsInputInstruction::Reset),
}); });
} }
pub const fn output(&self)->PhysicsOutputState{ pub const fn output(&self)->PhysicsOutputState{
@ -1090,9 +1063,6 @@ impl PhysicsContext{
} }
pub fn generate_models(&mut self,map:&map::CompleteMap){ pub fn generate_models(&mut self,map:&map::CompleteMap){
self.data.modes=map.modes.clone(); 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 used_attributes=Vec::new();
let mut physics_attr_id_from_model_attr_id=HashMap::<CollisionAttributesId,PhysicsAttributesId>::new(); let mut physics_attr_id_from_model_attr_id=HashMap::<CollisionAttributesId,PhysicsAttributesId>::new();
let mut used_meshes=Vec::new(); let mut used_meshes=Vec::new();
@ -1160,19 +1130,16 @@ impl PhysicsContext{
} }
//tickless gaming //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. //prepare is ommitted - everything is done via instructions.
while let Some(instruction)=self.next_instruction(time_limit){//collect while let Some(instruction)=self.next_instruction(time_limit){//collect
//process //process
self.process_instruction(TimedInstruction{ self.process_instruction(instruction);
time:instruction.time,
instruction:PhysicsInstruction::Internal(instruction.instruction),
});
//write hash lol //write hash lol
} }
} }
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction>){ pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction>){
self.run_internal_exhaustive(instruction.time); self.run(instruction.time);
self.process_instruction(TimedInstruction{ self.process_instruction(TimedInstruction{
time:instruction.time, time:instruction.time,
instruction:PhysicsInstruction::Input(instruction.instruction), instruction:PhysicsInstruction::Input(instruction.instruction),
@ -1180,8 +1147,7 @@ impl PhysicsContext{
} }
} }
//this is the one who asks fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
let mut collector = instruction::InstructionCollector::new(time_limit); let mut collector = instruction::InstructionCollector::new(time_limit);
@ -1195,7 +1161,7 @@ impl PhysicsContext{
aabb.inflate(data.hitbox_mesh.halfsize); aabb.inflate(data.hitbox_mesh.halfsize);
//common body //common body
let relative_body=VirtualBody::relative(&Body::default(),&state.body).body(state.time); 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. //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());
@ -1203,7 +1169,7 @@ impl PhysicsContext{
//temp (?) code to avoid collision loops //temp (?) code to avoid collision loops
.map_or(None,|(face,time)|if time==state.time{None}else{Some((face,time))}) .map_or(None,|(face,time)|if time==state.time{None}else{Some((face,time))})
.map(|(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::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{convex_mesh_id,face_id:face}),
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{convex_mesh_id}), 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 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>{ 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); 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)) 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{ match wormhole{
&Some(gameplay_attributes::Wormhole{destination_model})=>{ &Some(gameplay_attributes::Wormhole{destination_model})=>{
let origin_model=models.model(convex_mesh_id.model_id); let origin_model=models.model(convex_mesh_id.model_id).unwrap();
let destination_model=models.model(destination_model.into()); let destination_model=models.model(destination_model.into()).unwrap();
//ignore the transform for now //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)) 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>){ fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction>){
let should_advance_body=match ins.instruction{ match &ins.instruction{
PhysicsInternalInstruction::CollisionStart(_) PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|PhysicsInternalInstruction::CollisionEnd(_) |PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|PhysicsInternalInstruction::StrafeTick |PhysicsInstruction::Input(PhysicsInputInstruction::ReplaceMouse(_,_))
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>true, |PhysicsInstruction::StrafeTick=>(),
}; _=>println!("{}|{:?}",ins.time,ins.instruction),
if should_advance_body{ }
state.body.advance_time(state.time); //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{ match ins.instruction{
PhysicsInternalInstruction::CollisionStart(collision)=>{ PhysicsInstruction::CollisionStart(collision)=>{
let convex_mesh_id=collision.convex_mesh_id(); let convex_mesh_id=collision.convex_mesh_id();
match (data.models.attr(convex_mesh_id.model_id),&collision){ match (data.models.attr(convex_mesh_id.model_id),&collision){
(PhysicsCollisionAttributes::Contact{contacting,general},&Collision::Contact(contact))=>{ (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 //doing input_and_body to refresh the walk state if you hit a wall while accelerating
state.apply_enum_and_input_and_body(data); 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 //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
state.touching.insert(collision); state.touching.insert(collision);
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){ 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); 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"), _=>panic!("invalid pair"),
} }
}, },
PhysicsInternalInstruction::CollisionEnd(collision)=>{ PhysicsInstruction::CollisionEnd(collision)=>{
match (data.models.attr(collision.convex_mesh_id().model_id),&collision){ match (data.models.attr(collision.convex_mesh_id().model_id),&collision){
(PhysicsCollisionAttributes::Contact{contacting:_,general:_},&Collision::Contact(contact))=>{ (PhysicsCollisionAttributes::Contact{contacting:_,general:_},&Collision::Contact(contact))=>{
state.touching.remove(&collision);//remove contact before calling contact_constrain_acceleration 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(_))=>{ (PhysicsCollisionAttributes::Intersect{intersecting: _,general:_},Collision::Intersect(_))=>{
state.touching.remove(&collision); 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"), _=>panic!("invalid pair"),
} }
}, },
PhysicsInternalInstruction::StrafeTick=>{ PhysicsInstruction::StrafeTick=>{
//TODO make this less huge //TODO make this less huge
if let Some(strafe_settings)=&state.style.strafe{ if let Some(strafe_settings)=&state.style.strafe{
let controls=state.input_state.controls; 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{ match &mut state.move_state{
MoveState::Air MoveState::Air
|MoveState::Water |MoveState::Water
@ -1534,46 +1482,10 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
} }
} }
}, },
} PhysicsInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity,
} PhysicsInstruction::Input(input_instruction)=>{
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
let mut b_refresh_walk_target=true; let mut b_refresh_walk_target=true;
match ins.instruction{ match input_instruction{
PhysicsInputInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity,
PhysicsInputInstruction::SetNextMouse(m)=>{ PhysicsInputInstruction::SetNextMouse(m)=>{
state.camera.move_mouse(state.input_state.mouse_delta()); state.camera.move_mouse(state.input_state.mouse_delta());
state.input_state.set_next_mouse(m); 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::SetMoveUp(s)=>state.input_state.set_control(Controls::MoveUp,s),
PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s), PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s),
PhysicsInputInstruction::SetJump(s)=>{ PhysicsInputInstruction::SetJump(s)=>{
b_refresh_walk_target=false;
state.input_state.set_control(Controls::Jump,s); state.input_state.set_control(Controls::Jump,s);
if let Some(walk_state)=state.move_state.get_walk_state(){ if let Some(walk_state)=state.move_state.get_walk_state(){
if let Some(jump_settings)=&state.style.jump{ 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); state.cull_velocity(&data,jumped_velocity);
} }
} }
b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::SetZoom(s)=>{ PhysicsInputInstruction::SetZoom(s)=>{
state.input_state.set_control(Controls::Zoom,s); state.input_state.set_control(Controls::Zoom,s);
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::Restart=>{ PhysicsInputInstruction::Reset=>{
//totally reset physics state //it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
state.reset_to_default(); state.mode_state.clear();
//spawn at start zone state.mode_state.set_stage_id(gameplay_modes::StageId::FIRST);
let spawn_point=data.modes.get_mode(state.mode_state.get_mode_id()).map(|mode| 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: 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()).map(|model|model.transform.vertex.translation)
data.models.model(mode.get_start().into()).transform.vertex.translation
).unwrap_or(Planar64Vec3::ZERO); ).unwrap_or(Planar64Vec3::ZERO);
set_position(&mut state.body,&mut state.touching,spawn_point); set_position(&mut state.body,&mut state.touching,spawn_point);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::Air);
b_refresh_walk_target=false; 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=>{ PhysicsInputInstruction::PracticeFly=>{
match &state.move_state{ match &state.move_state{
@ -1635,33 +1539,13 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::Idle=>{ PhysicsInputInstruction::Idle=>{b_refresh_walk_target=false;},//literally idle!
//literally idle!
b_refresh_walk_target=false;
},
} }
if b_refresh_walk_target{ if b_refresh_walk_target{
state.apply_input_and_body(data); state.apply_input_and_body(data);
state.cull_velocity(data,state.body.velocity); 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 Time::ZERO
),None); ),None);
} }
} }

@ -1,8 +1,6 @@
use crate::physics::{MouseState,PhysicsInputInstruction}; use crate::physics::{MouseState,PhysicsInputInstruction};
use strafesnet_common::integer::Time; use strafesnet_common::integer::Time;
use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
use strafesnet_common::timer::{Scaled,Timer,TimerState};
#[derive(Debug)] #[derive(Debug)]
pub enum InputInstruction{ pub enum InputInstruction{
MoveMouse(glam::IVec2), MoveMouse(glam::IVec2),
@ -14,8 +12,7 @@ pub enum InputInstruction{
MoveForward(bool), MoveForward(bool),
Jump(bool), Jump(bool),
Zoom(bool), Zoom(bool),
Restart, Reset,
Spawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
PracticeFly, PracticeFly,
} }
pub enum Instruction{ pub enum Instruction{
@ -24,163 +21,111 @@ pub enum Instruction{
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings), Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
GenerateModels(strafesnet_common::map::CompleteMap), GenerateModels(strafesnet_common::map::CompleteMap),
ClearModels, ClearModels,
SetPaused(bool),
//Graphics(crate::graphics_worker::Instruction), //Graphics(crate::graphics_worker::Instruction),
} }
pub struct MouseInterpolator{
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>, 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>>{
last_mouse_time:Time,//this value is pre-transformed to simulation time let mut mouse_blocking=true;
mouse_blocking:bool, let mut last_mouse_time=physics.get_next_mouse().time;
timer:Timer<Scaled>, let mut timeline=std::collections::VecDeque::new();
} crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
impl MouseInterpolator{ if if let Some(phys_input)=match &ins.instruction{
fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,m:glam::IVec2){ Instruction::Input(input_instruction)=>match input_instruction{
if self.mouse_blocking{ &InputInstruction::MoveMouse(m)=>{
//tell the game state which is living in the past about its future if mouse_blocking{
self.timeline.push_front(TimedInstruction{ //tell the game state which is living in the past about its future
time:self.last_mouse_time, timeline.push_front(TimedInstruction{
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}), 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. }else{
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero //mouse has just started moving again after being still for longer than 10ms.
self.timeline.push_front(TimedInstruction{ //replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
time:self.last_mouse_time, timeline.push_front(TimedInstruction{
instruction:PhysicsInputInstruction::ReplaceMouse( time:last_mouse_time,
MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos}, instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.timer.time(ins.time),pos:m} 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 });
self.mouse_blocking=true; //delay physics execution until we have an interpolation target
} mouse_blocking=true;
self.last_mouse_time=self.timer.time(ins.time); }
} last_mouse_time=ins.time;
/// returns the mapped physics input instruction None
/// may or may not mutate internal state XD! },
fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->Option<PhysicsInputInstruction>{ &InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
match &ins.instruction{ &InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
Instruction::Input(input_instruction)=>match input_instruction{ &InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveMouse(m)=>{ &InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)),
if !self.timer.is_paused(){ &InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)),
self.push_mouse_instruction(physics,ins,m); &InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)),
} &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)),
None &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)),
InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset),
InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly),
}, },
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle),
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle),
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle),
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), Instruction::Render=>Some(PhysicsInputInstruction::Idle),
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), }{
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), //non-mouse event
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), timeline.push_back(TimedInstruction{
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), time:ins.time,
&InputInstruction::Spawn(mode_id,stage_id)=>Some(PhysicsInputInstruction::Spawn(mode_id,stage_id)), instruction:phys_input,
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}),
}); });
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. if mouse_blocking{
self.mouse_blocking=false; //assume the mouse has stopped moving after 10ms.
true //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{ }else{
false //mouse event
true
}{
//empty queue
while let Some(instruction)=timeline.pop_front(){
physics.run_input_instruction(instruction);
}
} }
}else{ match ins.instruction{
//keep this up to date so that it can be used as a known-timestamp Instruction::Render=>{
//that the mouse was Timer<Scaled>not moving when the mouse starts moving again graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap();
self.last_mouse_time=self.timer.time(ins.time); },
true Instruction::Resize(size,user_settings)=>{
} graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
} },
/// returns whether or not to empty the instruction queue Instruction::GenerateModels(map)=>{
fn handle_physics_input(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,phys_input_option:Option<PhysicsInputInstruction>)->bool{ physics.generate_models(&map);
if let Some(phys_input)=phys_input_option{ physics.spawn();
//non-mouse event graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
self.timeline.push_back(TimedInstruction{ },
time:self.timer.time(ins.time), Instruction::ClearModels=>{
instruction:phys_input, physics.clear();
}); graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
},
//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();
},
_=>(),
}
})
}

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

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

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