]> git.lizzy.rs Git - mt_client.git/commitdiff
Basic map rendering
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Tue, 28 Feb 2023 17:14:06 +0000 (18:14 +0100)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Tue, 28 Feb 2023 17:14:06 +0000 (18:14 +0100)
96 files changed:
Cargo.lock
Cargo.toml
assets/ascii-art.txt [new file with mode: 0644]
assets/shaders/map.wgsl [new file with mode: 0644]
assets/textures/air.png [new file with mode: 0644]
assets/textures/aux1_btn.png [new file with mode: 0644]
assets/textures/blank.png [new file with mode: 0644]
assets/textures/bubble.png [new file with mode: 0644]
assets/textures/bubble_gone.png [new file with mode: 0644]
assets/textures/camera_btn.png [new file with mode: 0644]
assets/textures/cdb_add.png [new file with mode: 0644]
assets/textures/cdb_clear.png [new file with mode: 0644]
assets/textures/cdb_downloading.png [new file with mode: 0644]
assets/textures/cdb_queued.png [new file with mode: 0644]
assets/textures/cdb_update.png [new file with mode: 0644]
assets/textures/cdb_viewonline.png [new file with mode: 0644]
assets/textures/chat_btn.png [new file with mode: 0644]
assets/textures/chat_hide_btn.png [new file with mode: 0644]
assets/textures/chat_show_btn.png [new file with mode: 0644]
assets/textures/checkbox_16.png [new file with mode: 0644]
assets/textures/checkbox_16_white.png [new file with mode: 0644]
assets/textures/checkbox_32.png [new file with mode: 0644]
assets/textures/checkbox_64.png [new file with mode: 0644]
assets/textures/clear.png [new file with mode: 0644]
assets/textures/crack_anylength.png [new file with mode: 0644]
assets/textures/debug_btn.png [new file with mode: 0644]
assets/textures/down.png [new file with mode: 0644]
assets/textures/drop_btn.png [new file with mode: 0644]
assets/textures/end_icon.png [new file with mode: 0644]
assets/textures/error_icon_orange.png [new file with mode: 0644]
assets/textures/error_icon_red.png [new file with mode: 0644]
assets/textures/error_screenshot.png [new file with mode: 0644]
assets/textures/fast_btn.png [new file with mode: 0644]
assets/textures/fly_btn.png [new file with mode: 0644]
assets/textures/gear_icon.png [new file with mode: 0644]
assets/textures/halo.png [new file with mode: 0644]
assets/textures/heart.png [new file with mode: 0644]
assets/textures/heart_gone.png [new file with mode: 0644]
assets/textures/ignore.png [new file with mode: 0644]
assets/textures/inventory_btn.png [new file with mode: 0644]
assets/textures/joystick_bg.png [new file with mode: 0644]
assets/textures/joystick_center.png [new file with mode: 0644]
assets/textures/joystick_off.png [new file with mode: 0644]
assets/textures/jump_btn.png [new file with mode: 0644]
assets/textures/loading_screenshot.png [new file with mode: 0644]
assets/textures/logo.png [new file with mode: 0644]
assets/textures/menu_bg.png [new file with mode: 0644]
assets/textures/menu_header.png [new file with mode: 0644]
assets/textures/minimap_btn.png [new file with mode: 0644]
assets/textures/minimap_mask_round.png [new file with mode: 0644]
assets/textures/minimap_mask_square.png [new file with mode: 0644]
assets/textures/minimap_overlay_round.png [new file with mode: 0644]
assets/textures/minimap_overlay_square.png [new file with mode: 0644]
assets/textures/next_icon.png [new file with mode: 0644]
assets/textures/no_screenshot.png [new file with mode: 0644]
assets/textures/no_texture.png [new file with mode: 0644]
assets/textures/no_texture_airlike.png [new file with mode: 0644]
assets/textures/noclip_btn.png [new file with mode: 0644]
assets/textures/object_marker_red.png [new file with mode: 0644]
assets/textures/player.png [new file with mode: 0644]
assets/textures/player_back.png [new file with mode: 0644]
assets/textures/player_marker.png [new file with mode: 0644]
assets/textures/plus.png [new file with mode: 0644]
assets/textures/prev_icon.png [new file with mode: 0644]
assets/textures/progress_bar.png [new file with mode: 0644]
assets/textures/progress_bar_bg.png [new file with mode: 0644]
assets/textures/rangeview_btn.png [new file with mode: 0644]
assets/textures/rare_controls.png [new file with mode: 0644]
assets/textures/refresh.png [new file with mode: 0644]
assets/textures/search.png [new file with mode: 0644]
assets/textures/server_favorite.png [new file with mode: 0644]
assets/textures/server_favorite_delete.png [new file with mode: 0644]
assets/textures/server_flags_creative.png [new file with mode: 0644]
assets/textures/server_flags_damage.png [new file with mode: 0644]
assets/textures/server_flags_pvp.png [new file with mode: 0644]
assets/textures/server_incompatible.png [new file with mode: 0644]
assets/textures/server_ping_1.png [new file with mode: 0644]
assets/textures/server_ping_2.png [new file with mode: 0644]
assets/textures/server_ping_3.png [new file with mode: 0644]
assets/textures/server_ping_4.png [new file with mode: 0644]
assets/textures/server_public.png [new file with mode: 0644]
assets/textures/smoke_puff.png [new file with mode: 0644]
assets/textures/start_icon.png [new file with mode: 0644]
assets/textures/sunrisebg.png [new file with mode: 0644]
assets/textures/unknown_item.png [new file with mode: 0644]
assets/textures/unknown_node.png [new file with mode: 0644]
assets/textures/unknown_object.png [new file with mode: 0644]
assets/textures/wieldhand.png [new file with mode: 0644]
assets/textures/zoom.png [new file with mode: 0644]
src/gfx.rs [new file with mode: 0644]
src/gfx/map.rs [new file with mode: 0644]
src/gfx/media.rs [new file with mode: 0644]
src/gfx/state.rs [new file with mode: 0644]
src/gfx/util.rs [new file with mode: 0644]
src/main.rs
src/net.rs [new file with mode: 0644]

index 43e621c6a2991fa66a21ec37e0e63a0bc2690dac..907a3751c516842de0bc2e8c1260ca351226cf97 100644 (file)
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "ab_glyph"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe21446ad43aa56417a767f3e2f3d7c4ca522904de1dd640529a76e9c5c3b33c"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
+[[package]]
+name = "addr2line"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+dependencies = [
+ "gimli",
+]
+
 [[package]]
 name = "adler"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "android-activity"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6"
