Compare commits

...

10 Commits

Author SHA1 Message Date
copygirl 04c63abee2 Temporarily fix hostname resolving 1 month ago
copygirl 560d27466b Remove `on_connecting` observer 1 month ago
copygirl 93245c136e Reorganize common exports into `prelude` module 1 month ago
copygirl 765882b8b6 Delay gameplay until assets loaded 1 month ago
copygirl 058ed952fc Start local server by adding `(Linked, Started)` 1 month ago
copygirl ebd3c009fa Fix `print_certificate_digest` running on local server 1 month ago
copygirl 6d547242a0 Don't clamp movement along Y axis 1 month ago
copygirl 82716e8492 Make `Args::parse` more pretty 1 month ago
copygirl 88dcb9eea1 Fix `.meta` files not found error on web 1 month ago
copygirl 1715f51b35 Parse arguments and cleanup networking 1 month ago
  1. 191
      Cargo.lock
  2. 3
      Cargo.toml
  3. 4
      client/Cargo.toml
  4. 66
      client/src/args.rs
  5. 31
      client/src/block.rs
  6. 39
      client/src/block_assets.rs
  7. 11
      client/src/camera.rs
  8. 20
      client/src/loading.rs
  9. 112
      client/src/main.rs
  10. 4
      client/src/placement.rs
  11. 1
      common/Cargo.toml
  12. 72
      common/src/asset_loading.rs
  13. 1
      common/src/block.rs
  14. 9
      common/src/lib.rs
  15. 48
      common/src/network/client.rs
  16. 70
      common/src/network/client_webtransport.rs
  17. 3
      common/src/network/mod.rs
  18. 80
      common/src/network/server.rs
  19. 60
      common/src/network/server_webtransport.rs

191
Cargo.lock generated

@ -281,6 +281,56 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.60.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
@ -579,8 +629,10 @@ dependencies = [
"bevy",
"bevy-bloxel-classic-common",
"bevy_fix_cursor_unlock_web",
"clap",
"lightyear",
"serde",
"web-sys",
]
[[package]]
@ -590,6 +642,7 @@ dependencies = [
"bevy",
"lightyear",
"serde",
"thiserror 2.0.17",
]
[[package]]
@ -2138,6 +2191,46 @@ dependencies = [
"libloading",
]
[[package]]
name = "clap"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "cmake"
version = "0.1.54"
@ -2158,6 +2251,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "combine"
version = "4.6.7"
@ -3441,6 +3540,12 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
@ -3697,6 +3802,12 @@ dependencies = [
"mach2",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itertools"
version = "0.13.0"
@ -3880,7 +3991,7 @@ dependencies = [
[[package]]
name = "lightyear"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"bevy_app",
@ -3922,7 +4033,7 @@ dependencies = [
[[package]]
name = "lightyear_aeronet"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"bevy_app",
@ -3936,7 +4047,7 @@ dependencies = [
[[package]]
name = "lightyear_avian2d"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"avian2d",
"bevy_app",
@ -3957,7 +4068,7 @@ dependencies = [
[[package]]
name = "lightyear_avian3d"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"avian3d",
"bevy_app",
@ -3978,7 +4089,7 @@ dependencies = [
[[package]]
name = "lightyear_connection"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -3997,7 +4108,7 @@ dependencies = [
[[package]]
name = "lightyear_core"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4015,7 +4126,7 @@ dependencies = [
[[package]]
name = "lightyear_frame_interpolation"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4034,7 +4145,7 @@ dependencies = [
[[package]]
name = "lightyear_inputs"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4057,7 +4168,7 @@ dependencies = [
[[package]]
name = "lightyear_inputs_bei"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4077,7 +4188,7 @@ dependencies = [
[[package]]
name = "lightyear_inputs_leafwing"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4097,7 +4208,7 @@ dependencies = [
[[package]]
name = "lightyear_inputs_native"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4112,7 +4223,7 @@ dependencies = [
[[package]]
name = "lightyear_interpolation"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4136,7 +4247,7 @@ dependencies = [
[[package]]
name = "lightyear_link"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4152,7 +4263,7 @@ dependencies = [
[[package]]
name = "lightyear_messages"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4173,7 +4284,7 @@ dependencies = [
[[package]]
name = "lightyear_netcode"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"bevy_app",
@ -4198,7 +4309,7 @@ dependencies = [
[[package]]
name = "lightyear_prediction"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4226,28 +4337,22 @@ dependencies = [
[[package]]
name = "lightyear_raw_connection"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"bevy_app",
"bevy_ecs",
"bevy_platform",
"bevy_reflect",
"bytes",
"lightyear_connection",
"lightyear_core",
"lightyear_link",
"lightyear_serde",
"serde",
"smallvec",
"thiserror 2.0.17",
"lightyear_transport",
"tracing",
]
[[package]]
name = "lightyear_replication"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"avian2d",
"avian3d",
@ -4282,7 +4387,7 @@ dependencies = [
[[package]]
name = "lightyear_serde"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_derive",
"bevy_ecs",
@ -4302,7 +4407,7 @@ dependencies = [
[[package]]
name = "lightyear_steam"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"aeronet_steam",
@ -4319,7 +4424,7 @@ dependencies = [
[[package]]
name = "lightyear_sync"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_derive",
@ -4342,7 +4447,7 @@ dependencies = [
[[package]]
name = "lightyear_transport"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_ecs",
@ -4370,7 +4475,7 @@ dependencies = [
[[package]]
name = "lightyear_udp"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"bevy_app",
@ -4386,7 +4491,7 @@ dependencies = [
[[package]]
name = "lightyear_ui"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_app",
"bevy_color",
@ -4405,7 +4510,7 @@ dependencies = [
[[package]]
name = "lightyear_utils"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"bevy_ecs",
"bevy_platform",
@ -4420,7 +4525,7 @@ dependencies = [
[[package]]
name = "lightyear_websocket"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"aeronet_websocket",
@ -4436,7 +4541,7 @@ dependencies = [
[[package]]
name = "lightyear_webtransport"
version = "0.25.3"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=5559dd47a014040f570516983ace2c9e9a25ac89#5559dd47a014040f570516983ace2c9e9a25ac89"
source = "git+https://github.com/cBournhonesque/lightyear.git?rev=f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa#f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
dependencies = [
"aeronet_io",
"aeronet_webtransport",
@ -5220,6 +5325,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "oneshot"
version = "0.1.11"
@ -6493,6 +6604,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@ -7072,6 +7189,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.18.1"

@ -14,9 +14,10 @@ opt-level = 3
bevy = { version = "0.17.2", features = [ "serialize" ] }
# lightyear = { version = "0.25.3", features = [ "netcode", "webtransport" ] }
serde = "1.0.228"
thiserror = "2.0.17"
# TODO: Once lightyear releases a version with `raw_connection` support, switch to that.
[workspace.dependencies.lightyear]
git = "https://github.com/cBournhonesque/lightyear.git"
rev = "5559dd47a014040f570516983ace2c9e9a25ac89"
rev = "f8583d6630a72bdb8bebb4083a8f3f9cc20aecaa"
features = [ "raw_connection", "webtransport" ]

@ -1,5 +1,6 @@
[package]
name = "bevy-bloxel-classic"
description = "A multiplayer bloxel sandbox game inspired by Minecraft Classic (2009)"
version = "0.1.0"
edition = "2024"
@ -11,3 +12,6 @@ lightyear.workspace = true
serde.workspace = true
bevy_fix_cursor_unlock_web = "0.2.0"
clap = { version = "4.5.50", features = [ "derive" ] }
web-sys = { version = "0.3.81", features = [ "Location", "UrlSearchParams", "Window" ] }

@ -0,0 +1,66 @@
use bevy::ecs::resource::Resource;
use clap::{Parser, Subcommand};
use common::network::DEFAULT_PORT;
#[derive(Resource, Parser, Default, Debug)]
#[command(version, about)]
pub struct Args {
#[command(subcommand)]
pub mode: Option<Mode>,
}
#[derive(Subcommand, Debug)]
pub enum Mode {
/// Play the game on your own.
Local,
/// Host a server and play on it.
#[cfg(not(target_family = "wasm"))]
Host {
#[arg(default_value_t = DEFAULT_PORT)]
port: u16,
},
/// Connect to an existing server.
Connect {
/// Address to connect to.
address: String,
/// Certificate digest provided by the server.
digest: String,
},
}
impl Default for Mode {
fn default() -> Self {
Self::Local
}
}
impl Args {
#[cfg(not(target_family = "wasm"))]
pub fn parse() -> Self {
<Args as Parser>::parse()
}
#[cfg(target_family = "wasm")]
pub fn parse() -> Self {
use bevy::log::error;
use web_sys::{UrlSearchParams, window};
let params = window()
.and_then(|window| window.location().search().ok())
.and_then(|search| UrlSearchParams::new_with_str(&search).ok());
if let Some(params) = params {
if let Some(address) = params.get("connect") {
if let Some(digest) = params.get("digest") {
return Self {
mode: Some(Mode::Connect { address, digest }),
};
} else {
error!("Missing 'digest' parameter.");
}
}
}
Self::default()
}
}

@ -1,31 +0,0 @@
use bevy::prelude::*;
pub use common::block::*;
#[derive(Resource)]
pub struct BlockResources {
mesh: Handle<Mesh>,
material: Handle<StandardMaterial>,
}
pub fn setup_blocks(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.insert_resource(BlockResources {
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
material: materials.add(Color::srgb_u8(124, 144, 255)),
});
}
pub fn add_block_visuals(
add: On<Add, Block>,
mut commands: Commands,
resources: Res<BlockResources>,
) {
commands.entity(add.entity).insert((
Mesh3d(resources.mesh.clone()),
MeshMaterial3d(resources.material.clone()),
));
}

@ -0,0 +1,39 @@
use bevy::prelude::*;
use common::prelude::*;
pub fn plugin(app: &mut App) {
app.add_observer(insert_block_visuals);
app.load_resource::<BlockAssets>();
}
#[derive(Resource, Asset, Reflect, Clone)]
#[reflect(Resource)]
pub struct BlockAssets {
#[dependency]
mesh: Handle<Mesh>,
#[dependency]
material: Handle<StandardMaterial>,
}
impl FromWorld for BlockAssets {
fn from_world(world: &mut World) -> Self {
let assets = world.resource::<AssetServer>();
Self {
mesh: assets.add(Cuboid::new(1.0, 1.0, 1.0).into()),
material: assets.add(Color::srgb_u8(124, 144, 255).into()),
}
}
}
/// Observer which automatically inserts block visuals (mesh and
/// material) when an entity has the `Block` component added to it.
fn insert_block_visuals(
add: On<Add, Block>,
mut commands: Commands,
block_assets: Res<BlockAssets>,
) {
commands.entity(add.entity).insert((
Mesh3d(block_assets.mesh.clone()),
MeshMaterial3d(block_assets.material.clone()),
));
}

@ -1,7 +1,8 @@
use std::f32::consts::TAU;
use bevy::input::mouse::AccumulatedMouseMotion;
use bevy::prelude::*;
use bevy::input::mouse::AccumulatedMouseMotion;
use bevy::window::{CursorGrabMode, CursorOptions};
pub fn cursor_grab(
@ -43,7 +44,7 @@ pub fn cursor_grab(
// On the web, the cursor can be locked, but setting its position is
// not supported at all, so this would instead log a bunch of errors.
let center = window.resolution.size() / 2.;
#[cfg(not(target_arch = "wasm32"))] // skip on web
#[cfg(not(target_family = "wasm"))] // skip on web
window.set_cursor_position(Some(center));
}
@ -157,9 +158,11 @@ pub fn noclip_controller(
if key_input.pressed(noclip.key_back ) { movement.z -= 1.0; }
if key_input.pressed(noclip.key_right ) { movement.x += 1.0; }
if key_input.pressed(noclip.key_left ) { movement.x -= 1.0; }
movement = movement.clamp_length_max(1.0);
// Movement along the Y (up/down) axis shouldn't be clamped.
if key_input.pressed(noclip.key_up ) { movement.y += 1.0; }
if key_input.pressed(noclip.key_down ) { movement.y -= 1.0; }
movement.clamp_length_max(1.0) * noclip.speed
if key_input.pressed(noclip.key_down) { movement.y -= 1.0; }
movement * noclip.speed
};
if movement != Vec3::ZERO {

@ -0,0 +1,20 @@
use bevy::prelude::*;
use common::asset_loading::ResourceHandles;
use crate::Screen;
pub fn plugin(app: &mut App) {
app.add_systems(
Update,
enter_gameplay_screen.run_if(in_state(Screen::Loading).and(all_assets_loaded)),
);
}
fn enter_gameplay_screen(mut next_screen: ResMut<NextState<Screen>>) {
next_screen.set(Screen::Gameplay);
}
fn all_assets_loaded(resource_handles: Res<ResourceHandles>) -> bool {
resource_handles.is_all_done()
}

@ -1,20 +1,33 @@
use bevy::prelude::*;
use common::prelude::network::*;
use common::prelude::*;
use bevy::asset::AssetMetaCheck;
use bevy::window::WindowResolution;
use lightyear::prelude::server::Started;
mod block;
mod args;
mod block_assets;
mod camera;
mod loading;
mod placement;
use block::*;
use args::*;
use camera::*;
use placement::*;
#[rustfmt::skip]
fn main() {
#[derive(States, Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Screen {
Loading,
Gameplay,
}
fn main() -> Result {
let mut app = App::new();
app.add_plugins(DefaultPlugins.set(WindowPlugin {
app.add_plugins(
DefaultPlugins
.set(WindowPlugin {
primary_window: Some(Window {
title: "bevy-bloxel-classic".into(),
// Steam Deck: DPI appears pretty high, causing everything to be scaled up.
@ -27,37 +40,62 @@ fn main() {
..default()
}),
..default()
}));
})
.set(AssetPlugin {
// WEB: Don't check for `.meta` files since we don't use them.
meta_check: AssetMetaCheck::Never,
..default()
}),
);
// Fixes issue on web where the cursor isn't ungrabbed properly.
app.add_plugins(bevy_fix_cursor_unlock_web::FixPointerUnlockPlugin);
app.add_plugins((
bevy_fix_cursor_unlock_web::FixPointerUnlockPlugin,
ServerPlugin,
ClientPlugin,
common::asset_loading::plugin,
block_assets::plugin,
loading::plugin,
));
app.insert_resource(Args::parse());
app.insert_state(Screen::Loading);
app.add_systems(Startup, setup_crosshair);
app.add_systems(Startup, setup_blocks);
app.add_systems(Startup, setup_scene.after(setup_blocks));
app.add_systems(
OnEnter(Screen::Gameplay),
(setup_crosshair, setup_scene, start_server_or_connect),
);
app.add_systems(Update, cursor_grab);
app.add_systems(Update, update_crosshair_visibility.after(cursor_grab));
app.add_systems(Update, camera_look.after(cursor_grab).run_if(is_cursor_grabbed));
app.add_systems(Update, noclip_controller.after(camera_look).run_if(is_cursor_grabbed));
// TODO: Create and configure `SystemSet`s for gameplay, input, etc.
app.add_systems(
Update,
(
cursor_grab,
update_crosshair_visibility.after(cursor_grab),
camera_look.after(cursor_grab).run_if(is_cursor_grabbed),
noclip_controller
.after(camera_look)
.run_if(is_cursor_grabbed),
)
.run_if(in_state(Screen::Gameplay)),
);
// `place_break_blocks` requires the camera's `GlobalTransform`.
// For a most up-to-date value, run it after that's been updated.
app.add_systems(PostUpdate, place_break_blocks.after(TransformSystems::Propagate));
app.add_observer(add_block_visuals);
app.add_systems(
PostUpdate,
place_break_blocks
.after(TransformSystems::Propagate)
.run_if(in_state(Screen::Gameplay).and(is_cursor_grabbed)),
);
// FIXME: Don't hardcode this!
#[cfg(not(target_arch = "wasm32"))]
app.add_plugins(common::network::ServerPlugin);
#[cfg(target_arch = "wasm32")]
app.add_plugins(common::network::ClientPlugin);
// TODO: Move this to a more general world generation module.
app.add_observer(spawn_initial_blocks);
app.run();
Ok(())
}
fn setup_scene(mut commands: Commands, mut blocks: Blocks) {
// light
fn setup_scene(mut commands: Commands) {
commands.spawn((
PointLight {
shadows_enabled: true,
@ -66,18 +104,38 @@ fn setup_scene(mut commands: Commands, mut blocks: Blocks) {
Transform::from_xyz(4.0, 8.0, 4.0),
));
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
CameraFreeLook::default(),
CameraNoClip::default(),
));
}
// blocks
fn spawn_initial_blocks(_event: On<Add, Started>, mut blocks: Blocks) {
for x in -8..8 {
for z in -8..8 {
blocks.spawn(IVec3::new(x, 0, z));
}
}
}
fn start_server_or_connect(args: Res<Args>, mut commands: Commands) -> Result {
let mode = args.mode.as_ref();
let default = Mode::default();
match mode.unwrap_or(&default) {
Mode::Local => {
commands.queue(StartLocalServerCommand);
}
#[cfg(not(target_family = "wasm"))]
Mode::Host { port } => {
commands.queue(StartWebTransportServerCommand::new(*port));
}
Mode::Connect { address, digest } => {
commands.queue(ConnectWebTransportCommand::new(&address, digest.clone())?);
}
}
// NOTE: When setting up a local server, a host-client is automatically
// connected to it thanks to the `autoconnect_host_client` observer.
Ok(())
}

@ -1,7 +1,9 @@
use bevy::prelude::*;
use common::prelude::*;
use bevy::window::{CursorGrabMode, CursorOptions};
use crate::block::*;
// TODO: Use picking system instead of manually raycasting.
pub fn place_break_blocks(
mut commands: Commands,

@ -7,3 +7,4 @@ edition = "2024"
bevy.workspace = true
lightyear.workspace = true
serde.workspace = true
thiserror.workspace = true

@ -0,0 +1,72 @@
//! A high-level way to load collections of asset handles as resources.
// Taken from: https://github.com/TheBevyFlock/bevy_new_2d/blob/main/src/asset_tracking.rs
use std::collections::VecDeque;
use bevy::prelude::*;
pub fn plugin(app: &mut App) {
app.init_resource::<ResourceHandles>();
app.add_systems(PreUpdate, load_resource_assets);
}
pub trait LoadResource {
/// This will load the [`Resource`] as an [`Asset`]. When all of its asset dependencies
/// have been loaded, it will be inserted as a resource. This ensures that the resource only
/// exists when the assets are ready.
fn load_resource<T: Resource + Asset + Clone + FromWorld>(&mut self) -> &mut Self;
}
impl LoadResource for App {
fn load_resource<T: Resource + Asset + Clone + FromWorld>(&mut self) -> &mut Self {
self.init_asset::<T>();
let world = self.world_mut();
let value = T::from_world(world);
let assets = world.resource::<AssetServer>();
let handle = assets.add(value);
let mut handles = world.resource_mut::<ResourceHandles>();
handles
.waiting
.push_back((handle.untyped(), |world, handle| {
let assets = world.resource::<Assets<T>>();
if let Some(value) = assets.get(handle.id().typed::<T>()) {
world.insert_resource(value.clone());
}
}));
self
}
}
/// A function that inserts a loaded resource.
type InsertLoadedResource = fn(&mut World, &UntypedHandle);
#[derive(Resource, Default)]
pub struct ResourceHandles {
// Use a queue for waiting assets so they can be cycled through and moved to
// `finished` one at a time.
waiting: VecDeque<(UntypedHandle, InsertLoadedResource)>,
finished: Vec<UntypedHandle>,
}
impl ResourceHandles {
/// Returns true if all requested [`Asset`]s have finished loading and are available as [`Resource`]s.
pub fn is_all_done(&self) -> bool {
self.waiting.is_empty()
}
}
fn load_resource_assets(world: &mut World) {
world.resource_scope(|world, mut resource_handles: Mut<ResourceHandles>| {
world.resource_scope(|world, assets: Mut<AssetServer>| {
for _ in 0..resource_handles.waiting.len() {
let (handle, insert_fn) = resource_handles.waiting.pop_front().unwrap();
if assets.is_loaded_with_dependencies(&handle) {
insert_fn(world, &handle);
resource_handles.finished.push(handle);
} else {
resource_handles.waiting.push_back((handle, insert_fn));
}
}
});
});
}

@ -18,7 +18,6 @@ impl Blocks<'_, '_> {
self.commands.spawn((
Block,
Transform::from_translation(pos.as_vec3() + Vec3::ONE / 2.),
// Currently prints some warnings when no `Server` is active, but this is fine.
Replicate::to_clients(NetworkTarget::All),
));
}

@ -1,2 +1,11 @@
pub mod asset_loading;
pub mod block;
pub mod network;
// This is mostly just re-exportings things for now, but in the future
// we might want to limit what gets exposed by the `prelude` module.
pub mod prelude {
pub use super::asset_loading::LoadResource;
pub use super::block::*;
pub use super::network;
}

@ -1,63 +1,51 @@
use std::net::{Ipv4Addr, SocketAddr};
use bevy::prelude::*;
use lightyear::prelude::client::*;
use lightyear::prelude::*;
// FIXME: Don't hardcode this!
pub const DIGEST: &'static str = "";
pub use super::client_webtransport::ConnectWebTransportCommand;
pub struct ClientPlugin;
impl Plugin for ClientPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ClientPlugins::default());
// This maybe should be added by `ClientPlugins` but it currently isn't.
// (Unless we're using `NetcodeClientPlugin`, which would've added it.)
if !app.is_plugin_added::<lightyear::connection::client::ConnectionPlugin>() {
app.add_plugins(lightyear::connection::client::ConnectionPlugin);
}
if !app.is_plugin_added::<super::ProtocolPlugin>() {
app.add_plugins(super::ProtocolPlugin);
}
app.add_systems(Startup, connect_to_server);
app.add_observer(on_connecting);
app.add_observer(on_connected);
app.add_observer(on_disconnected);
app.add_observer(autoconnect_host_client);
}
}
fn connect_to_server(mut commands: Commands) {
let client_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0);
let server_addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), super::DEFAULT_PORT);
let certificate_digest = DIGEST.to_string();
/// Automatically creates a "host client" to connect to the local server when it is started.
fn autoconnect_host_client(event: On<Add, server::Started>, mut commands: Commands) {
let server = event.entity;
commands
.spawn((
Name::from("Client"),
LocalAddr(client_addr),
PeerAddr(server_addr),
Client::default(),
Name::from("HostClient"),
ReplicationReceiver::default(),
WebTransportClientIo { certificate_digest },
RawClient,
LinkOf { server },
))
.trigger(|entity| LinkStart { entity });
}
fn on_connecting(event: On<Add, Connecting>) {
let client = event.entity;
info!("Client '{client}' connecting ...");
.trigger(|entity| Connect { entity });
}
fn on_connected(event: On<Add, Connected>) {
fn on_connected(event: On<Add, Connected>, clients: Query<(), With<Client>>) {
let client = event.entity;
if !clients.contains(client) {
return; // Not a client we started. (server-side?)
};
info!("Client '{client}' connected!");
}
fn on_disconnected(event: On<Remove, Connected>) {
fn on_disconnected(event: On<Remove, Connected>, clients: Query<(), With<Client>>) {
let client = event.entity;
if !clients.contains(client) {
return; // Not a client we started. (server-side?)
};
info!("Client '{client}' disconnected!");
}

@ -0,0 +1,70 @@
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use bevy::prelude::*;
use lightyear::prelude::client::*;
use lightyear::prelude::*;
use crate::network::DEFAULT_PORT;
pub struct ConnectWebTransportCommand {
server_addr: SocketAddr,
certificate_digest: String,
}
impl ConnectWebTransportCommand {
pub fn new(server_addr: &str, certificate_digest: String) -> Result<Self> {
Ok(Self {
server_addr: resolve_addr(&server_addr, DEFAULT_PORT)?,
certificate_digest,
})
}
}
impl Command for ConnectWebTransportCommand {
fn apply(self, world: &mut World) -> () {
let client_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0);
let certificate_digest = self.certificate_digest;
world
.spawn((
Client::default(),
Name::from("Client"),
LocalAddr(client_addr),
PeerAddr(self.server_addr),
ReplicationReceiver::default(),
WebTransportClientIo { certificate_digest },
RawClient,
))
.trigger(|entity| Connect { entity });
}
}
// TODO: Move this to its own `utils` module?
// FIXME: Hostname resolving does not work on web.
fn resolve_addr(addr: &str, default_port: u16) -> Result<SocketAddr> {
let addr = with_default_port(addr, default_port);
let socket_addrs = addr.to_socket_addrs()?.collect::<Vec<_>>();
if let Some(ipv4) = socket_addrs.iter().find(|a| a.is_ipv4()) {
Ok(*ipv4) // Prefer first IPv4 address found..
} else if let Some(ipv6) = socket_addrs.first() {
Ok(*ipv6) // ..otherwise fallback to IPv6.
} else {
Err(std::io::Error::other("hostname could not be resolved").into())
}
}
fn with_default_port(addr: &str, default_port: u16) -> String {
if is_port_specified(addr) {
addr.to_string()
} else {
format!("{addr}:{default_port}")
}
}
fn is_port_specified(addr: &str) -> bool {
match (addr.rfind(']'), addr.rfind(':')) {
// This doesn't match colons within IPv6 brackets, like `[::1]`.
(Some(bracket), Some(colon)) if bracket < colon => true,
(None, Some(_)) => true,
_ => false,
}
}

@ -2,6 +2,9 @@ mod client;
mod protocol;
mod server;
mod client_webtransport;
mod server_webtransport;
pub use client::*;
pub use protocol::*;
pub use server::*;

@ -1,67 +1,49 @@
use std::net::{Ipv4Addr, SocketAddr};
use bevy::prelude::*;
use lightyear::prelude::server::*;
use lightyear::prelude::*;
#[cfg(not(target_family = "wasm"))]
pub use super::server_webtransport::StartWebTransportServerCommand;
pub struct ServerPlugin;
impl Plugin for ServerPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ServerPlugins::default());
// These maybe should be added by `ServerPlugins` but currently aren't.
// (Unless we're using `NetcodeServerPlugin`, which would've added it.)
if !app.is_plugin_added::<lightyear::connection::client::ConnectionPlugin>() {
app.add_plugins(lightyear::connection::client::ConnectionPlugin);
}
if !app.is_plugin_added::<lightyear::connection::server::ConnectionPlugin>() {
app.add_plugins(lightyear::connection::server::ConnectionPlugin);
}
if !app.is_plugin_added::<super::ProtocolPlugin>() {
app.add_plugins(super::ProtocolPlugin);
}
app.add_systems(Startup, start_server);
// TODO: See what happens when we try to start a server, but it
// can't, for example due to the port already being in use.
app.add_observer(on_server_started);
app.add_observer(on_server_stopped);
app.add_observer(handle_client_connected);
app.add_observer(handle_client_disconnected);
#[cfg(not(target_family = "wasm"))]
app.add_observer(super::server_webtransport::print_certificate_digest);
}
}
fn start_server(mut commands: Commands) -> Result {
let server_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), super::DEFAULT_PORT);
let certificate = Identity::self_signed(["localhost", "127.0.0.1", "::1"])?;
commands
.spawn((
Name::from("Server"),
LocalAddr(server_addr),
WebTransportServerIo { certificate },
RawServer,
))
.trigger(|entity| LinkStart { entity });
Ok(())
/// Starts a local-only server that only a host-client can connect to.
pub struct StartLocalServerCommand;
impl Command for StartLocalServerCommand {
fn apply(self, world: &mut World) {
world.spawn((
Server::default(),
Name::from("LocalServer"),
// NOTE: Inserting both makes the local-only server act properly.
(Linked, Started), // yeah it's started, alright
));
}
}
fn on_server_started(event: On<Add, Started>, servers: Query<&WebTransportServerIo>) -> Result {
fn on_server_started(event: On<Add, Started>) {
let server = event.entity;
info!("Server '{server}' started!");
let certificate = &servers.get(server)?.certificate;
let certificate_hash = certificate.certificate_chain().as_slice()[0].hash();
let certificate_digest = certificate_hash.to_string().replace(':', "");
info!("== Certificate Digest ==");
info!(" Clients use this to securely connect to the server.");
info!(" {certificate_digest}");
Ok(())
}
fn on_server_stopped(event: On<Add, Stopped>) {
@ -73,19 +55,29 @@ fn handle_client_connected(
event: On<Add, Connected>,
clients: Query<&LinkOf>,
mut commands: Commands,
) -> Result {
) {
let client = event.entity;
let server = clients.get(client)?.server;
let Ok(LinkOf { server }) = clients.get(client) else {
return; // Not a client of the server. (client-side?)
};
info!("Client '{client}' connected to server '{server}'");
commands.entity(client).insert(ReplicationSender::default());
commands
.entity(client)
.insert_if_new(Name::from("RemoteClient"))
.insert(ReplicationSender::default());
Ok(())
// TODO: Spawn player entity.
}
fn handle_client_disconnected(event: On<Add, Disconnected>, clients: Query<&LinkOf>) -> Result {
fn handle_client_disconnected(event: On<Remove, Connected>, clients: Query<&LinkOf>) {
let client = event.entity;
let server = clients.get(client)?.server;
let Ok(LinkOf { server }) = clients.get(client) else {
return; // Not a client of the server. (client-side?)
};
info!("Client '{client}' disconnected from server '{server}'");
Ok(())
// TODO: Despawn player entity.
}

@ -0,0 +1,60 @@
#![cfg(not(target_family = "wasm"))]
use std::net::{Ipv4Addr, SocketAddr};
use bevy::prelude::*;
use lightyear::prelude::server::*;
use lightyear::prelude::*;
pub struct StartWebTransportServerCommand {
server_addr: SocketAddr,
certificate: Identity,
}
impl StartWebTransportServerCommand {
pub fn new(port: u16) -> StartWebTransportServerCommand {
Self {
// TODO: Allow specifying this?
server_addr: SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port),
certificate: Identity::self_signed(["localhost", "127.0.0.1", "::1"]).unwrap(),
}
}
}
impl Default for StartWebTransportServerCommand {
fn default() -> Self {
Self::new(super::DEFAULT_PORT)
}
}
impl Command for StartWebTransportServerCommand {
fn apply(self, world: &mut World) {
world
.spawn((
Server::default(),
Name::from("Server"),
LocalAddr(self.server_addr),
WebTransportServerIo {
certificate: self.certificate,
},
RawServer,
))
.trigger(|entity| Start { entity });
}
}
pub(super) fn print_certificate_digest(
event: On<Add, WebTransportServerIo>,
servers: Query<&WebTransportServerIo>,
) {
let server = event.entity;
// SAFETY: Event guarantees the component exists on the entity.
let certificate = &servers.get(server).unwrap().certificate;
let certificate_hash = &certificate.certificate_chain().as_slice()[0].hash();
let certificate_digest = certificate_hash.to_string().replace(':', "");
info!("== Certificate Digest ==");
info!(" {certificate_digest}");
info!(" (Clients use this to securely connect to the server.)");
}
Loading…
Cancel
Save