+dependencies = [
+ "android-properties",
+ "bitflags",
+ "cc",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "approx"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+
+[[package]]
+name = "ash"
+version = "0.37.2+1.3.238"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03"
+dependencies = [
+ "libloading",
+]
+
 [[package]]
 name = "async-recursion"
 version = "1.0.2"
@@ -30,12 +129,63 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "autocfg"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
+dependencies = [
+ "autocfg 1.1.0",
+]
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "backtrace"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
 [[package]]
 name = "block-buffer"
 version = "0.10.3"
@@ -45,12 +195,70 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "block-sys"
+version = "0.1.0-beta.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "block2"
+version = "0.2.0-alpha.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42"
+dependencies = [
+ "block-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
+[[package]]
+name = "bytemuck"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "byteorder"
 version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
+[[package]]
+name = "calloop"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192"
+dependencies = [
+ "log",
+ "nix 0.25.1",
+ "slotmap",
+ "thiserror",
+ "vec_map",
+]
+
 [[package]]
 name = "cc"
 version = "1.0.79"
@@ -66,6 +274,69 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cgmath"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
+dependencies = [
+ "approx",
+ "num-traits",
+ "rand 0.6.5",
+ "serde",
+]
+
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "collision"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6107f6be76c2269a9c8d89e707a66122bd3086f987fa508133a5f774e8ac4ced"
+dependencies = [
+ "approx",
+ "bit-set",
+ "cgmath",
+ "num",
+ "rand 0.6.5",
+ "smallvec 0.6.14",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "com-rs"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642"
+
 [[package]]
 name = "convert_case"
 version = "0.6.0"
@@ -75,6 +346,47 @@ dependencies = [
  "unicode-segmentation",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "core-graphics"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "foreign-types",
+ "libc",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.5"
@@ -103,6 +415,23 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "cty"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+
+[[package]]
+name = "d3d12"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da"
+dependencies = [
+ "bitflags",
+ "libloading",
+ "winapi",
+]
+
 [[package]]
 name = "darling"
 version = "0.14.3"
@@ -160,10 +489,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "drop_bomb"
-version = "0.1.5"
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dlib"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 
 [[package]]
 name = "enumset"
@@ -184,6 +528,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "euclid"
+version = "0.22.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "flate2"
 version = "1.0.25"
@@ -202,89 +555,390 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
-name = "generic-array"
-version = "0.14.6"
+name = "foreign-types"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 dependencies = [
- "typenum",
- "version_check",
+ "foreign-types-shared",
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.2.8"
+name = "foreign-types-shared"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "fps-camera"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60c7ada020a020615930e1faeb0a4106b1d327bad75498ae06a1ea9063b1e9b3"
 dependencies = [
- "cfg-if",
- "libc",
- "wasi",
+ "bitflags",
+ "piston3d-cam",
+ "vecmath",
 ]
 
 [[package]]
-name = "hashbrown"
-version = "0.12.3"
+name = "fuchsia-cprng"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 
 [[package]]
-name = "hermit-abi"
-version = "0.2.6"
+name = "futures"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
 dependencies = [
- "libc",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
 ]
 
 [[package]]
-name = "hydra"
-version = "0.1.0"
+name = "futures-channel"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
 dependencies = [
- "mt_net",
- "rand",
- "sha2",
- "srp",
- "tokio",
+ "futures-core",
+ "futures-sink",
 ]
 
 [[package]]
-name = "ident_case"
-version = "1.0.1"
+name = "futures-core"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
 
 [[package]]
-name = "indexmap"
-version = "1.9.2"
+name = "futures-executor"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
 dependencies = [
- "autocfg",
- "hashbrown",
+ "futures-core",
+ "futures-task",
+ "futures-util",
 ]
 
 [[package]]
-name = "jobserver"
-version = "0.1.25"
+name = "futures-io"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
 dependencies = [
- "libc",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "lazy_static"
-version = "1.4.0"
+name = "futures-sink"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
 
 [[package]]
-name = "libc"
-version = "0.2.139"
+name = "futures-task"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
+
+[[package]]
+name = "futures-util"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+
+[[package]]
+name = "glow"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
+dependencies = [
+ "bitflags",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8"
+dependencies = [
+ "backtrace",
+ "log",
+ "thiserror",
+ "winapi",
+ "windows",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
+dependencies = [
+ "bitflags",
+ "gpu-descriptor-types",
+ "hashbrown",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "guillotiere"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hassle-rs"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85"
+dependencies = [
+ "bitflags",
+ "com-rs",
+ "libc",
+ "libloading",
+ "thiserror",
+ "widestring",
+ "winapi",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "image"
+version = "0.24.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "jpeg-decoder",
+ "num-rational 0.4.1",
+ "num-traits",
+ "png",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg 1.1.0",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
+dependencies = [
+ "libc",
+ "libloading",
+ "pkg-config",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lerp"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b19ac50d419bff1a024a8eb8034bfbee6c1cf8fa3472502f6bd3def327b09e4c"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.139"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
 
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
 [[package]]
 name = "libz-sys"
 version = "1.1.8"
@@ -296,6 +950,16 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg 1.1.0",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.17"
@@ -305,12 +969,65 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+
 [[package]]
 name = "memchr"
 version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
+[[package]]
+name = "memmap2"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af2c65375e552a67fe3829ca63e8a7c27a378a62824594f43b2851d682b5ec2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg 1.1.0",
+]
+
+[[package]]
+name = "metal"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
 [[package]]
 name = "miniz_oxide"
 version = "0.6.2"
@@ -332,11 +1049,36 @@ dependencies = [
  "windows-sys 0.45.0",
 ]
 
+[[package]]
+name = "mt_client"
+version = "0.1.0"
+dependencies = [
+ "bytemuck",
+ "cgmath",
+ "fps-camera",
+ "futures",
+ "guillotiere",
+ "image",
+ "lerp",
+ "mt_net",
+ "rand 0.8.5",
+ "rust-embed",
+ "sha2",
+ "srp",
+ "threadpool",
+ "tokio",
+ "wgpu",
+ "winit",
+]
+
 [[package]]
 name = "mt_net"
 version = "0.1.0"
-source = "git+https://github.com/minetest-rust/mt_net#c545faea8fddd6bd4707dd7e67c83bf7ddd297cb"
+source = "git+https://github.com/minetest-rust/mt_net#85d55e42119ea80cd1cd9e9e34c05ea7d07b3a88"
 dependencies = [
+ "async-trait",
+ "cgmath",
+ "collision",
  "delegate",
  "enumset",
  "mt_rudp",
@@ -347,13 +1089,11 @@ dependencies = [
 [[package]]
 name = "mt_rudp"
 version = "0.1.0"
-source = "git+https://github.com/minetest-rust/mt_rudp#88ff69e7a8e90a4fa3929fa173ed93e99b60f37c"
+source = "git+https://github.com/minetest-rust/mt_rudp#feb966ec55874037a8371acb018cd739a42efd4f"
 dependencies = [
  "async-recursion",
  "async-trait",
  "byteorder",
- "delegate",
- "drop_bomb",
  "num_enum",
  "thiserror",
  "tokio",
@@ -362,9 +1102,11 @@ dependencies = [
 [[package]]
 name = "mt_ser"
 version = "0.1.0"
-source = "git+https://github.com/minetest-rust/mt_ser#3561472c60acfeda2dadb477a1da4afd287ab30a"
+source = "git+https://github.com/minetest-rust/mt_ser#5baa62aea9af9ba7cc74823d07250fe772518abc"
 dependencies = [
  "byteorder",
+ "cgmath",
+ "collision",
  "enumset",
  "flate2",
  "mt_ser_derive",
@@ -376,7 +1118,7 @@ dependencies = [
 [[package]]
 name = "mt_ser_derive"
 version = "0.1.0"
-source = "git+https://github.com/minetest-rust/mt_ser#3561472c60acfeda2dadb477a1da4afd287ab30a"
+source = "git+https://github.com/minetest-rust/mt_ser#5baa62aea9af9ba7cc74823d07250fe772518abc"
 dependencies = [
  "convert_case",
  "darling",
@@ -386,32 +1128,188 @@ dependencies = [
 ]
 
 [[package]]
-name = "nom8"
-version = "0.2.0"
+name = "naga"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ndk"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
+dependencies = [
+ "bitflags",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.4.1+23.1.7779620"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "nix"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+dependencies = [
+ "autocfg 1.1.0",
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "num"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
+dependencies = [
+ "num-bigint 0.2.6",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational 0.2.4",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-integer",
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
 dependencies = [
- "memchr",
+ "autocfg 1.1.0",
+ "num-integer",
+ "num-traits",
 ]
 
 [[package]]
-name = "num-bigint"
-version = "0.4.3"
+name = "num-rational"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
 dependencies = [
- "autocfg",
+ "autocfg 1.1.0",
+ "num-bigint 0.2.6",
  "num-integer",
  "num-traits",
+ "serde",
 ]
 
 [[package]]
-name = "num-integer"
-version = "0.1.45"
+name = "num-rational"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
 dependencies = [
- "autocfg",
+ "autocfg 1.1.0",
+ "num-integer",
  "num-traits",
 ]
 
@@ -421,7 +1319,7 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
- "autocfg",
+ "autocfg 1.1.0",
 ]
 
 [[package]]
@@ -436,18 +1334,18 @@ dependencies = [
 
 [[package]]
 name = "num_enum"
-version = "0.5.9"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
+checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e"
 dependencies = [
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.9"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
+checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -455,30 +1353,168 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.2.0-beta.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
+
+[[package]]
+name = "objc2"
+version = "0.3.0-beta.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe31e5425d3d0b89a15982c024392815da40689aceb34bad364d58732bcfd649"
+dependencies = [
+ "block2",
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "2.0.0-pre.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "object"
+version = "0.30.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "once_cell"
 version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
+[[package]]
+name = "orbclient"
+version = "0.3.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba683f1641c11041c59d5d93689187abcab3c1349dc6d9d70c550c9f9360802f"
+dependencies = [
+ "cfg-if",
+ "redox_syscall 0.2.16",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec 1.10.0",
+ "windows-sys 0.45.0",
+]
+
 [[package]]
 name = "paste"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
 
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
 [[package]]
 name = "pin-project-lite"
 version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
 
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "piston-float"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b058c3a640efd4bcf63266512e4bb03187192c1b29edd38b16d5a014613e3199"
+
+[[package]]
+name = "piston3d-cam"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6153024c02c4d09e3193ce0eb63abc585fda3389de375bbd41f059b91f2c520a"
+dependencies = [
+ "quaternion",
+ "vecmath",
+]
+
 [[package]]
 name = "pkg-config"
 version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
 
+[[package]]
+name = "png"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "flate2",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.17"
@@ -504,6 +1540,21 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "profiling"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df"
+
+[[package]]
+name = "quaternion"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5598168e2776e6000e53955f69d238183160199a2c43375e3aa0f74bd34e675"
+dependencies = [
+ "vecmath",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.23"
@@ -513,34 +1564,288 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "rand"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
+dependencies = [
+ "autocfg 0.1.8",
+ "libc",
+ "rand_chacha 0.1.1",
+ "rand_core 0.4.2",
+ "rand_hc",
+ "rand_isaac",
+ "rand_jitter",
+ "rand_os",
+ "rand_pcg",
+ "rand_xorshift",
+ "winapi",
+]
+
 [[package]]
 name = "rand"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
+dependencies = [
+ "autocfg 0.1.8",
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+dependencies = [
+ "rand_core 0.4.2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "rand_isaac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "rand_jitter"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
+dependencies = [
+ "libc",
+ "rand_core 0.4.2",
+ "winapi",
+]
+
+[[package]]
+name = "rand_os"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
+dependencies = [
+ "cloudabi",
+ "fuchsia-cprng",
+ "libc",
+ "rand_core 0.4.2",
+ "rdrand",
+ "winapi",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
+dependencies = [
+ "autocfg 0.1.8",
+ "rand_core 0.4.2",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
+dependencies = [
+ "cty",
+]
+
+[[package]]
+name = "rdrand"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb02a9aee8e8c7ad8d86890f1e16b49e0bbbffc9961ff3788c31d57c98bcbf03"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "renderdoc-sys"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
+
+[[package]]
+name = "rust-embed"
+version = "6.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
+dependencies = [
+ "rust-embed-impl",
+ "rust-embed-utils",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-impl"
+version = "6.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rust-embed-utils",
+ "syn",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-utils"
+version = "7.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
+dependencies = [
+ "sha2",
+ "walkdir",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "sctk-adwaita"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc56402866c717f54e48b122eb93c69f709bc5a6359c403598992fd92f017931"
+dependencies = [
+ "ab_glyph",
+ "log",
+ "memmap2",
+ "smithay-client-toolkit",
+ "tiny-skia",
 ]
 
 [[package]]
-name = "rand_chacha"
-version = "0.3.1"
+name = "serde"
+version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
 dependencies = [
- "ppv-lite86",
- "rand_core",
+ "serde_derive",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.6.4"
+name = "serde_derive"
+version = "1.0.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
 dependencies = [
- "getrandom",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
@@ -563,6 +1868,58 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg 1.1.0",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
+dependencies = [
+ "maybe-uninit",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454"
+dependencies = [
+ "bitflags",
+ "calloop",
+ "dlib",
+ "lazy_static",
+ "log",
+ "memmap2",
+ "nix 0.24.3",
+ "pkg-config",
+ "wayland-client",
+ "wayland-cursor",
+ "wayland-protocols",
+]
+
 [[package]]
 name = "socket2"
 version = "0.4.7"
@@ -573,6 +1930,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "spirv"
+version = "0.2.0+1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
+dependencies = [
+ "bitflags",
+ "num-traits",
+]
+
 [[package]]
 name = "srp"
 version = "0.6.0"
@@ -581,10 +1948,22 @@ dependencies = [
  "digest",
  "generic-array",
  "lazy_static",
- "num-bigint",
+ "num-bigint 0.4.3",
  "subtle",
 ]
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strict-num"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1"
+
 [[package]]
 name = "strsim"
 version = "0.10.0"
@@ -597,6 +1976,12 @@ version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
+[[package]]
+name = "svg_fmt"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
+
 [[package]]
 name = "syn"
 version = "1.0.107"
@@ -608,6 +1993,15 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "thiserror"
 version = "1.0.38"
@@ -628,13 +2022,47 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "png",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
 [[package]]
 name = "tokio"
 version = "1.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
 dependencies = [
- "autocfg",
+ "autocfg 1.1.0",
  "libc",
  "mio",
  "num_cpus",
@@ -673,6 +2101,12 @@ dependencies = [
  "toml_datetime",
 ]
 
+[[package]]
+name = "ttf-parser"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
+
 [[package]]
 name = "typenum"
 version = "1.16.0"
@@ -691,24 +2125,317 @@ version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
 
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "vecmath"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bdd6034ee9c1e5e12485f3e4120e12777f6c81cf43bf9a73bff98ed2b479afe"
+dependencies = [
+ "piston-float",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
+[[package]]
+name = "wayland-client"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
+dependencies = [
+ "bitflags",
+ "downcast-rs",
+ "libc",
+ "nix 0.24.3",
+ "scoped-tls",
+ "wayland-commons",
+ "wayland-scanner",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-commons"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
+dependencies = [
+ "nix 0.24.3",
+ "once_cell",
+ "smallvec 1.10.0",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
+dependencies = [
+ "nix 0.24.3",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
+dependencies = [
+ "bitflags",
+ "wayland-client",
+ "wayland-commons",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "xml-rs",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.29.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
+dependencies = [
+ "dlib",
+ "lazy_static",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wgpu"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7"
+dependencies = [
+ "arrayvec",
+ "cfg-if",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec 1.10.0",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags",
+ "codespan-reporting",
+ "fxhash",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec 1.10.0",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37a7816f00690eca4540579ad861ee9b646d938b467ce83caa5ffb8b1d8180f6"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "d3d12",
+ "foreign-types",
+ "fxhash",
+ "glow",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "hassle-rs",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "smallvec 1.10.0",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a321d5436275e62be2d1ebb87991486fb3a561823656beb5410571500972cc65"
+dependencies = [
+ "bitflags",
+ "js-sys",
+ "web-sys",
+]
+
+[[package]]
+name = "widestring"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -725,12 +2452,30 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
+dependencies = [
+ "windows-targets",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.42.0"
@@ -812,6 +2557,67 @@ version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
 
+[[package]]
+name = "winit"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4755d4ba0e3d30fc7beef2095e246b1e6a6fad0717608bcb87a2df4b003bcf"
+dependencies = [
+ "android-activity",
+ "bitflags",
+ "cfg_aliases",
+ "core-foundation",
+ "core-graphics",
+ "dispatch",
+ "instant",
+ "libc",
+ "log",
+ "mio",
+ "ndk",
+ "objc2",
+ "once_cell",
+ "orbclient",
+ "percent-encoding",
+ "raw-window-handle",
+ "redox_syscall 0.3.4",
+ "sctk-adwaita",
+ "smithay-client-toolkit",
+ "wasm-bindgen",
+ "wayland-client",
+ "wayland-commons",
+ "wayland-protocols",
+ "wayland-scanner",
+ "web-sys",
+ "windows-sys 0.45.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "xcursor"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
+
 [[package]]
 name = "zstd"
 version = "0.12.3+zstd.1.5.2"
index d14cb17fd89cbae606979d42714c34e8d1bfae2f..6b389824bc08a92c71eac4e307892ca5ba2c3c2d 100644 (file)
@@ -1,11 +1,22 @@
 [package]
-name = "hydra"
+name = "mt_client"
 version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-mt_net = { git = "https://github.com/minetest-rust/mt_net", version = "0.1.0", features = ["conn", "client"] }
+bytemuck = { version = "1.13.0", features = ["derive"] }
+cgmath = "0.17.0"
+fps-camera = "0.1.2"
+futures = { version = "0.3.26" }
+guillotiere = "0.6.2"
+image = { version = "0.24.5", features = ["jpeg", "png", "bmp", "tga"], default-features = false }
+lerp = "0.4.0"
+mt_net = { git = "https://github.com/minetest-rust/mt_net", features = ["conn", "client"] }
 rand = "0.8.5"
+rust-embed = "6.4.2"
 sha2 = "0.10.6"
-srp = { git = "https://github.com/minetest-rust/PAKEs.git", version = "0.6.0" }
-tokio = { version = "1.25.0", features = ["rt", "rt-multi-thread"] }
+srp = { git = "https://github.com/minetest-rust/PAKEs.git" }
+threadpool = "1.8.1"
+tokio = { version = "1.25.0", features = ["rt", "rt-multi-thread", "signal"] }
+wgpu = "0.15.1"
+winit = "0.28.1"
diff --git a/assets/ascii-art.txt b/assets/ascii-art.txt
new file mode 100644 (file)
index 0000000..51c82fb
--- /dev/null
@@ -0,0 +1,6 @@
+     _              _    _            _   
+ ___| |__  _ __ ___| | _| |_ ___  ___| |_ 
+/ __| '_ \| '__/ _ \ |/ / __/ _ \/ __| __|
+\__ \ | | | | |  __/   <| ||  __/\__ \ |_ 
+|___/_| |_|_|  \___|_|\_\\__\___||___/\__|
+                                          
diff --git a/assets/shaders/map.wgsl b/assets/shaders/map.wgsl
new file mode 100644 (file)
index 0000000..f6919a9
--- /dev/null
@@ -0,0 +1,34 @@
+// Vertex shader
+
+struct VertexInput {
+       @location(0) pos: vec3<f32>,
+       @location(1) tex_coords: vec2<f32>,
+}
+
+struct VertexOutput {
+       @builtin(position) pos: vec4<f32>,
+       @location(0) tex_coords: vec2<f32>,
+}
+
+@group(1) @binding(0) var<uniform> view_proj: mat4x4<f32>;
+@group(2) @binding(0) var<uniform> model: mat4x4<f32>;
+
+@vertex
+fn vs_main(
+       in: VertexInput,
+) -> VertexOutput {
+       var out: VertexOutput;
+       out.pos = view_proj * model * vec4<f32>(in.pos, 1.0);
+       out.tex_coords = in.tex_coords;
+       return out;
+}
+
+// Fragment shader
+
+@group(0) @binding(0) var atlas_texture: texture_2d<f32>;
+@group(0) @binding(1) var atlas_sampler: sampler;
+
+@fragment
+fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
+       return textureSample(atlas_texture, atlas_sampler, in.tex_coords);
+}
diff --git a/assets/textures/air.png b/assets/textures/air.png
new file mode 100644 (file)
index 0000000..e2c4872
Binary files /dev/null and b/assets/textures/air.png differ
diff --git a/assets/textures/aux1_btn.png b/assets/textures/aux1_btn.png
new file mode 100644 (file)
index 0000000..8ceb095
Binary files /dev/null and b/assets/textures/aux1_btn.png differ
diff --git a/assets/textures/blank.png b/assets/textures/blank.png
new file mode 100644 (file)
index 0000000..85e0250
Binary files /dev/null and b/assets/textures/blank.png differ
diff --git a/assets/textures/bubble.png b/assets/textures/bubble.png
new file mode 100644 (file)
index 0000000..799327c
Binary files /dev/null and b/assets/textures/bubble.png differ
diff --git a/assets/textures/bubble_gone.png b/assets/textures/bubble_gone.png
new file mode 100644 (file)
index 0000000..240ca4f
Binary files /dev/null and b/assets/textures/bubble_gone.png differ
diff --git a/assets/textures/camera_btn.png b/assets/textures/camera_btn.png
new file mode 100644 (file)
index 0000000..9327dc4
Binary files /dev/null and b/assets/textures/camera_btn.png differ
diff --git a/assets/textures/cdb_add.png b/assets/textures/cdb_add.png
new file mode 100644 (file)
index 0000000..3e3d067
Binary files /dev/null and b/assets/textures/cdb_add.png differ
diff --git a/assets/textures/cdb_clear.png b/assets/textures/cdb_clear.png
new file mode 100644 (file)
index 0000000..d5df4a0
Binary files /dev/null and b/assets/textures/cdb_clear.png differ
diff --git a/assets/textures/cdb_downloading.png b/assets/textures/cdb_downloading.png
new file mode 100644 (file)
index 0000000..8cba383
Binary files /dev/null and b/assets/textures/cdb_downloading.png differ
diff --git a/assets/textures/cdb_queued.png b/assets/textures/cdb_queued.png
new file mode 100644 (file)
index 0000000..6972f7f
Binary files /dev/null and b/assets/textures/cdb_queued.png differ
diff --git a/assets/textures/cdb_update.png b/assets/textures/cdb_update.png
new file mode 100644 (file)
index 0000000..262e42d
Binary files /dev/null and b/assets/textures/cdb_update.png differ
diff --git a/assets/textures/cdb_viewonline.png b/assets/textures/cdb_viewonline.png
new file mode 100644 (file)
index 0000000..ae2a146
Binary files /dev/null and b/assets/textures/cdb_viewonline.png differ
diff --git a/assets/textures/chat_btn.png b/assets/textures/chat_btn.png
new file mode 100644 (file)
index 0000000..be4477d
Binary files /dev/null and b/assets/textures/chat_btn.png differ
diff --git a/assets/textures/chat_hide_btn.png b/assets/textures/chat_hide_btn.png
new file mode 100644 (file)
index 0000000..92a8ece
Binary files /dev/null and b/assets/textures/chat_hide_btn.png differ
diff --git a/assets/textures/chat_show_btn.png b/assets/textures/chat_show_btn.png
new file mode 100644 (file)
index 0000000..b260d25
Binary files /dev/null and b/assets/textures/chat_show_btn.png differ
diff --git a/assets/textures/checkbox_16.png b/assets/textures/checkbox_16.png
new file mode 100644 (file)
index 0000000..db6101f
Binary files /dev/null and b/assets/textures/checkbox_16.png differ
diff --git a/assets/textures/checkbox_16_white.png b/assets/textures/checkbox_16_white.png
new file mode 100644 (file)
index 0000000..0cf0f3e
Binary files /dev/null and b/assets/textures/checkbox_16_white.png differ
diff --git a/assets/textures/checkbox_32.png b/assets/textures/checkbox_32.png
new file mode 100644 (file)
index 0000000..f5ff59a
Binary files /dev/null and b/assets/textures/checkbox_32.png differ
diff --git a/assets/textures/checkbox_64.png b/assets/textures/checkbox_64.png
new file mode 100644 (file)
index 0000000..03eb90b
Binary files /dev/null and b/assets/textures/checkbox_64.png differ
diff --git a/assets/textures/clear.png b/assets/textures/clear.png
new file mode 100644 (file)
index 0000000..9244264
Binary files /dev/null and b/assets/textures/clear.png differ
diff --git a/assets/textures/crack_anylength.png b/assets/textures/crack_anylength.png
new file mode 100644 (file)
index 0000000..d9b49f9
Binary files /dev/null and b/assets/textures/crack_anylength.png differ
diff --git a/assets/textures/debug_btn.png b/assets/textures/debug_btn.png
new file mode 100644 (file)
index 0000000..ab210a6
Binary files /dev/null and b/assets/textures/debug_btn.png differ
diff --git a/assets/textures/down.png b/assets/textures/down.png
new file mode 100644 (file)
index 0000000..c290d3a
Binary files /dev/null and b/assets/textures/down.png differ
diff --git a/assets/textures/drop_btn.png b/assets/textures/drop_btn.png
new file mode 100644 (file)
index 0000000..4403ce6
Binary files /dev/null and b/assets/textures/drop_btn.png differ
diff --git a/assets/textures/end_icon.png b/assets/textures/end_icon.png
new file mode 100644 (file)
index 0000000..4fb4d52
Binary files /dev/null and b/assets/textures/end_icon.png differ
diff --git a/assets/textures/error_icon_orange.png b/assets/textures/error_icon_orange.png
new file mode 100644 (file)
index 0000000..1f1586f
Binary files /dev/null and b/assets/textures/error_icon_orange.png differ
diff --git a/assets/textures/error_icon_red.png b/assets/textures/error_icon_red.png
new file mode 100644 (file)
index 0000000..1f5bafb
Binary files /dev/null and b/assets/textures/error_icon_red.png differ
diff --git a/assets/textures/error_screenshot.png b/assets/textures/error_screenshot.png
new file mode 100644 (file)
index 0000000..e35a0a3
Binary files /dev/null and b/assets/textures/error_screenshot.png differ
diff --git a/assets/textures/fast_btn.png b/assets/textures/fast_btn.png
new file mode 100644 (file)
index 0000000..aeb4d3c
Binary files /dev/null and b/assets/textures/fast_btn.png differ
diff --git a/assets/textures/fly_btn.png b/assets/textures/fly_btn.png
new file mode 100644 (file)
index 0000000..641d0fa
Binary files /dev/null and b/assets/textures/fly_btn.png differ
diff --git a/assets/textures/gear_icon.png b/assets/textures/gear_icon.png
new file mode 100644 (file)
index 0000000..520e82e
Binary files /dev/null and b/assets/textures/gear_icon.png differ
diff --git a/assets/textures/halo.png b/assets/textures/halo.png
new file mode 100644 (file)
index 0000000..ed3ff9d
Binary files /dev/null and b/assets/textures/halo.png differ
diff --git a/assets/textures/heart.png b/assets/textures/heart.png
new file mode 100644 (file)
index 0000000..13db59b
Binary files /dev/null and b/assets/textures/heart.png differ
diff --git a/assets/textures/heart_gone.png b/assets/textures/heart_gone.png
new file mode 100644 (file)
index 0000000..240ca4f
Binary files /dev/null and b/assets/textures/heart_gone.png differ
diff --git a/assets/textures/ignore.png b/assets/textures/ignore.png
new file mode 100644 (file)
index 0000000..a73d222
Binary files /dev/null and b/assets/textures/ignore.png differ
diff --git a/assets/textures/inventory_btn.png b/assets/textures/inventory_btn.png
new file mode 100644 (file)
index 0000000..278ce39
Binary files /dev/null and b/assets/textures/inventory_btn.png differ
diff --git a/assets/textures/joystick_bg.png b/assets/textures/joystick_bg.png
new file mode 100644 (file)
index 0000000..4061939
Binary files /dev/null and b/assets/textures/joystick_bg.png differ
diff --git a/assets/textures/joystick_center.png b/assets/textures/joystick_center.png
new file mode 100644 (file)
index 0000000..1e4754d
Binary files /dev/null and b/assets/textures/joystick_center.png differ
diff --git a/assets/textures/joystick_off.png b/assets/textures/joystick_off.png
new file mode 100644 (file)
index 0000000..6dfbc7a
Binary files /dev/null and b/assets/textures/joystick_off.png differ
diff --git a/assets/textures/jump_btn.png b/assets/textures/jump_btn.png
new file mode 100644 (file)
index 0000000..d21b4a9
Binary files /dev/null and b/assets/textures/jump_btn.png differ
diff --git a/assets/textures/loading_screenshot.png b/assets/textures/loading_screenshot.png
new file mode 100644 (file)
index 0000000..4d65a5d
Binary files /dev/null and b/assets/textures/loading_screenshot.png differ
diff --git a/assets/textures/logo.png b/assets/textures/logo.png
new file mode 100644 (file)
index 0000000..4879367
Binary files /dev/null and b/assets/textures/logo.png differ
diff --git a/assets/textures/menu_bg.png b/assets/textures/menu_bg.png
new file mode 100644 (file)
index 0000000..ed7e34f
Binary files /dev/null and b/assets/textures/menu_bg.png differ
diff --git a/assets/textures/menu_header.png b/assets/textures/menu_header.png
new file mode 100644 (file)
index 0000000..0769a01
Binary files /dev/null and b/assets/textures/menu_header.png differ
diff --git a/assets/textures/minimap_btn.png b/assets/textures/minimap_btn.png
new file mode 100644 (file)
index 0000000..727ba1e
Binary files /dev/null and b/assets/textures/minimap_btn.png differ
diff --git a/assets/textures/minimap_mask_round.png b/assets/textures/minimap_mask_round.png
new file mode 100644 (file)
index 0000000..a0c6b2d
Binary files /dev/null and b/assets/textures/minimap_mask_round.png differ
diff --git a/assets/textures/minimap_mask_square.png b/assets/textures/minimap_mask_square.png
new file mode 100644 (file)
index 0000000..160b6fa
Binary files /dev/null and b/assets/textures/minimap_mask_square.png differ
diff --git a/assets/textures/minimap_overlay_round.png b/assets/textures/minimap_overlay_round.png
new file mode 100644 (file)
index 0000000..16e3c41
Binary files /dev/null and b/assets/textures/minimap_overlay_round.png differ
diff --git a/assets/textures/minimap_overlay_square.png b/assets/textures/minimap_overlay_square.png
new file mode 100644 (file)
index 0000000..c971c4b
Binary files /dev/null and b/assets/textures/minimap_overlay_square.png differ
diff --git a/assets/textures/next_icon.png b/assets/textures/next_icon.png
new file mode 100644 (file)
index 0000000..03f9609
Binary files /dev/null and b/assets/textures/next_icon.png differ
diff --git a/assets/textures/no_screenshot.png b/assets/textures/no_screenshot.png
new file mode 100644 (file)
index 0000000..14df091
Binary files /dev/null and b/assets/textures/no_screenshot.png differ
diff --git a/assets/textures/no_texture.png b/assets/textures/no_texture.png
new file mode 100644 (file)
index 0000000..681b810
Binary files /dev/null and b/assets/textures/no_texture.png differ
diff --git a/assets/textures/no_texture_airlike.png b/assets/textures/no_texture_airlike.png
new file mode 100644 (file)
index 0000000..634ee8c
Binary files /dev/null and b/assets/textures/no_texture_airlike.png differ
diff --git a/assets/textures/noclip_btn.png b/assets/textures/noclip_btn.png
new file mode 100644 (file)
index 0000000..5d7372d
Binary files /dev/null and b/assets/textures/noclip_btn.png differ
diff --git a/assets/textures/object_marker_red.png b/assets/textures/object_marker_red.png
new file mode 100644 (file)
index 0000000..f345d03
Binary files /dev/null and b/assets/textures/object_marker_red.png differ
diff --git a/assets/textures/player.png b/assets/textures/player.png
new file mode 100644 (file)
index 0000000..6d61c43
Binary files /dev/null and b/assets/textures/player.png differ
diff --git a/assets/textures/player_back.png b/assets/textures/player_back.png
new file mode 100644 (file)
index 0000000..a84a066
Binary files /dev/null and b/assets/textures/player_back.png differ
diff --git a/assets/textures/player_marker.png b/assets/textures/player_marker.png
new file mode 100644 (file)
index 0000000..f2cc2c6
Binary files /dev/null and b/assets/textures/player_marker.png differ
diff --git a/assets/textures/plus.png b/assets/textures/plus.png
new file mode 100644 (file)
index 0000000..64680b5
Binary files /dev/null and b/assets/textures/plus.png differ
diff --git a/assets/textures/prev_icon.png b/assets/textures/prev_icon.png
new file mode 100644 (file)
index 0000000..71509e7
Binary files /dev/null and b/assets/textures/prev_icon.png differ
diff --git a/assets/textures/progress_bar.png b/assets/textures/progress_bar.png
new file mode 100644 (file)
index 0000000..e803671
Binary files /dev/null and b/assets/textures/progress_bar.png differ
diff --git a/assets/textures/progress_bar_bg.png b/assets/textures/progress_bar_bg.png
new file mode 100644 (file)
index 0000000..4e30ae8
Binary files /dev/null and b/assets/textures/progress_bar_bg.png differ
diff --git a/assets/textures/rangeview_btn.png b/assets/textures/rangeview_btn.png
new file mode 100644 (file)
index 0000000..e49a1b6
Binary files /dev/null and b/assets/textures/rangeview_btn.png differ
diff --git a/assets/textures/rare_controls.png b/assets/textures/rare_controls.png
new file mode 100644 (file)
index 0000000..953d9e0
Binary files /dev/null and b/assets/textures/rare_controls.png differ
diff --git a/assets/textures/refresh.png b/assets/textures/refresh.png
new file mode 100644 (file)
index 0000000..7193677
Binary files /dev/null and b/assets/textures/refresh.png differ
diff --git a/assets/textures/search.png b/assets/textures/search.png
new file mode 100644 (file)
index 0000000..aace804
Binary files /dev/null and b/assets/textures/search.png differ
diff --git a/assets/textures/server_favorite.png b/assets/textures/server_favorite.png
new file mode 100644 (file)
index 0000000..6a3fc5e
Binary files /dev/null and b/assets/textures/server_favorite.png differ
diff --git a/assets/textures/server_favorite_delete.png b/assets/textures/server_favorite_delete.png
new file mode 100644 (file)
index 0000000..e35c6aa
Binary files /dev/null and b/assets/textures/server_favorite_delete.png differ
diff --git a/assets/textures/server_flags_creative.png b/assets/textures/server_flags_creative.png
new file mode 100644 (file)
index 0000000..fa37a19
Binary files /dev/null and b/assets/textures/server_flags_creative.png differ
diff --git a/assets/textures/server_flags_damage.png b/assets/textures/server_flags_damage.png
new file mode 100644 (file)
index 0000000..3f0bf0d
Binary files /dev/null and b/assets/textures/server_flags_damage.png differ
diff --git a/assets/textures/server_flags_pvp.png b/assets/textures/server_flags_pvp.png
new file mode 100644 (file)
index 0000000..977dfdc
Binary files /dev/null and b/assets/textures/server_flags_pvp.png differ
diff --git a/assets/textures/server_incompatible.png b/assets/textures/server_incompatible.png
new file mode 100644 (file)
index 0000000..9076ab5
Binary files /dev/null and b/assets/textures/server_incompatible.png differ
diff --git a/assets/textures/server_ping_1.png b/assets/textures/server_ping_1.png
new file mode 100644 (file)
index 0000000..ba5bba1
Binary files /dev/null and b/assets/textures/server_ping_1.png differ
diff --git a/assets/textures/server_ping_2.png b/assets/textures/server_ping_2.png
new file mode 100644 (file)
index 0000000..8dca0be
Binary files /dev/null and b/assets/textures/server_ping_2.png differ
diff --git a/assets/textures/server_ping_3.png b/assets/textures/server_ping_3.png
new file mode 100644 (file)
index 0000000..c2cab01
Binary files /dev/null and b/assets/textures/server_ping_3.png differ
diff --git a/assets/textures/server_ping_4.png b/assets/textures/server_ping_4.png
new file mode 100644 (file)
index 0000000..03b4b5b
Binary files /dev/null and b/assets/textures/server_ping_4.png differ
diff --git a/assets/textures/server_public.png b/assets/textures/server_public.png
new file mode 100644 (file)
index 0000000..46a48fa
Binary files /dev/null and b/assets/textures/server_public.png differ
diff --git a/assets/textures/smoke_puff.png b/assets/textures/smoke_puff.png
new file mode 100644 (file)
index 0000000..488b50f
Binary files /dev/null and b/assets/textures/smoke_puff.png differ
diff --git a/assets/textures/start_icon.png b/assets/textures/start_icon.png
new file mode 100644 (file)
index 0000000..3830fb3
Binary files /dev/null and b/assets/textures/start_icon.png differ
diff --git a/assets/textures/sunrisebg.png b/assets/textures/sunrisebg.png
new file mode 100644 (file)
index 0000000..3352317
Binary files /dev/null and b/assets/textures/sunrisebg.png differ
diff --git a/assets/textures/unknown_item.png b/assets/textures/unknown_item.png
new file mode 100644 (file)
index 0000000..c8cf616
Binary files /dev/null and b/assets/textures/unknown_item.png differ
diff --git a/assets/textures/unknown_node.png b/assets/textures/unknown_node.png
new file mode 100644 (file)
index 0000000..cb59ff4
Binary files /dev/null and b/assets/textures/unknown_node.png differ
diff --git a/assets/textures/unknown_object.png b/assets/textures/unknown_object.png
new file mode 100644 (file)
index 0000000..c0166c3
Binary files /dev/null and b/assets/textures/unknown_object.png differ
diff --git a/assets/textures/wieldhand.png b/assets/textures/wieldhand.png
new file mode 100644 (file)
index 0000000..ff2283a
Binary files /dev/null and b/assets/textures/wieldhand.png differ
diff --git a/assets/textures/zoom.png b/assets/textures/zoom.png
new file mode 100644 (file)
index 0000000..b27c8c8
Binary files /dev/null and b/assets/textures/zoom.png differ
diff --git a/src/gfx.rs b/src/gfx.rs
new file mode 100644 (file)
index 0000000..e3fb073
--- /dev/null
@@ -0,0 +1,141 @@
+use crate::{GfxEvent::*, NetEvent};
+use cgmath::Rad;
+use std::time::Instant;
+use tokio::sync::mpsc;
+use winit::{
+    event::{DeviceEvent::*, Event::*, WindowEvent::*},
+    event_loop::ControlFlow::ExitWithCode,
+    platform::run_return::EventLoopExtRunReturn,
+    window::CursorGrabMode,
+};
+
+mod map;
+mod media;
+mod state;
+mod util;
+
+pub async fn run(
+    mut event_loop: winit::event_loop::EventLoop<crate::GfxEvent>,
+    net_events: mpsc::UnboundedSender<NetEvent>,
+) {
+    let window = winit::window::WindowBuilder::new()
+        .build(&event_loop)
+        .unwrap();
+
+    window
+        .set_cursor_grab(CursorGrabMode::Locked)
+        .or_else(|_e| window.set_cursor_grab(CursorGrabMode::Confined))
+        .unwrap();
+
+    window.set_cursor_visible(false);
+
+    let mut state = state::State::new(&window).await;
+    let mut map = None;
+    let mut media = media::MediaMgr::new();
+
+    let mut nodedefs = None;
+
+    let mut last_frame = Instant::now();
+
+    event_loop.run_return(move |event, _, flow| match event {
+        MainEventsCleared => window.request_redraw(),
+        RedrawRequested(id) if id == window.id() => {
+            let now = Instant::now();
+            let dt = now - last_frame;
+            last_frame = now;
+
+            state.update(dt);
+            net_events
+                .send(NetEvent::PlayerPos(
+                    state.camera.position.into(),
+                    Rad(state.camera.yaw).into(),
+                    Rad(state.camera.pitch).into(),
+                ))
+                .ok();
+
+            use wgpu::SurfaceError::*;
+            match state.render(&map) {
+                Ok(_) => {}
+                Err(Lost) => state.configure_surface(),
+                Err(OutOfMemory) => *flow = ExitWithCode(0),
+                Err(err) => eprintln!("gfx error: {err:?}"),
+            }
+        }
+        WindowEvent {
+            ref event,
+            window_id: id,
+        } if id == window.id() => match event {
+            CloseRequested => *flow = ExitWithCode(0),
+            Resized(size) => state.resize(*size),
+            ScaleFactorChanged { new_inner_size, .. } => state.resize(**new_inner_size),
+            KeyboardInput {
+                input:
+                    winit::event::KeyboardInput {
+                        virtual_keycode: Some(key),
+                        state: key_state,
+                        ..
+                    },
+                ..
+            } => {
+                use fps_camera::Actions;
+                use winit::event::{ElementState::*, VirtualKeyCode as Key};
+
+                let actions = match key {
+                    Key::W => Actions::MOVE_FORWARD,
+                    Key::A => Actions::STRAFE_LEFT,
+                    Key::S => Actions::MOVE_BACKWARD,
+                    Key::D => Actions::STRAFE_RIGHT,
+                    Key::Space => Actions::FLY_UP,
+                    Key::LShift => Actions::FLY_DOWN,
+                    _ => Actions::empty(),
+                };
+
+                match key_state {
+                    Pressed => state.camera.enable_actions(actions),
+                    Released => state.camera.disable_action(actions),
+                }
+            }
+            _ => {}
+        },
+        DeviceEvent {
+            event: MouseMotion { delta },
+            ..
+        } => {
+            state.camera.update_mouse(delta.0 as f32, delta.1 as f32);
+            window
+                .set_cursor_position(winit::dpi::PhysicalPosition::new(
+                    state.config.width / 2,
+                    state.config.height / 2,
+                ))
+                .ok();
+        }
+        UserEvent(event) => match event {
+            Close => *flow = ExitWithCode(0),
+            NodeDefs(defs) => nodedefs = Some(defs),
+            MapBlock(pos, blk) => {
+                if let Some(map) = map.as_mut() {
+                    map.add_block(&mut state, pos, blk);
+                }
+            }
+            Media(files, finished) => {
+                media.add_server_media(files);
+
+                if finished {
+                    map = Some(map::MapRender::new(
+                        &mut state,
+                        &media,
+                        nodedefs.take().unwrap_or_default(),
+                    ));
+
+                    net_events.send(NetEvent::Ready).ok();
+                }
+            }
+            PlayerPos(pos, pitch, yaw) => {
+                state.camera.position = pos.into();
+                state.camera.pitch = Rad::<f32>::from(pitch).0;
+                state.camera.yaw = Rad::<f32>::from(yaw).0;
+            }
+        },
+        _ => {}
+    });
+}
diff --git a/src/gfx/map.rs b/src/gfx/map.rs
new file mode 100644 (file)
index 0000000..d95a4d2
--- /dev/null
@@ -0,0 +1,447 @@
+use super::{media::MediaMgr, state::State, util::MatrixUniform};
+use cgmath::{prelude::*, Matrix4, Point3, Vector3};
+use mt_net::{MapBlock, NodeDef};
+use rand::Rng;
+use std::{collections::HashMap, ops::Range};
+use wgpu::util::DeviceExt;
+
+pub struct MapRender {
+    pipeline: wgpu::RenderPipeline,
+    textures: HashMap<String, [Range<f32>; 2]>,
+    nodes: HashMap<u16, NodeDef>,
+    atlas: wgpu::BindGroup,
+    model: wgpu::BindGroupLayout,
+    blocks: HashMap<[i16; 3], BlockMesh>,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
+struct Vertex {
+    pos: [f32; 3],
+    tex_coords: [f32; 2],
+}
+
+impl Vertex {
+    const ATTRIBS: [wgpu::VertexAttribute; 2] =
+        wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2];
+
+    fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
+        wgpu::VertexBufferLayout {
+            array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
+            step_mode: wgpu::VertexStepMode::Vertex,
+            attributes: &Self::ATTRIBS,
+        }
+    }
+}
+
+struct BlockMesh {
+    vertex_buffer: wgpu::Buffer,
+    num_vertices: u32,
+    model: MatrixUniform,
+}
+
+impl MapRender {
+    pub fn render<'a>(&'a self, state: &'a State, pass: &mut wgpu::RenderPass<'a>) {
+        pass.set_pipeline(&self.pipeline);
+        pass.set_bind_group(0, &self.atlas, &[]);
+        pass.set_bind_group(1, &state.camera_uniform.bind_group, &[]);
+
+        for mesh in self.blocks.values() {
+            pass.set_bind_group(2, &mesh.model.bind_group, &[]);
+            pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
+            pass.draw(0..mesh.num_vertices, 0..1);
+        }
+    }
+
+    pub fn add_block(&mut self, state: &mut State, pos: Point3<i16>, block: Box<MapBlock>) {
+        let mut vertices = Vec::with_capacity(10000);
+        for (index, content) in block.param_0.iter().enumerate() {
+            let def = match self.nodes.get(content) {
+                Some(x) => x,
+                None => continue,
+            };
+
+            use lerp::Lerp;
+            use mt_net::DrawType;
+            use std::array::from_fn as array;
+
+            match def.draw_type {
+                DrawType::Cube => {
+                    let pos: [i16; 3] = array(|i| ((index >> (4 * i)) & 0xf) as i16);
+                    for (f, face) in CUBE.iter().enumerate() {
+                        let dir = FACE_DIR[f];
+                        let npos: [i16; 3] = array(|i| dir[i] + pos[i]);
+                        if npos.iter().all(|x| (0..16).contains(x)) {
+                            let nindex = npos[0] | (npos[1] << 4) | (npos[2] << 8);
+
+                            if let Some(ndef) = self.nodes.get(&block.param_0[nindex as usize]) {
+                                if ndef.draw_type == DrawType::Cube {
+                                    continue;
+                                }
+                            }
+                        }
+
+                        let tile = &def.tiles[f];
+                        let rect = self.textures.get(&tile.texture).unwrap();
+
+                        for vertex in face.iter() {
+                            /*println!(
+                                "{:?} {:?} {:?} {:?}",
+                                (vertex.1[0], vertex.1[1]),
+                                (rect[0].start, rect[1].start),
+                                (rect[0].end, rect[1].end),
+                                (
+                                    vertex.1[0].lerp(rect[0].start, rect[0].end),
+                                    vertex.1[1].lerp(rect[1].start, rect[1].end)
+                                )
+                            );*/
+                            vertices.push(Vertex {
+                                pos: array(|i| pos[i] as f32 - 8.5 + vertex.0[i]),
+                                tex_coords: array(|i| rect[i].start.lerp(rect[i].end, vertex.1[i])),
+                            })
+                        }
+                    }
+                }
+                DrawType::None => {}
+                _ => {
+                    // TODO
+                }
+            }
+        }
+
+        self.blocks.insert(
+            pos.into(),
+            BlockMesh {
+                vertex_buffer: state
+                    .device
+                    .create_buffer_init(&wgpu::util::BufferInitDescriptor {
+                        label: Some("mapblock.vertex_buffer"),
+                        contents: bytemuck::cast_slice(&vertices),
+                        usage: wgpu::BufferUsages::VERTEX,
+                    }),
+                num_vertices: vertices.len() as u32,
+                model: MatrixUniform::new(
+                    &state.device,
+                    &self.model,
+                    Matrix4::from_translation(
+                        pos.cast::<f32>().unwrap().to_vec() * 16.0 + Vector3::new(8.5, 8.5, 8.5),
+                    ),
+                    "mapblock",
+                    false,
+                ),
+            },
+        );
+    }
+
+    pub fn new(state: &mut State, media: &MediaMgr, nodes: HashMap<u16, NodeDef>) -> Self {
+        let mut rng = rand::thread_rng();
+        let mut atlas_map = HashMap::new();
+        let mut atlas_alloc = guillotiere::SimpleAtlasAllocator::new(guillotiere::size2(1, 1));
+
+        for node in nodes.values() {
+            let tiles = node
+                .tiles
+                .iter()
+                .chain(node.overlay_tiles.iter())
+                .chain(node.special_tiles.iter());
+
+            let load_texture = |texture: &str| {
+                let payload = media
+                    .get(texture)
+                    .ok_or_else(|| format!("texture not found: {texture}"))?;
+
+                image::load_from_memory(payload)
+                    .or_else(|_| {
+                        image::load_from_memory_with_format(payload, image::ImageFormat::Tga)
+                    })
+                    .map_err(|e| format!("failed to load texture {texture}: {e}"))
+                    .map(|x| image::imageops::flip_vertical(&x))
+            };
+
+            let mut make_texture = |texture: &str| {
+                texture
+                    .split('^')
+                    .map(|part| match load_texture(part) {
+                        Ok(v) => v,
+                        Err(e) => {
+                            if !texture.is_empty() && !texture.contains('[') {
+                                eprintln!("{e}");
+                            }
+
+                            let mut img = image::RgbImage::new(1, 1);
+                            rng.fill(&mut img.get_pixel_mut(0, 0).0);
+
+                            image::DynamicImage::from(img).to_rgba8()
+                        }
+                    })
+                    .reduce(|mut base, top| {
+                        image::imageops::overlay(&mut base, &top, 0, 0);
+                        base
+                    })
+                    .unwrap()
+            };
+
+            for tile in tiles {
+                atlas_map.entry(tile.texture.clone()).or_insert_with(|| {
+                    let img = make_texture(&tile.texture);
+
+                    let dimensions = img.dimensions();
+                    let size = guillotiere::size2(dimensions.0 as i32, dimensions.1 as i32);
+
+                    loop {
+                        match atlas_alloc.allocate(size) {
+                            None => {
+                                let mut atlas_size = atlas_alloc.size();
+                                atlas_size.width *= 2;
+                                atlas_size.height *= 2;
+                                atlas_alloc.grow(atlas_size);
+                            }
+                            Some(v) => return (img, v),
+                        }
+                    }
+                });
+            }
+        }
+
+        let atlas_size = atlas_alloc.size();
+        let mut atlas = image::RgbaImage::new(atlas_size.width as u32, atlas_size.height as u32);
+
+        let textures = atlas_map
+            .into_iter()
+            .map(|(name, (img, rect))| {
+                let w = atlas_size.width as f32;
+                let h = atlas_size.height as f32;
+
+                let x = (rect.min.x as f32 / w)..(rect.max.x as f32 / w);
+                let y = (rect.min.y as f32 / h)..(rect.max.y as f32 / h);
+
+                use image::GenericImage;
+                atlas
+                    .copy_from(&img, rect.min.x as u32, rect.min.y as u32)
+                    .unwrap();
+
+                (name, [x, y])
+            })
+            .collect();
+
+        let size = wgpu::Extent3d {
+            width: atlas_size.width as u32,
+            height: atlas_size.height as u32,
+            depth_or_array_layers: 1,
+        };
+
+        let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
+            size,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Rgba8UnormSrgb,
+            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
+            label: Some("tile_atlas"),
+            view_formats: &[],
+        });
+
+        state.queue.write_texture(
+            wgpu::ImageCopyTexture {
+                texture: &atlas_texture,
+                mip_level: 0,
+                origin: wgpu::Origin3d::ZERO,
+                aspect: wgpu::TextureAspect::All,
+            },
+            &atlas,
+            wgpu::ImageDataLayout {
+                offset: 0,
+                bytes_per_row: std::num::NonZeroU32::new(4 * atlas_size.width as u32),
+                rows_per_image: std::num::NonZeroU32::new(atlas_size.height as u32),
+            },
+            size,
+        );
+
+        let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
+
+        let atlas_sampler = state.device.create_sampler(&wgpu::SamplerDescriptor {
+            address_mode_u: wgpu::AddressMode::ClampToEdge,
+            address_mode_v: wgpu::AddressMode::ClampToEdge,
+            address_mode_w: wgpu::AddressMode::ClampToEdge,
+            // "We've got you surrounded, stop using Nearest filter"
+            // - "I hate bilinear filtering I hate bilinear filtering I hate bilinear filtering"
+            mag_filter: wgpu::FilterMode::Nearest,
+            min_filter: wgpu::FilterMode::Nearest,
+            mipmap_filter: wgpu::FilterMode::Nearest,
+            ..Default::default()
+        });
+
+        let atlas_bind_group_layout =
+            state
+                .device
+                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
+                    entries: &[
+                        wgpu::BindGroupLayoutEntry {
+                            binding: 0,
+                            visibility: wgpu::ShaderStages::FRAGMENT,
+                            ty: wgpu::BindingType::Texture {
+                                multisampled: false,
+                                view_dimension: wgpu::TextureViewDimension::D2,
+                                sample_type: wgpu::TextureSampleType::Float { filterable: true },
+                            },
+                            count: None,
+                        },
+                        wgpu::BindGroupLayoutEntry {
+                            binding: 1,
+                            visibility: wgpu::ShaderStages::FRAGMENT,
+                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
+                            count: None,
+                        },
+                    ],
+                    label: Some("atlas.bind_group_layout"),
+                });
+
+        let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: &atlas_bind_group_layout,
+            entries: &[
+                wgpu::BindGroupEntry {
+                    binding: 0,
+                    resource: wgpu::BindingResource::TextureView(&atlas_view),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 1,
+                    resource: wgpu::BindingResource::Sampler(&atlas_sampler),
+                },
+            ],
+            label: Some("atlas.bind_group"),
+        });
+
+        let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
+
+        let shader = state
+            .device
+            .create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
+
+        let pipeline_layout =
+            state
+                .device
+                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+                    label: None,
+                    bind_group_layouts: &[
+                        &atlas_bind_group_layout,
+                        &model_bind_group_layout,
+                        &state.camera_bind_group_layout,
+                    ],
+                    push_constant_ranges: &[],
+                });
+
+        let pipeline = state
+            .device
+            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+                label: None,
+                layout: Some(&pipeline_layout),
+                vertex: wgpu::VertexState {
+                    module: &shader,
+                    entry_point: "vs_main",
+                    buffers: &[Vertex::desc()],
+                },
+                fragment: Some(wgpu::FragmentState {
+                    module: &shader,
+                    entry_point: "fs_main",
+                    targets: &[Some(wgpu::ColorTargetState {
+                        format: state.config.format,
+                        blend: Some(wgpu::BlendState::REPLACE),
+                        write_mask: wgpu::ColorWrites::ALL,
+                    })],
+                }),
+                primitive: wgpu::PrimitiveState {
+                    topology: wgpu::PrimitiveTopology::TriangleList,
+                    strip_index_format: None,
+                    front_face: wgpu::FrontFace::Ccw,
+                    cull_mode: Some(wgpu::Face::Back),
+                    polygon_mode: wgpu::PolygonMode::Fill,
+                    unclipped_depth: false,
+                    conservative: false,
+                },
+                depth_stencil: Some(wgpu::DepthStencilState {
+                    format: wgpu::TextureFormat::Depth32Float,
+                    depth_write_enabled: true,
+                    depth_compare: wgpu::CompareFunction::Less,
+                    stencil: wgpu::StencilState::default(),
+                    bias: wgpu::DepthBiasState::default(),
+                }),
+                multisample: wgpu::MultisampleState {
+                    count: 1,
+                    mask: !0,
+                    alpha_to_coverage_enabled: false,
+                },
+                multiview: None,
+            });
+
+        Self {
+            pipeline,
+            nodes,
+            textures,
+            atlas: atlas_bind_group,
+            model: model_bind_group_layout,
+            blocks: HashMap::new(),
+        }
+    }
+}
+
+#[rustfmt::skip]
+const CUBE: [[([f32; 3], [f32; 2]); 6]; 6] = [
+       [
+               ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
+               ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
+               ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
+               ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
+               ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
+               ([-0.5,  0.5,  0.5], [ 0.0,  0.0]),
+       ],
+       [
+               ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
+               ([ 0.5, -0.5, -0.5], [ 1.0,  1.0]),
+               ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
+               ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
+               ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
+               ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
+       ],
+       [
+               ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
+               ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([ 0.5,  0.5, -0.5], [ 0.0,  1.0]),
+               ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
+               ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
+       ],
+       [
+               ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
+               ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
+               ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([-0.5, -0.5,  0.5], [ 1.0,  0.0]),
+               ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
+       ],
+       [
+               ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
+               ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
+               ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
+               ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
+               ([-0.5,  0.5,  0.5], [ 0.0,  1.0]),
+               ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
+       ],
+       [
+               ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
+               ([ 0.5, -0.5, -0.5], [ 1.0,  0.0]),
+               ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
+               ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
+               ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
+       ],
+];
+
+#[rustfmt::skip]
+const FACE_DIR: [[i16; 3]; 6] = [
+       [ 0,  1,  0],
+       [ 0, -1,  0],
+       [ 1,  0,  0],
+       [-1,  0,  0],
+       [ 0,  0,  1],
+       [ 0,  0, -1],
+];
diff --git a/src/gfx/media.rs b/src/gfx/media.rs
new file mode 100644 (file)
index 0000000..ac5d158
--- /dev/null
@@ -0,0 +1,42 @@
+use std::collections::HashMap;
+
+#[derive(rust_embed::RustEmbed)]
+#[folder = "assets/textures"]
+pub struct BaseFolder; // copied from github.com/minetest/minetest
+
+pub struct MediaMgr {
+    packs: Vec<HashMap<String, Vec<u8>>>,
+    srv_idx: usize,
+}
+
+impl MediaMgr {
+    pub fn new() -> Self {
+        Self {
+            packs: [
+                BaseFolder::iter()
+                    .map(|file| {
+                        (
+                            file.to_string(),
+                            BaseFolder::get(&file).unwrap().data.into_owned(),
+                        )
+                    })
+                    .collect(),
+                HashMap::new(),
+            ]
+            .into(),
+            srv_idx: 1,
+        }
+    }
+
+    pub fn add_server_media(&mut self, files: HashMap<String, Vec<u8>>) {
+        self.packs[self.srv_idx].extend(files.into_iter());
+    }
+
+    pub fn get(&self, file: &str) -> Option<&[u8]> {
+        self.packs
+            .iter()
+            .rev()
+            .find_map(|pack| pack.get(file))
+            .map(Vec::as_slice)
+    }
+}
diff --git a/src/gfx/state.rs b/src/gfx/state.rs
new file mode 100644 (file)
index 0000000..9d1cfd1
--- /dev/null
@@ -0,0 +1,233 @@
+use super::util::MatrixUniform;
+use cgmath::{prelude::*, Deg, Matrix4, Rad};
+use fps_camera::{FirstPerson, FirstPersonSettings};
+use std::time::Duration;
+
+pub struct State {
+    pub surface: wgpu::Surface,
+    pub device: wgpu::Device,
+    pub queue: wgpu::Queue,
+    pub config: wgpu::SurfaceConfiguration,
+    pub fov: Rad<f32>,
+    pub view: Matrix4<f32>,
+    pub proj: Matrix4<f32>,
+    pub camera: FirstPerson,
+    pub camera_uniform: MatrixUniform,
+    pub camera_bind_group_layout: wgpu::BindGroupLayout,
+    pub depth_texture: wgpu::Texture,
+    pub depth_view: wgpu::TextureView,
+    pub depth_sampler: wgpu::Sampler,
+}
+
+impl State {
+    pub async fn new(window: &winit::window::Window) -> Self {
+        let size = window.inner_size();
+
+        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+            backends: wgpu::Backends::all(),
+            dx12_shader_compiler: Default::default(),
+        });
+
+        let surface = unsafe { instance.create_surface(window) }.unwrap();
+
+        let adapter = instance
+            .request_adapter(&wgpu::RequestAdapterOptions {
+                power_preference: wgpu::PowerPreference::default(),
+                compatible_surface: Some(&surface),
+                force_fallback_adapter: false,
+            })
+            .await
+            .unwrap();
+
+        let (device, queue) = adapter
+            .request_device(
+                &wgpu::DeviceDescriptor {
+                    features: wgpu::Features::empty(),
+                    limits: Default::default(),
+                    label: None,
+                },
+                None,
+            )
+            .await
+            .unwrap();
+
+        let surface_caps = surface.get_capabilities(&adapter);
+        let surface_format = surface_caps
+            .formats
+            .iter()
+            .copied()
+            .find(|f| f.describe().srgb)
+            .unwrap_or(surface_caps.formats[0]);
+
+        let config = wgpu::SurfaceConfiguration {
+            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+            format: surface_format,
+            width: size.width,
+            height: size.height,
+            present_mode: surface_caps.present_modes[0],
+            alpha_mode: surface_caps.alpha_modes[0],
+            view_formats: vec![],
+        };
+
+        let (depth_texture, depth_view, depth_sampler) =
+            Self::create_depth_texture(&config, &device);
+
+        let camera = FirstPerson::new(
+            [0.0, 0.0, 0.0],
+            FirstPersonSettings {
+                speed_horizontal: 10.0,
+                speed_vertical: 10.0,
+                mouse_sensitivity_horizontal: 1.0,
+                mouse_sensitivity_vertical: 1.0,
+            },
+        );
+
+        let camera_bind_group_layout = MatrixUniform::layout(&device, "camera");
+
+        let camera_uniform = MatrixUniform::new(
+            &device,
+            &camera_bind_group_layout,
+            Matrix4::identity(),
+            "camera",
+            true,
+        );
+
+        let mut state = Self {
+            surface,
+            device,
+            queue,
+            config,
+            fov: Deg(90.0).into(),
+            proj: Matrix4::identity(),
+            view: Matrix4::identity(),
+            camera,
+            camera_uniform,
+            camera_bind_group_layout,
+            depth_texture,
+            depth_view,
+            depth_sampler,
+        };
+
+        state.resize(size);
+
+        state
+    }
+
+    pub fn create_depth_texture(
+        config: &wgpu::SurfaceConfiguration,
+        device: &wgpu::Device,
+    ) -> (wgpu::Texture, wgpu::TextureView, wgpu::Sampler) {
+        let depth_size = wgpu::Extent3d {
+            width: config.width,
+            height: config.height,
+            depth_or_array_layers: 1,
+        };
+        let depth_descriptor = wgpu::TextureDescriptor {
+            label: Some("depth texture"),
+            size: depth_size,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Depth32Float,
+            usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
+                               | wgpu::TextureUsages::TEXTURE_BINDING,
+            view_formats: &[],
+        };
+        let depth_texture = device.create_texture(&depth_descriptor);
+
+        let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
+        let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+            address_mode_u: wgpu::AddressMode::ClampToEdge,
+            address_mode_v: wgpu::AddressMode::ClampToEdge,
+            address_mode_w: wgpu::AddressMode::ClampToEdge,
+            mag_filter: wgpu::FilterMode::Linear,
+            min_filter: wgpu::FilterMode::Linear,
+            mipmap_filter: wgpu::FilterMode::Nearest,
+            compare: Some(wgpu::CompareFunction::LessEqual),
+            lod_min_clamp: 0.0,
+            lod_max_clamp: 100.0,
+            ..Default::default()
+        });
+
+        (depth_texture, depth_view, depth_sampler)
+    }
+
+    pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
+        if size.width > 0 && size.height > 0 {
+            self.config.width = size.width;
+            self.config.height = size.height;
+            self.configure_surface();
+            self.update_projection();
+            (self.depth_texture, self.depth_view, self.depth_sampler) =
+                Self::create_depth_texture(&self.config, &self.device);
+        }
+    }
+
+    pub fn configure_surface(&mut self) {
+        self.surface.configure(&self.device, &self.config);
+    }
+
+    pub fn update_projection(&mut self) {
+        self.proj = cgmath::perspective(
+            self.fov,
+            self.config.width as f32 / self.config.height as f32,
+            0.1,
+            100000.0,
+        );
+    }
+
+    pub fn update(&mut self, dt: Duration) {
+        let cam = self.camera.camera(dt.as_secs_f32());
+        self.camera.position = cam.position;
+        self.view = Matrix4::from(cam.orthogonal());
+
+        self.camera_uniform.set(&self.queue, self.proj * self.view);
+    }
+
+    pub fn render(&self, map: &Option<super::map::MapRender>) -> Result<(), wgpu::SurfaceError> {
+        let output = self.surface.get_current_texture()?;
+        let view = output
+            .texture
+            .create_view(&wgpu::TextureViewDescriptor::default());
+
+        let mut encoder = self
+            .device
+            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+
+        {
+            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+                label: None,
+                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+                    view: &view,
+                    resolve_target: None,
+                    ops: wgpu::Operations {
+                        load: wgpu::LoadOp::Clear(wgpu::Color {
+                            r: 0x87 as f64 / 255.0,
+                            g: 0xCE as f64 / 255.0,
+                            b: 0xEB as f64 / 255.0,
+                            a: 1.0,
+                        }),
+                        store: true,
+                    },
+                })],
+                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
+                    view: &self.depth_view,
+                    depth_ops: Some(wgpu::Operations {
+                        load: wgpu::LoadOp::Clear(1.0),
+                        store: true,
+                    }),
+                    stencil_ops: None,
+                }),
+            });
+
+            if let Some(map) = map.as_ref() {
+                map.render(self, &mut render_pass);
+            }
+        }
+
+        self.queue.submit(std::iter::once(encoder.finish()));
+        output.present();
+
+        Ok(())
+    }
+}
diff --git a/src/gfx/util.rs b/src/gfx/util.rs
new file mode 100644 (file)
index 0000000..f93accd
--- /dev/null
@@ -0,0 +1,63 @@
+use cgmath::Matrix4;
+use wgpu::util::DeviceExt;
+
+pub struct MatrixUniform {
+    buffer: wgpu::Buffer,
+    pub bind_group: wgpu::BindGroup,
+}
+
+impl MatrixUniform {
+    pub fn new(
+        device: &wgpu::Device,
+        bind_group_layout: &wgpu::BindGroupLayout,
+        init: Matrix4<f32>,
+        name: &str,
+        writable: bool,
+    ) -> Self {
+        let uniform: [[f32; 4]; 4] = init.into();
+
+        let mut usage = wgpu::BufferUsages::UNIFORM;
+
+        if writable {
+            usage |= wgpu::BufferUsages::COPY_DST;
+        }
+
+        let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+            label: Some(&format!("{name}.buffer")),
+            contents: bytemuck::cast_slice(&[uniform]),
+            usage,
+        });
+
+        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: bind_group_layout,
+            entries: &[wgpu::BindGroupEntry {
+                binding: 0,
+                resource: buffer.as_entire_binding(),
+            }],
+            label: Some(&format!("{name}.bind_group")),
+        });
+
+        Self { buffer, bind_group }
+    }
+
+    pub fn layout(device: &wgpu::Device, name: &str) -> wgpu::BindGroupLayout {
+        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
+            entries: &[wgpu::BindGroupLayoutEntry {
+                binding: 0,
+                visibility: wgpu::ShaderStages::VERTEX,
+                ty: wgpu::BindingType::Buffer {
+                    ty: wgpu::BufferBindingType::Uniform,
+                    has_dynamic_offset: false,
+                    min_binding_size: None,
+                },
+                count: None,
+            }],
+            label: Some(&format!("{name}.bind_group_layout")),
+        })
+    }
+
+    pub fn set(&self, queue: &wgpu::Queue, to: Matrix4<f32>) {
+        let uniform: [[f32; 4]; 4] = to.into();
+        queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[uniform]));
+    }
+}
index 5b8817e7ee916284a0e89854a96326bef0f53370..f517ee2911ba3bf250e36d476a36967ae8512ca8 100644 (file)
-use mt_net::{MtReceiver, MtSender, RemoteSrv, ToCltPkt, ToSrvPkt};
-use rand::RngCore;
-use sha2::Sha256;
-use srp::{client::SrpClient, groups::G_2048};
-use std::time::Duration;
-use tokio::sync::oneshot;
-
-async fn handle(tx: MtSender<RemoteSrv>, rx: &mut MtReceiver<RemoteSrv>) {
-    let mut username = "hydra".to_string();
-    let password = "password";
-
-    let (init_tx, mut init_rx) = oneshot::channel();
-
-    {
-        let tx = tx.clone();
-        let pkt = ToSrvPkt::Init {
-            serialize_version: 29,
-            min_proto_version: 40,
-            max_proto_version: 40,
-            player_name: username.clone(),
-            send_full_item_meta: false,
-        };
-
-        tokio::spawn(async move {
-            let mut interval = tokio::time::interval(Duration::from_millis(100));
-            while tokio::select! {
-                _ = &mut init_rx => false,
-                _ = interval.tick() => true,
-            } {
-                tx.send(&pkt).await.unwrap()
-            }
-        });
-    }
-
-    let mut init_tx = Some(init_tx);
-    let mut auth = None;
-
-    while let Some(res) = rx.recv().await {
-        match res {
-            Ok(pkt) => {
-                use ToCltPkt::*;
-
-                match pkt {
-                    Hello {
-                        auth_methods,
-                        username: name,
-                        ..
-                    } => {
-                        use mt_net::AuthMethod;
-
-                        if let Some(chan) = init_tx.take() {
-                            chan.send(()).unwrap();
-
-                            let client = SrpClient::<Sha256>::new(&G_2048);
-
-                            let mut rand_bytes = vec![0; 32];
-                            rand::thread_rng().fill_bytes(&mut rand_bytes);
-
-                            username = name;
+mod gfx;
+mod net;
+
+use cgmath::{Deg, Point3};
+use std::collections::HashMap;
+use tokio::sync::mpsc;
+
+#[derive(Debug, Clone)]
+pub enum GfxEvent {
+    Close,
+    Media(HashMap<String, Vec<u8>>, bool),
+    NodeDefs(HashMap<u16, mt_net::NodeDef>),
+    MapBlock(Point3<i16>, Box<mt_net::MapBlock>),
+    PlayerPos(Point3<f32>, Deg<f32>, Deg<f32>),
+}
 
-                            if auth_methods.contains(AuthMethod::FirstSrp) {
-                                let verifier = client.compute_verifier(
-                                    username.to_lowercase().as_bytes(),
-                                    password.as_bytes(),
-                                    &rand_bytes,
-                                );
+#[derive(Debug, Clone)]
+pub enum NetEvent {
+    PlayerPos(Point3<f32>, Deg<f32>, Deg<f32>),
+    Ready,
+}
 
-                                tx.send(&ToSrvPkt::FirstSrp {
-                                    salt: rand_bytes,
-                                    verifier,
-                                    empty_passwd: password.is_empty(),
-                                })
-                                .await
-                                .unwrap();
-                            } else if auth_methods.contains(AuthMethod::Srp) {
-                                let a = client.compute_public_ephemeral(&rand_bytes);
-                                auth = Some((rand_bytes, client));
+fn main() {
+    println!(include_str!("../assets/ascii-art.txt"));
+    println!("Early WIP. Expext breakage. Trans rights <3");
 
-                                tx.send(&ToSrvPkt::SrpBytesA { a, no_sha1: true })
-                                    .await
-                                    .unwrap();
-                            } else {
-                                panic!("unsupported auth methods: {auth_methods:?}");
-                            }
-                        }
-                    }
-                    SrpBytesSaltB { salt, b } => {
-                        if let Some((a, client)) = auth.take() {
-                            let m = client
-                                .process_reply(
-                                    &a,
-                                    username.to_lowercase().as_bytes(),
-                                    password.as_bytes(),
-                                    &salt,
-                                    &b,
-                                )
-                                .unwrap()
-                                .proof()
-                                .into();
+    let (net_tx, net_rx) = mpsc::unbounded_channel();
+    let event_loop = winit::event_loop::EventLoopBuilder::<GfxEvent>::with_user_event().build();
+    let event_loop_proxy = event_loop.create_proxy();
 
-                            tx.send(&ToSrvPkt::SrpBytesM { m }).await.unwrap();
-                        }
-                    }
-                    x => println!("{x:?}"),
-                }
-            }
-            Err(err) => eprintln!("{err}"),
-        }
-    }
-}
+    let runtime = tokio::runtime::Builder::new_multi_thread()
+        .enable_io()
+        .enable_time()
+        .thread_name("network")
+        .build()
+        .unwrap();
 
-#[tokio::main]
-async fn main() {
-    let (tx, mut rx) = mt_net::connect("localhost:30000").await.unwrap();
+    let net_thread = runtime.spawn(net::run(event_loop_proxy, net_rx));
 
-    tokio::select! {
-        _ = tokio::signal::ctrl_c() => println!("canceled"),
-        _ = handle(tx, &mut rx) => {
-            println!("disconnected");
-        }
-    }
+    // graphics code is pseudo async: the winit event loop is blocking
+    // so we can't really use async capabilities
+    futures::executor::block_on(gfx::run(event_loop, net_tx));
 
-    rx.close().await;
+    // wait for net to finish
+    runtime.block_on(net_thread).unwrap();
 }
diff --git a/src/net.rs b/src/net.rs
new file mode 100644 (file)
index 0000000..323fe06
--- /dev/null
@@ -0,0 +1,252 @@
+use crate::{GfxEvent, NetEvent};
+use cgmath::{Deg, Point3, Vector3};
+use futures::future::OptionFuture;
+use mt_net::{CltSender, ReceiverExt, SenderExt, ToCltPkt, ToSrvPkt};
+use rand::RngCore;
+use sha2::Sha256;
+use srp::{client::SrpClient, groups::G_2048};
+use std::{future::Future, time::Duration};
+use tokio::{
+    sync::mpsc,
+    time::{interval, Instant, Interval},
+};
+use winit::event_loop::EventLoopProxy;
+
+enum AuthState {
+    Init(Interval),
+    Verify(Vec<u8>, SrpClient<'static, Sha256>),
+    Done,
+}
+
+struct Conn {
+    tx: CltSender,
+    auth: AuthState,
+    send_pos_iv: Option<Interval>,
+    username: String,
+    password: String,
+    pos: Point3<f32>,
+    pitch: Deg<f32>,
+    yaw: Deg<f32>,
+    events: EventLoopProxy<GfxEvent>,
+}
+
+fn maybe_tick(iv: Option<&mut Interval>) -> OptionFuture<impl Future<Output = Instant> + '_> {
+    OptionFuture::from(iv.map(Interval::tick))
+}
+
+pub(crate) async fn run(
+    evt_out: EventLoopProxy<GfxEvent>,
+    mut evt_in: mpsc::UnboundedReceiver<NetEvent>,
+) {
+    let (tx, mut rx, worker) = mt_net::connect("localhost:30000").await.unwrap();
+
+    let mut conn = Conn {
+        tx,
+        auth: AuthState::Init(interval(Duration::from_millis(100))),
+        send_pos_iv: None,
+        username: "shrek".into(), // shrek is love, shrek is life <3
+        password: "boobies".into(),
+        pos: Point3::new(0.0, 0.0, 0.0),
+        pitch: Deg(0.0),
+        yaw: Deg(0.0),
+        events: evt_out,
+    };
+
+    let init_pkt = ToSrvPkt::Init {
+        serialize_version: 29,
+        proto_version: 40..=40,
+        player_name: conn.username.clone(),
+        send_full_item_meta: false,
+    };
+
+    let worker_thread = tokio::spawn(worker.run());
+
+    loop {
+        tokio::select! {
+            pkt = rx.recv() => match pkt {
+                None => break,
+                Some(Err(e)) => eprintln!("{e}"),
+                Some(Ok(v)) => conn.handle_pkt(v).await,
+            },
+            Some(_) = maybe_tick(match &mut conn.auth {
+                AuthState::Init(iv) => Some(iv),
+                _ => None,
+            }) => {
+                conn.tx.send(&init_pkt).await.unwrap();
+            }
+            Some(_) = maybe_tick(conn.send_pos_iv.as_mut()) => {
+                conn.tx
+                    .send(&ToSrvPkt::PlayerPos(mt_net::PlayerPos {
+                        pos: conn.pos,
+                        vel: Vector3::new(0.0, 0.0, 0.0),
+                        pitch: conn.pitch,
+                        yaw: conn.yaw,
+                        keys: mt_net::enumset::EnumSet::empty(),
+                        fov: Deg(90.0).into(),
+                        wanted_range: 12,
+                    }))
+                    .await
+                    .unwrap();
+            }
+            evt = evt_in.recv() => {
+                match evt {
+                    Some(NetEvent::PlayerPos(pos, yaw, pitch)) => {
+                        conn.pos = pos;
+                        conn.yaw = yaw;
+                        conn.pitch = pitch;
+                    },
+                    Some(NetEvent::Ready) => {
+                        conn.tx
+                            .send(&ToSrvPkt::CltReady {
+                                major: 0,
+                                minor: 1,
+                                patch: 0,
+                                reserved: 0,
+                                version: format!("Minetest Rust {}", env!("CARGO_PKG_VERSION")),
+                                formspec: 4,
+                            })
+                            .await
+                            .unwrap();
+                    }
+                    None => conn.tx.close(),
+                }
+            }
+            _ = tokio::signal::ctrl_c() => {
+                conn.tx.close();
+            }
+        }
+    }
+
+    conn.events.send_event(GfxEvent::Close).ok(); // TODO: make sure to send this on panic
+    worker_thread.await.unwrap();
+}
+
+impl Conn {
+    async fn handle_pkt(&mut self, pkt: ToCltPkt) {
+        use ToCltPkt::*;
+
+        match pkt {
+            Hello {
+                auth_methods,
+                username: name,
+                ..
+            } => {
+                use mt_net::AuthMethod;
+
+                if !matches!(self.auth, AuthState::Init(_)) {
+                    return;
+                }
+
+                let srp = SrpClient::<Sha256>::new(&G_2048);
+
+                let mut rand_bytes = vec![0; 32];
+                rand::thread_rng().fill_bytes(&mut rand_bytes);
+
+                if self.username != name {
+                    panic!("username changed");
+                }
+
+                if auth_methods.contains(AuthMethod::FirstSrp) {
+                    let verifier = srp.compute_verifier(
+                        self.username.to_lowercase().as_bytes(),
+                        self.password.as_bytes(),
+                        &rand_bytes,
+                    );
+
+                    self.tx
+                        .send(&ToSrvPkt::FirstSrp {
+                            salt: rand_bytes,
+                            verifier,
+                            empty_passwd: self.password.is_empty(),
+                        })
+                        .await
+                        .unwrap();
+
+                    self.auth = AuthState::Done;
+                } else if auth_methods.contains(AuthMethod::Srp) {
+                    let a = srp.compute_public_ephemeral(&rand_bytes);
+
+                    self.tx
+                        .send(&ToSrvPkt::SrpBytesA { a, no_sha1: true })
+                        .await
+                        .unwrap();
+
+                    self.auth = AuthState::Verify(rand_bytes, srp);
+                } else {
+                    panic!("unsupported auth methods: {auth_methods:?}");
+                }
+            }
+            SrpBytesSaltB { salt, b } => {
+                if let AuthState::Verify(a, srp) = &self.auth {
+                    let m = srp
+                        .process_reply(
+                            a,
+                            self.username.to_lowercase().as_bytes(),
+                            self.password.as_bytes(),
+                            &salt,
+                            &b,
+                        )
+                        .unwrap()
+                        .proof()
+                        .into();
+
+                    self.tx.send(&ToSrvPkt::SrpBytesM { m }).await.unwrap();
+
+                    self.auth = AuthState::Done;
+                }
+            }
+            NodeDefs(defs) => {
+                self.events.send_event(GfxEvent::NodeDefs(defs.0)).ok();
+            }
+            Kick(reason) => {
+                println!("kicked: {reason}");
+            }
+            AcceptAuth { player_pos, .. } => {
+                self.tx
+                    .send(&ToSrvPkt::Init2 {
+                        lang: "en_US".into(), // localization is unironically overrated
+                    })
+                    .await
+                    .unwrap();
+
+                self.pos = player_pos;
+                self.send_pos_iv = Some(interval(Duration::from_millis(100)));
+            }
+            MovePlayer { pos, pitch, yaw } => {
+                self.pos = pos;
+                self.pitch = pitch;
+                self.yaw = yaw;
+
+                self.events
+                    .send_event(GfxEvent::PlayerPos(self.pos, self.pitch, self.yaw))
+                    .ok();
+            }
+            BlockData { pos, block } => {
+                self.events.send_event(GfxEvent::MapBlock(pos, block)).ok();
+                self.tx
+                    .send(&ToSrvPkt::GotBlocks {
+                        blocks: Vec::from([pos]),
+                    })
+                    .await
+                    .unwrap();
+            }
+            AnnounceMedia { files, .. } => {
+                self.tx
+                    .send(&ToSrvPkt::RequestMedia {
+                        filenames: files.into_keys().collect(), // TODO: cache
+                    })
+                    .await
+                    .ok();
+            }
+            Media { files, n, i } => {
+                self.events
+                    .send_event(GfxEvent::Media(files, i + 1 == n))
+                    .ok();
+            }
+            ChatMsg { text, .. } => {
+                println!("{text}");
+            }
+            _ => {}
+        }
+    }
+}