diff --git a/.gitignore b/.gitignore index 07db1ba..1b52c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,6 @@ debug/ target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dad53cc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.tabSize": 2, + "rust-analyzer.diagnostics.disabled": [ + //rust-analyzer issue #14269, + "unresolved-method", + "unresolved-import", + "unresolved-field" + ] +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8a4cca4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2317 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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 = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + +[[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 = "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 = "bracket-noise" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b7443d0990c69db7a83f376f0101d684c20a911698e5f932bffbda2c8b08dd" +dependencies = [ + "bracket-random", +] + +[[package]] +name = "bracket-random" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437be61484077b1ddb57002ce3c96b7d03cbf500b5d15157ee7e67e22332c39b" +dependencies = [ + "getrandom", + "js-sys", + "lazy_static", + "rand", + "rand_xorshift", + "regex", + "wasm-bindgen", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "cmake" +version = "0.1.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" +dependencies = [ + "cc", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[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 0.3.2", + "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 0.3.2", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossfont" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" +dependencies = [ + "cocoa", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "dwrote", + "foreign-types 0.5.0", + "freetype-rs", + "libc", + "log", + "objc", + "once_cell", + "pkg-config", + "servo-fontconfig", + "winapi", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +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 = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "serde", + "serde_derive", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "freetype-rs" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" +dependencies = [ + "bitflags", + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gilrs" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d0342acdc7b591d171212e17c9350ca02383b86d5f9af33c6e3598e03a6c57e" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6789d356476c3280a4e15365d23f62b4b4f1bcdac81fdd552f65807bce4666dd" +dependencies = [ + "core-foundation", + "io-kit-sys", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix 0.25.1", + "rusty-xinput", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "winapi", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" +dependencies = [ + "serde", +] + +[[package]] +name = "glium" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2766728ecb86014b91d3d687614b32d65aacbbdc887f424a7b03cba3ab593bf" +dependencies = [ + "backtrace", + "fnv", + "gl_generator", + "glutin", + "lazy_static", + "memoffset 0.6.5", + "smallvec", + "takeable-option", +] + +[[package]] +name = "glutin" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" +dependencies = [ + "cgl", + "cocoa", + "core-foundation", + "glutin_egl_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading", + "log", + "objc", + "once_cell", + "osmesa-sys", + "parking_lot", + "raw-window-handle 0.5.0", + "wayland-client", + "wayland-egl", + "winapi", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" +dependencies = [ + "gl_generator", + "winapi", +] + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" +dependencies = [ + "gl_generator", + "objc", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[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", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[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 = "io-kit-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7789f7f3c9686f96164f5109d69152de759e76e284f736bd57661c6df5091919" +dependencies = [ + "core-foundation-sys", + "mach", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[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_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kubi" +version = "0.1.0" +dependencies = [ + "anyhow", + "flume", + "gilrs", + "glam", + "glium", + "hashbrown 0.13.2", + "image", + "kubi-logging", + "kubi-shared", + "log", + "nohash-hasher", + "postcard", + "rayon", + "shipyard", + "strum", + "uflow", + "winapi", +] + +[[package]] +name = "kubi-logging" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "kubi-server" +version = "0.1.0" +dependencies = [ + "anyhow", + "flume", + "glam", + "hashbrown 0.13.2", + "kubi-logging", + "kubi-shared", + "log", + "nohash-hasher", + "postcard", + "rand", + "rayon", + "serde", + "shipyard", + "toml", + "uflow", +] + +[[package]] +name = "kubi-shared" +version = "0.1.0" +dependencies = [ + "anyhow", + "bracket-noise", + "glam", + "postcard", + "rand", + "rand_xoshiro", + "serde", + "shipyard", + "strum", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[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 = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[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 0.5.0", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" +dependencies = [ + "libc", + "log", + "ndk", + "ndk-context", + "ndk-macro", + "ndk-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[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 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[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 = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[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 = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +dependencies = [ + "shared_library", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + +[[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", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "postcard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa512cd0d087cc9f99ad30a1bf64795b67871edbead083ffc3a4dfafa59aa00" +dependencies = [ + "cobs", + "heapless", + "serde", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "packed_simd_2", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[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_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +dependencies = [ + "cty", +] + +[[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 = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "rusty-xinput" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2aa654bc32eb9ca14cce1a084abc9dfe43949a4547c35269a094c39272db3bb" +dependencies = [ + "lazy_static", + "log", + "winapi", +] + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + +[[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.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" +dependencies = [ + "crossfont", + "log", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shipyard" +version = "0.6.0" +source = "git+https://github.com/leudz/shipyard?rev=eb189f66#eb189f66ed2917440af3225d03b1feecb0d18bfd" +dependencies = [ + "hashbrown 0.12.3", + "lock_api", + "rayon", + "shipyard_proc", +] + +[[package]] +name = "shipyard_proc" +version = "0.3.0" +source = "git+https://github.com/leudz/shipyard?rev=eb189f66#eb189f66ed2917440af3225d03b1feecb0d18bfd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "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 = "spin" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "takeable-option" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" + +[[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.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-skia" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "safe_arch", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" +dependencies = [ + "arrayref", + "bytemuck", +] + +[[package]] +name = "toml" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "uflow" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be4d71c1c106a57b0333ac2c28bd4521e0b16a2b98fe84405cdf7f544be46b6" +dependencies = [ + "rand", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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-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", + "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-egl" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d" +dependencies = [ + "wayland-client", + "wayland-sys", +] + +[[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 = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +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-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winit" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" +dependencies = [ + "bitflags", + "cocoa", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk", + "ndk-glue", + "objc", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.4.3", + "raw-window-handle 0.5.0", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-protocols", + "web-sys", + "windows-sys 0.36.1", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[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" diff --git a/Cargo.toml b/Cargo.toml index b558ff3..cfbd2e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,35 +1,22 @@ -[package] -name = "kubi" -version = "0.1.0" -edition = "2021" - -[dependencies] -glium = "0.32" -image = { version = "0.24", default_features = false, features = ["png"] } -log = "0.4" -env_logger = "0.10" -strum = { version = "0.24", features = ["derive"] } -glam = { version = "0.22", features = ["debug-glam-assert", "mint", "fast-math"] } -hashbrown = "0.13" -rayon = "1.6" -shipyard = { version = "0.6", features = ["thread_local"] } -nohash-hasher = "0.2.0" -anyhow = "1.0" -flume = "0.10" -bracket-noise = "0.8" -#rkyv = "0.7" - -[profile.dev] -opt-level = 1 - -[profile.dev.package."*"] -opt-level = 1 - -[profile.dev.package.glium] -opt-level = 3 - -[profile.dev.package.bracket-noise] -opt-level = 3 - -[profile.dev.package.rayon] -opt-level = 3 +[workspace] +members = ["kubi", "kubi-server", "kubi-shared", "kubi-logging"] +resolver = "2" + +[profile.release-with-debug] +inherits = "release" +debug = true + +[profile.dev] +opt-level = 1 + +[profile.dev.package."*"] +opt-level = 1 + +[profile.dev.package.glium] +opt-level = 3 + +[profile.dev.package.bracket-noise] +opt-level = 3 + +[profile.dev.package.rayon] +opt-level = 3 diff --git a/Server.toml b/Server.toml new file mode 100644 index 0000000..1cfc2a3 --- /dev/null +++ b/Server.toml @@ -0,0 +1,7 @@ +[server] +address = "0.0.0.0:12345" +max_clients = 254 +timeout_ms = 10000 + +[world] +seed = 0xfeb_face_dead_cafe diff --git a/assets/blocks/cobblestone.png b/assets/blocks/cobblestone.png new file mode 100644 index 0000000..44b1324 Binary files /dev/null and b/assets/blocks/cobblestone.png differ diff --git a/assets/blocks/planks.png b/assets/blocks/planks.png new file mode 100644 index 0000000..c06e269 Binary files /dev/null and b/assets/blocks/planks.png differ diff --git a/assets/blocks/solid_water.png b/assets/blocks/solid_water.png new file mode 100644 index 0000000..7505981 Binary files /dev/null and b/assets/blocks/solid_water.png differ diff --git a/crabs.txt b/crabs.txt deleted file mode 100644 index aecc062..0000000 --- a/crabs.txt +++ /dev/null @@ -1 +0,0 @@ -sorry no crabs here diff --git a/kubi-logging/Cargo.toml b/kubi-logging/Cargo.toml new file mode 100644 index 0000000..c88c172 --- /dev/null +++ b/kubi-logging/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kubi-logging" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4" +env_logger = "0.10" diff --git a/src/logging.rs b/kubi-logging/src/lib.rs similarity index 70% rename from src/logging.rs rename to kubi-logging/src/lib.rs index 013b4e9..7a4c922 100644 --- a/src/logging.rs +++ b/kubi-logging/src/lib.rs @@ -1,14 +1,16 @@ -//! Custom env_logger options and styling +/// Custom env_logger options and styling use env_logger::{fmt::Color, Builder, Env}; use log::Level; use std::io::Write; +pub use log; +pub use env_logger; + +#[inline] pub fn init() { - let mut env = Env::default(); - if cfg!(debug_assertions) { - env = env.filter_or("RUST_LOG", "trace"); - } + let env = Env::default() + .filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn"); Builder::from_env(env) .format(|buf, record| { let mut level_style = buf.style(); @@ -18,6 +20,9 @@ pub fn init() { _ => Color::Blue }).set_bold(true); + let mut bold_style = buf.style(); + bold_style.set_bold(true); + let mut location_style = buf.style(); location_style.set_bold(true); location_style.set_dimmed(true); @@ -25,9 +30,11 @@ pub fn init() { let mut location_line_style = buf.style(); location_line_style.set_dimmed(true); + let text = format!("{}", record.args()); + writeln!( buf, - "{} {:<50}\t{}{}{}", + "{} {:<50}\t{}{}{}{}", level_style.value(match record.level() { Level::Error => "[e]", Level::Warn => "[w]", @@ -35,7 +42,8 @@ pub fn init() { Level::Debug => "[d]", Level::Trace => "[t]", }), - format!("{}", record.args()), + text, + bold_style.value((text.len() > 50).then_some("\n ╰─ ").unwrap_or_default()), location_style.value(record.target()), location_line_style.value(" :"), location_line_style.value(record.line().unwrap_or(0)) diff --git a/kubi-server/Cargo.toml b/kubi-server/Cargo.toml new file mode 100644 index 0000000..6fb5e3d --- /dev/null +++ b/kubi-server/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "kubi-server" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +kubi-shared = { path = "../kubi-shared" } +kubi-logging = { path = "../kubi-logging" } +log = "*" +shipyard = { git = "https://github.com/leudz/shipyard", rev = "eb189f66", features = ["thread_local"] } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +toml = "0.7" +glam = { version = "0.23", features = ["debug-glam-assert", "fast-math"] } +hashbrown = "0.13" +nohash-hasher = "0.2.0" +anyhow = "1.0" +rayon = "1.6" +flume = "0.10" +rand = "0.8" +uflow = "0.7" +postcard = { version = "1.0", features = ["alloc"] } + +[features] +default = [] +nightly = ["rand/nightly", "rand/simd_support", "serde/unstable", "glam/core-simd", "kubi-shared/nightly"] diff --git a/kubi-server/src/auth.rs b/kubi-server/src/auth.rs new file mode 100644 index 0000000..7d6dfae --- /dev/null +++ b/kubi-server/src/auth.rs @@ -0,0 +1,86 @@ +use shipyard::{UniqueView, NonSendSync}; +use uflow::{server::Event as ServerEvent, SendMode}; +use kubi_shared::networking::messages::{ + ClientToServerMessage, + ServerToClientMessage, + InitData, + C_CLIENT_HELLO +}; +use crate::{ + server::{ServerEvents, UdpServer, IsMessageOfType}, + config::ConfigTable +}; + +pub fn authenticate_players( + server: NonSendSync>, + events: UniqueView, + config: UniqueView +) { + for event in &events.0 { + // if let ServerEvent::MessageReceived { + // from, + // message: ClientToServerMessage::ClientHello { + // username, + // password + // } + // } = event { + + let ServerEvent::Receive(client_addr, data) = event else{ + continue + }; + let Some(client) = server.0.client(client_addr) else { + log::error!("Client doesn't exist"); + continue + }; + if !event.is_message_of_type::() { + continue + } + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + continue + }; + let ClientToServerMessage::ClientHello { username, password } = parsed_message else { + unreachable!() + }; + + log::info!("ClientHello; username={} password={:?}", username, password); + + // Handle password auth + if let Some(server_password) = &config.server.password { + if let Some(user_password) = &password { + if server_password != user_password { + let res = postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff { + reason: "Passwords don't match".into() + }).unwrap().into_boxed_slice(); + client.borrow_mut().send( + res, 0, SendMode::Reliable + ); + continue + } + } else { + let res = postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff { + reason: "This server is password protected".into() + }).unwrap().into_boxed_slice(); + client.borrow_mut().send( + res, 0, SendMode::Reliable + ); + continue + } + } + + //Spawn the user + //TODO Spawn the user on server side + + //Approve the user + let res = postcard::to_allocvec(&ServerToClientMessage::ServerHello { + init: InitData { + users: vec![] //TODO create init data + } + }).unwrap().into_boxed_slice(); + client.borrow_mut().send( + res, 0, SendMode::Reliable + ); + + log::info!("{username} joined the game!") + } +} diff --git a/kubi-server/src/client.rs b/kubi-server/src/client.rs new file mode 100644 index 0000000..086e398 --- /dev/null +++ b/kubi-server/src/client.rs @@ -0,0 +1,19 @@ +use shipyard::{Component, EntityId}; +use hashbrown::HashMap; +use nohash_hasher::BuildNoHashHasher; +use kubi_shared::networking::client::ClientId; + +#[derive(Component)] +pub struct Client(ClientId); + +pub struct ClientMap(HashMap>); +impl ClientMap { + pub fn new() -> Self { + Self(HashMap::with_hasher(BuildNoHashHasher::default())) + } +} +impl Default for ClientMap { + fn default() -> Self { + Self::new() + } +} diff --git a/kubi-server/src/config.rs b/kubi-server/src/config.rs new file mode 100644 index 0000000..3a6abf1 --- /dev/null +++ b/kubi-server/src/config.rs @@ -0,0 +1,31 @@ +use shipyard::{AllStoragesView, Unique}; +use serde::{Serialize, Deserialize}; +use std::{fs, net::SocketAddr}; + +#[derive(Serialize, Deserialize)] +pub struct ConfigTableServer { + pub address: SocketAddr, + pub max_clients: usize, + pub timeout_ms: u64, + pub password: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct ConfigTableWorld { + pub seed: u64, +} + +#[derive(Unique, Serialize, Deserialize)] +pub struct ConfigTable { + pub server: ConfigTableServer, + pub world: ConfigTableWorld, +} + +pub fn read_config( + storages: AllStoragesView, +) { + log::info!("Reading config..."); + let config_str = fs::read_to_string("Server.toml").expect("No config file found"); + let config: ConfigTable = toml::from_str(&config_str).expect("Invalid configuration file"); + storages.add_unique(config); +} diff --git a/kubi-server/src/main.rs b/kubi-server/src/main.rs new file mode 100644 index 0000000..ad24e18 --- /dev/null +++ b/kubi-server/src/main.rs @@ -0,0 +1,46 @@ +use shipyard::{World, Workload, IntoWorkload}; +use std::{thread, time::Duration}; + +pub(crate) mod util; +pub(crate) mod config; +pub(crate) mod server; +pub(crate) mod client; +//pub(crate) mod world; +pub(crate) mod auth; + +use config::read_config; +use server::{bind_server, update_server, log_server_errors}; +use auth::authenticate_players; +//use world::{update_world, init_world}; + +fn initialize() -> Workload { + ( + read_config, + bind_server, + //init_world, + ).into_workload() +} + +fn update() -> Workload { + ( + update_server, + ( + log_server_errors, + authenticate_players, + //update_world, + ).into_workload() + ).into_sequential_workload() +} + +fn main() { + kubi_logging::init(); + let world = World::new(); + world.add_workload(initialize); + world.add_workload(update); + world.run_workload(initialize).unwrap(); + log::info!("The server is now running"); + loop { + world.run_workload(update).unwrap(); + thread::sleep(Duration::from_millis(16)); + } +} diff --git a/kubi-server/src/server.rs b/kubi-server/src/server.rs new file mode 100644 index 0000000..8b84264 --- /dev/null +++ b/kubi-server/src/server.rs @@ -0,0 +1,62 @@ +use shipyard::{AllStoragesView, Unique, UniqueView, UniqueViewMut, NonSendSync}; +use uflow::{server::{Server, Event as ServerEvent, Config as ServerConfig}, EndpointConfig}; +use crate::config::ConfigTable; + +#[derive(Unique)] +#[repr(transparent)] +pub struct UdpServer(pub Server); + +#[derive(Unique, Default)] +pub struct ServerEvents(pub Vec); + +pub trait IsMessageOfType { + ///Checks if postcard-encoded message has a type + fn is_message_of_type(&self) -> bool; +} +impl IsMessageOfType for ServerEvent { + fn is_message_of_type(&self) -> bool { + let ServerEvent::Receive(_, data) = &self else { return false }; + if data.len() == 0 { return false } + data[0] == T + } +} + +pub fn bind_server( + storages: AllStoragesView, +) { + log::info!("Creating server..."); + let config = storages.borrow::>().unwrap(); + let server = Server::bind( + config.server.address, + ServerConfig { + max_total_connections: config.server.max_clients, + max_active_connections: config.server.max_clients, + enable_handshake_errors: true, + endpoint_config: EndpointConfig { + active_timeout_ms: config.server.timeout_ms, + ..Default::default() + }, + ..Default::default() + } + ).expect("Failed to create the server"); + storages.add_unique_non_send_sync(UdpServer(server)); + storages.add_unique(ServerEvents::default()); +} + +pub fn update_server( + mut server: NonSendSync>, + mut events: UniqueViewMut, +) { + events.0.clear(); + events.0.extend(server.0.step()); +} + +pub fn log_server_errors( + events: UniqueView, +) { + for event in &events.0 { + if let ServerEvent::Error(addr, error) = event { + log::error!("Server error addr: {addr} error: {error:?}"); + } + } +} diff --git a/kubi-server/src/util.rs b/kubi-server/src/util.rs new file mode 100644 index 0000000..2438d9e --- /dev/null +++ b/kubi-server/src/util.rs @@ -0,0 +1,3 @@ +pub fn log_error(error: anyhow::Error) { + log::error!("{}", error); +} diff --git a/kubi-server/src/world.rs b/kubi-server/src/world.rs new file mode 100644 index 0000000..4124e7b --- /dev/null +++ b/kubi-server/src/world.rs @@ -0,0 +1,119 @@ +use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView}; +use glam::IVec3; +use hashbrown::HashMap; +use kubi_shared::networking::messages::{ClientToServerMessage, ServerToClientMessage}; +use crate::{ + server::{UdpServer, ServerEvents}, + config::ConfigTable, + util::log_error, +}; + +pub mod chunk; +pub mod tasks; + +use chunk::Chunk; + +use self::{tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager}, chunk::ChunkState}; + +#[derive(Unique, Default)] +pub struct ChunkManager { + pub chunks: HashMap +} +impl ChunkManager { + pub fn new() -> Self { + Self::default() + } +} + +fn process_chunk_requests( + mut server: UniqueViewMut, + events: UniqueView, + mut chunk_manager: UniqueViewMut, + task_manager: UniqueView, + config: UniqueView +) { + for event in &events.0 { + if let ServerEvent::MessageReceived { + from: client_id, + message: ClientToServerMessage::ChunkSubRequest { + chunk: chunk_position + } + } = event { + let chunk_position = IVec3::from_array(*chunk_position); + if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) { + chunk.subscriptions.insert(*client_id); + //TODO Start task here if status is "Nothing" + if let Some(blocks) = &chunk.blocks { + server.0.send_message(*client_id, kubi_shared::networking::messages::ServerToClientMessage::ChunkResponse { + chunk: chunk_position.to_array(), + data: blocks.clone(), + queued: Vec::with_capacity(0) + }).map_err(log_error).ok(); + } + } else { + let mut chunk = Chunk::new(chunk_position); + chunk.state = ChunkState::Loading; + chunk.subscriptions.insert(*client_id); + chunk_manager.chunks.insert(chunk_position, chunk); + task_manager.spawn_task(ChunkTask::LoadChunk { + position: chunk_position, + seed: config.world.seed, + }); + } + } + } +} + +fn process_finished_tasks( + mut server: UniqueViewMut, + task_manager: UniqueView, + mut chunk_manager: UniqueViewMut, +) { + let mut limit: usize = 8; + while let Some(res) = task_manager.receive() { + let ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } = res; + let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) else { + log::warn!("Chunk discarded: Doesn't exist"); + continue + }; + if chunk.state != ChunkState::Loading { + log::warn!("Chunk discarded: Not Loading"); + continue + } + chunk.state = ChunkState::Loaded; + chunk.blocks = Some(blocks.clone()); + for &subscriber in &chunk.subscriptions { + server.0.send_message(subscriber, ServerToClientMessage::ChunkResponse { + chunk: chunk_position.to_array(), + data: blocks.clone(), + queued: queue + }).map_err(log_error).ok(); + } + log::debug!("Chunk {chunk_position} loaded, {} subs", chunk.subscriptions.len()); + //HACK: Implement proper flow control/reliable transport in kubi-udp + limit -= 1; + if limit == 0 { + break; + } + } +} + +fn init_chunk_manager( + storages: AllStoragesView +) { + storages.add_unique(ChunkManager::new()); +} + +pub fn init_world() -> Workload { + ( + init_chunk_manager, + init_chunk_task_manager, + ).into_workload() +} + +pub fn update_world() -> Workload { + ( + process_chunk_requests, + process_finished_tasks, + ).into_workload() +} diff --git a/kubi-server/src/world/chunk.rs b/kubi-server/src/world/chunk.rs new file mode 100644 index 0000000..bfecbcd --- /dev/null +++ b/kubi-server/src/world/chunk.rs @@ -0,0 +1,31 @@ +use glam::IVec3; +use hashbrown::HashSet; +use nohash_hasher::BuildNoHashHasher; +use kubi_shared::{ + chunk::BlockData, + networking::client::ClientId +}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ChunkState { + Nothing, + Loading, + Loaded, +} + +pub struct Chunk { + pub position: IVec3, + pub state: ChunkState, + pub blocks: Option, + pub subscriptions: HashSet>, +} +impl Chunk { + pub fn new(position: IVec3) -> Self { + Self { + position, + state: ChunkState::Nothing, + blocks: None, + subscriptions: HashSet::with_hasher(BuildNoHashHasher::default()), + } + } +} diff --git a/kubi-server/src/world/tasks.rs b/kubi-server/src/world/tasks.rs new file mode 100644 index 0000000..8094632 --- /dev/null +++ b/kubi-server/src/world/tasks.rs @@ -0,0 +1,59 @@ +use shipyard::{Unique, AllStoragesView}; +use flume::{unbounded, Sender, Receiver}; +use glam::IVec3; +use rayon::{ThreadPool, ThreadPoolBuilder}; +use anyhow::Result; +use kubi_shared::{ + chunk::BlockData, + worldgen::generate_world, + queue::QueuedBlock, +}; + +pub enum ChunkTask { + LoadChunk { + position: IVec3, + seed: u64, + } +} + +pub enum ChunkTaskResponse { + ChunkLoaded { + chunk_position: IVec3, + blocks: BlockData, + queue: Vec + } +} + +#[derive(Unique)] +pub struct ChunkTaskManager { + channel: (Sender, Receiver), + pool: ThreadPool, +} +impl ChunkTaskManager { + pub fn new() -> Result { + Ok(Self { + channel: unbounded(), + pool: ThreadPoolBuilder::new().build()? + }) + } + pub fn spawn_task(&self, task: ChunkTask) { + let sender = self.channel.0.clone(); + self.pool.spawn(move || { + sender.send(match task { + ChunkTask::LoadChunk { position: chunk_position, seed } => { + let (blocks, queue) = generate_world(chunk_position, seed); + ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } + } + }).unwrap() + }) + } + pub fn receive(&self) -> Option { + self.channel.1.try_recv().ok() + } +} + +pub fn init_chunk_task_manager( + storages: AllStoragesView +) { + storages.add_unique(ChunkTaskManager::new().expect("ChunkTaskManager Init failed")); +} diff --git a/kubi-shared/Cargo.toml b/kubi-shared/Cargo.toml new file mode 100644 index 0000000..a63255d --- /dev/null +++ b/kubi-shared/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "kubi-shared" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glam = { version = "0.23", features = ["debug-glam-assert", "fast-math", "serde"] } +shipyard = { git = "https://github.com/leudz/shipyard", rev = "eb189f66", default-features = false, features = ["std"] } +strum = { version = "0.24", features = ["derive"] } +postcard = { version = "1.0", features = ["alloc"] } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +anyhow = "1.0" +bracket-noise = "0.8" +rand = { version = "0.8", default_features = false, features = ["std", "min_const_gen"] } +rand_xoshiro = "0.6" + +[features] +default = [] +nightly = ["rand/nightly", "rand/simd_support", "serde/unstable", "glam/core-simd"] diff --git a/kubi-shared/src/block.rs b/kubi-shared/src/block.rs new file mode 100644 index 0000000..b2f0bee --- /dev/null +++ b/kubi-shared/src/block.rs @@ -0,0 +1,211 @@ +use serde::{Serialize, Deserialize}; +use strum::EnumIter; + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, EnumIter)] +#[repr(u8)] +pub enum BlockTexture { + Stone, + Dirt, + GrassTop, + GrassSide, + Sand, + Bedrock, + Wood, + WoodTop, + Leaf, + Torch, + TallGrass, + Snow, + GrassSideSnow, + Cobblestone, + Planks, + WaterSolid, +} + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, EnumIter)] +#[repr(u8)] +pub enum Block { + Air, + Marker, + Stone, + Dirt, + Grass, + Sand, + Cobblestone, + TallGrass, + Planks, + Torch, + Wood, + Leaf, + Water, +} + +impl Block { + #[inline] + pub const fn descriptor(self) -> BlockDescriptor { + match self { + Self::Air => BlockDescriptor { + name: "air", + render: RenderType::None, + collision: CollisionType::None, + raycast_collision: false, + }, + Self::Marker => BlockDescriptor { + name: "marker", + render: RenderType::None, + collision: CollisionType::None, + raycast_collision: false, + }, + Self::Stone => BlockDescriptor { + name: "stone", + render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Stone)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Dirt => BlockDescriptor { + name: "dirt", + render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Dirt)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Grass => BlockDescriptor { + name: "grass", + render: RenderType::SolidBlock(CubeTexture::top_sides_bottom( + BlockTexture::GrassTop, + BlockTexture::GrassSide, + BlockTexture::Dirt + )), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Sand => BlockDescriptor { + name: "sand", + render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Sand)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Cobblestone => BlockDescriptor { + name: "cobblestone", + render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Cobblestone)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::TallGrass => BlockDescriptor { + name: "tall grass", + render: RenderType::CrossShape(CrossTexture::all(BlockTexture::TallGrass)), + collision: CollisionType::None, + raycast_collision: true, + }, + Self::Planks => BlockDescriptor { + name: "planks", + render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Planks)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Torch => BlockDescriptor { + name: "torch", + render: RenderType::CrossShape(CrossTexture::all(BlockTexture::Torch)), + collision: CollisionType::None, + raycast_collision: true, + }, + Self::Wood => BlockDescriptor { + name: "leaf", + render: RenderType::SolidBlock(CubeTexture::horizontal_vertical(BlockTexture::Wood, BlockTexture::WoodTop)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Leaf => BlockDescriptor { + name: "leaf", + render: RenderType::BinaryTransparency(CubeTexture::all(BlockTexture::Leaf)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Water => BlockDescriptor { + name: "water", + render: RenderType::BinaryTransparency(CubeTexture::all(BlockTexture::WaterSolid)), + collision: CollisionType::None, + raycast_collision: true, + }, + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct BlockDescriptor { + pub name: &'static str, + pub render: RenderType, + pub collision: CollisionType, + pub raycast_collision: bool, +} +// impl BlockDescriptor { +// pub fn of(block: Block) -> Self { +// block.descriptor() +// } +// } + +#[derive(Clone, Copy, Debug)] +pub struct CubeTexture { + pub top: BlockTexture, + pub bottom: BlockTexture, + pub left: BlockTexture, + pub right: BlockTexture, + pub front: BlockTexture, + pub back: BlockTexture, +} +impl CubeTexture { + pub const fn top_sides_bottom(top: BlockTexture, sides: BlockTexture, bottom: BlockTexture) -> Self { + Self { + top, + bottom, + left: sides, + right: sides, + front: sides, + back: sides, + } + } + pub const fn horizontal_vertical(horizontal: BlockTexture, vertical: BlockTexture) -> Self { + Self::top_sides_bottom(vertical, horizontal, vertical) + } + pub const fn all(texture: BlockTexture) -> Self { + Self::horizontal_vertical(texture, texture) + } +} + +#[derive(Clone, Copy, Debug)] +pub struct CrossTextureSides { + pub front: BlockTexture, + pub back: BlockTexture +} +impl CrossTextureSides { + pub const fn all(texture: BlockTexture) -> Self { + Self { + front: texture, + back: texture + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct CrossTexture(pub CrossTextureSides, pub CrossTextureSides); +impl CrossTexture { + pub const fn all(texture: BlockTexture) -> Self { + Self( + CrossTextureSides::all(texture), + CrossTextureSides::all(texture) + ) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CollisionType { + None, + Solid, +} + +#[derive(Clone, Copy, Debug)] +pub enum RenderType { + None, + SolidBlock(CubeTexture), + BinaryTransparency(CubeTexture), + CrossShape(CrossTexture), +} diff --git a/kubi-shared/src/chunk.rs b/kubi-shared/src/chunk.rs new file mode 100644 index 0000000..3456db5 --- /dev/null +++ b/kubi-shared/src/chunk.rs @@ -0,0 +1,4 @@ +use crate::block::Block; + +pub const CHUNK_SIZE: usize = 32; +pub type BlockData = Box<[[[Block; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]>; diff --git a/kubi-shared/src/entity.rs b/kubi-shared/src/entity.rs new file mode 100644 index 0000000..bf2f183 --- /dev/null +++ b/kubi-shared/src/entity.rs @@ -0,0 +1,18 @@ +use shipyard::Component; + +#[derive(Component)] +pub struct Entity; + +#[derive(Component)] +pub struct Health { + pub current: u8, + pub max: u8, +} +impl Health { + fn new(health: u8) -> Self { + Self { + current: health, + max: health + } + } +} diff --git a/kubi-shared/src/lib.rs b/kubi-shared/src/lib.rs new file mode 100644 index 0000000..10ea49c --- /dev/null +++ b/kubi-shared/src/lib.rs @@ -0,0 +1,8 @@ +pub mod block; +pub mod networking; +pub mod worldgen; +pub mod chunk; +pub mod transform; +pub mod entity; +pub mod player; +pub mod queue; diff --git a/kubi-shared/src/networking.rs b/kubi-shared/src/networking.rs new file mode 100644 index 0000000..05bff68 --- /dev/null +++ b/kubi-shared/src/networking.rs @@ -0,0 +1,3 @@ +pub mod messages; +pub mod state; +pub mod client; diff --git a/kubi-shared/src/networking/client.rs b/kubi-shared/src/networking/client.rs new file mode 100644 index 0000000..35f3744 --- /dev/null +++ b/kubi-shared/src/networking/client.rs @@ -0,0 +1,3 @@ +pub type ClientId = u16; +pub type ClientKey = u16; + diff --git a/kubi-shared/src/networking/messages.rs b/kubi-shared/src/networking/messages.rs new file mode 100644 index 0000000..9213547 --- /dev/null +++ b/kubi-shared/src/networking/messages.rs @@ -0,0 +1,72 @@ +use std::num::NonZeroUsize; +use serde::{Serialize, Deserialize}; +use crate::{chunk::BlockData, queue::QueuedBlock}; + +pub type IVec3Arr = [i32; 3]; +pub type Vec3Arr = [f32; 3]; +pub type QuatArr = [f32; 3]; + +pub const PROTOCOL_ID: u16 = 2; + +pub const C_CLIENT_HELLO: u8 = 0; +pub const C_POSITION_CHANGED: u8 = 1; +pub const C_CHUNK_SUB_REQUEST: u8 = 2; + +#[derive(Serialize, Deserialize, Clone)] +#[repr(u8)] +pub enum ClientToServerMessage { + ClientHello { + username: String, + password: Option, + } = C_CLIENT_HELLO, + PositionChanged { + position: Vec3Arr, + velocity: Vec3Arr, + direction: QuatArr, + } = C_POSITION_CHANGED, + ChunkSubRequest { + chunk: IVec3Arr, + } = C_CHUNK_SUB_REQUEST, +} + +pub const S_SERVER_HELLO: u8 = 0; +pub const S_SERVER_FUCK_OFF: u8 = 1; +pub const S_PLAYER_POSITION_CHANGED: u8 = 2; +pub const S_CHUNK_RESPONSE: u8 = 3; + +#[derive(Serialize, Deserialize, Clone)] +#[repr(u8)] +pub enum ServerToClientMessage { + ServerHello { + init: InitData + } = S_SERVER_HELLO, + ServerFuckOff { + reason: String, + } = S_SERVER_FUCK_OFF, + PlayerPositionChanged { + client_id: u8, + position: Vec3Arr, + direction: QuatArr, + } = S_PLAYER_POSITION_CHANGED, + ChunkResponse { + chunk: IVec3Arr, + data: BlockData, + queued: Vec, + } = S_CHUNK_RESPONSE, +} + +//--- + +#[derive(Serialize, Deserialize, Clone)] +pub struct UserInitData { + pub client_id: NonZeroUsize, //maybe use the proper type instead + pub username: String, + pub position: Vec3Arr, + pub velocity: Vec3Arr, + pub direction: QuatArr, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct InitData { + pub users: Vec +} diff --git a/kubi-shared/src/networking/state.rs b/kubi-shared/src/networking/state.rs new file mode 100644 index 0000000..98492c3 --- /dev/null +++ b/kubi-shared/src/networking/state.rs @@ -0,0 +1,15 @@ +use shipyard::{Unique, Component}; + +// disconnected => connect => join => load => ingame +#[derive(Unique, Component, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(u8)] +pub enum ClientJoinState { + /// Not connected yet + Disconnected, + /// Client has connected to the game, but hasn't authenticated yet + Connected, + /// Client has joined the game, but hasn't loaded the world yet + Joined, + /// Client is currently ingame + InGame, +} diff --git a/kubi-shared/src/player.rs b/kubi-shared/src/player.rs new file mode 100644 index 0000000..d2df58c --- /dev/null +++ b/kubi-shared/src/player.rs @@ -0,0 +1,4 @@ +use shipyard::Component; + +#[derive(Component)] +pub struct Player; diff --git a/kubi-shared/src/queue.rs b/kubi-shared/src/queue.rs new file mode 100644 index 0000000..7216756 --- /dev/null +++ b/kubi-shared/src/queue.rs @@ -0,0 +1,11 @@ +use glam::IVec3; +use serde::{Serialize, Deserialize}; +use crate::block::Block; + +#[derive(Serialize, Deserialize, Clone, Copy, Debug)] +pub struct QueuedBlock { + pub position: IVec3, + pub block_type: Block, + /// Only replace air blocks + pub soft: bool, +} diff --git a/kubi-shared/src/transform.rs b/kubi-shared/src/transform.rs new file mode 100644 index 0000000..c918deb --- /dev/null +++ b/kubi-shared/src/transform.rs @@ -0,0 +1,8 @@ +use shipyard::Component; +use glam::{Mat4, Mat3}; + +#[derive(Component, Clone, Copy, Debug, Default)] +pub struct Transform(pub Mat4); + +#[derive(Component, Clone, Copy, Debug, Default)] +pub struct Transform2d(pub Mat3); diff --git a/kubi-shared/src/worldgen.rs b/kubi-shared/src/worldgen.rs new file mode 100644 index 0000000..56fd807 --- /dev/null +++ b/kubi-shared/src/worldgen.rs @@ -0,0 +1,333 @@ +use bracket_noise::prelude::*; +use rand::prelude::*; +use glam::{IVec3, ivec3, Vec3Swizzles, IVec2}; +use rand_xoshiro::Xoshiro256StarStar; +use crate::{ + chunk::{BlockData, CHUNK_SIZE}, + block::Block, + queue::QueuedBlock, +}; + +fn mountain_ramp(mut x: f32) -> f32 { + x = x * 2.0; + if x < 0.4 { + 0.5 * x + } else if x < 0.55 { + 4. * (x - 0.4) + 0.2 + } else { + 0.4444 * (x - 0.55) + 0.8 + } +} + +fn local_height(height: i32, chunk_position: IVec3) -> usize { + let offset = chunk_position * CHUNK_SIZE as i32; + (height - offset.y).clamp(0, CHUNK_SIZE as i32) as usize +} + +fn local_y_position(height: i32, chunk_position: IVec3) -> Option { + let offset = chunk_position * CHUNK_SIZE as i32; + let position = height - offset.y; + (0..CHUNK_SIZE as i32).contains(&position).then_some(position as usize) +} + + +pub fn generate_world(chunk_position: IVec3, seed: u64) -> (BlockData, Vec) { + let offset = chunk_position * CHUNK_SIZE as i32; + let mut blocks = Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]); + let mut queue = Vec::with_capacity(0); + + let mut smart_place = |blocks: &mut BlockData, position: IVec3, block: Block| { + if position.to_array().iter().any(|&x| !(0..CHUNK_SIZE).contains(&(x as usize))) { + let event_pos = offset + position; + queue.retain(|block: &QueuedBlock| { + block.position != event_pos + }); + queue.push(QueuedBlock { + position: event_pos, + block_type: block, + soft: true + }); + } else { + blocks[position.x as usize][position.y as usize][position.z as usize] = block; + } + }; + + let mut height_noise = FastNoise::seeded(seed); + height_noise.set_fractal_type(FractalType::FBM); + height_noise.set_fractal_octaves(4); + height_noise.set_frequency(0.003); + + let mut elevation_noise = FastNoise::seeded(seed.rotate_left(1)); + elevation_noise.set_fractal_type(FractalType::FBM); + elevation_noise.set_fractal_octaves(1); + elevation_noise.set_frequency(0.001); + + let mut cave_noise_a = FastNoise::seeded(seed.rotate_left(2)); + cave_noise_a.set_fractal_type(FractalType::FBM); + cave_noise_a.set_fractal_octaves(2); + cave_noise_a.set_frequency(0.01); + + let mut cave_noise_b = FastNoise::seeded(seed.rotate_left(3)); + cave_noise_b.set_fractal_type(FractalType::FBM); + cave_noise_b.set_fractal_octaves(3); + cave_noise_b.set_frequency(0.015); + + let mut cave_noise_holes = FastNoise::seeded(seed.rotate_left(4)); + cave_noise_holes.set_fractal_type(FractalType::FBM); + cave_noise_holes.set_fractal_octaves(2); + cave_noise_holes.set_frequency(0.005); + + let mut ravine_nose_line = FastNoise::seeded(seed.rotate_left(5)); + ravine_nose_line.set_fractal_type(FractalType::Billow); + ravine_nose_line.set_fractal_octaves(2); + ravine_nose_line.set_frequency(0.005); + + let mut ravine_noise_location = FastNoise::seeded(seed.rotate_left(6)); + ravine_noise_location.set_fractal_type(FractalType::FBM); + ravine_noise_location.set_fractal_octaves(1); + ravine_noise_location.set_frequency(0.005); + + let mut river_noise = FastNoise::seeded(seed.rotate_left(7)); + river_noise.set_fractal_type(FractalType::Billow); + river_noise.set_fractal_octaves(2); + river_noise.set_frequency(0.5 * 0.005); + + let mut rng = Xoshiro256StarStar::seed_from_u64( + seed + ^ ((chunk_position.x as u32 as u64) << 0) + ^ ((chunk_position.z as u32 as u64) << 32) + ); + let rng_map_a: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + let rng_map_b: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + + //Generate height map + let mut within_heightmap = false; + let mut deco_heightmap = [[None; CHUNK_SIZE]; CHUNK_SIZE]; + + for x in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + let (noise_x, noise_y) = ((offset.x + x as i32) as f32, (offset.z + z as i32) as f32); + //sample noises (that are needed right now) + let raw_heightmap_value = height_noise.get_noise(noise_x, noise_y); + let raw_elevation_value = elevation_noise.get_noise(noise_x, noise_y); + let raw_ravine_location_value = ravine_noise_location.get_noise(noise_x, noise_y); + //compute height + let mut is_surface = true; + let mut river_fill_height = None; + let height = { + let local_elevation = raw_elevation_value.powi(4).sqrt(); + let mut height = (mountain_ramp(raw_heightmap_value) * local_elevation * 100.) as i32; + //Flatten valleys + if height < 0 { + height /= 2; + } + //Generate rivers + { + let river_width = (height as f32 / -5.).clamp(0.5, 1.); + let river_value = river_noise.get_noise(noise_x, noise_y); + if ((-0.00625 * river_width)..(0.00625 * river_width)).contains(&(river_value.powi(2))) { + is_surface = false; + river_fill_height = Some(height - 1); + //river_fill_height = Some(-3); + height -= (river_width * 15. * ((0.00625 * river_width) - river_value.powi(2)) * (1. / (0.00625 * river_width))).round() as i32; + } + } + //Generate ravines + if height < 0 { + if raw_ravine_location_value > 0.4 { + let raw_ravine_value = ravine_nose_line.get_noise(noise_x, noise_y); + if (-0.0125..0.0125).contains(&(raw_ravine_value.powi(2))) { + is_surface = false; + height -= (100. * (0.0125 - raw_ravine_value.powi(2)) * (1. / 0.0125)).round() as i32; + } + } + } + height + }; + //add to heightmap + if is_surface { + deco_heightmap[x as usize][z as usize] = Some(height); + //place dirt + for y in 0..local_height(height, chunk_position) { + blocks[x][y][z] = Block::Dirt; + within_heightmap = true; + } + //place stone + for y in 0..local_height(height - 5 - (raw_heightmap_value * 5.) as i32, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + //place grass + if let Some(y) = local_y_position(height, chunk_position) { + blocks[x][y][z] = Block::Grass; + within_heightmap = true; + } + } else { + if let Some(river_fill_height) = river_fill_height { + //Place water + for y in 0..local_height(river_fill_height, chunk_position) { + blocks[x][y][z] = Block::Water; + within_heightmap = true; + } + //Place stone + for y in 0..local_height(height - 1, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + //Place dirt + if let Some(y) = local_y_position(height, chunk_position) { + blocks[x][y][z] = Block::Dirt; + within_heightmap = true; + } + } else { + //Place stone + for y in 0..local_height(height, chunk_position) { + blocks[x][y][z] = Block::Stone; + within_heightmap = true; + } + } + } + } + } + + //Carve out caves + if within_heightmap { + for z in 0..CHUNK_SIZE { + for y in 0..CHUNK_SIZE { + for x in 0..CHUNK_SIZE { + if blocks[x][y][z] != Block::Stone { continue } + + let cave_size = ((offset.y + y as i32) as f32 / -100.).clamp(0., 1.); + let inv_cave_size = 1. - cave_size; + if cave_size < 0.1 { continue } + + let position = ivec3(x as i32, y as i32, z as i32) + offset; + + let is_cave = || { + let raw_cavemap_value_a = cave_noise_a.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + let raw_cavemap_value_b = cave_noise_b.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_a) && + ((cave_size * -0.15)..=(cave_size * 0.15)).contains(&raw_cavemap_value_b) + }; + let is_hole_cave = || { + let raw_cavemap_value_holes = cave_noise_holes.get_noise3d(position.x as f32, position.y as f32, position.z as f32); + ((0.9 + (0.1 * inv_cave_size))..=1.0).contains(&raw_cavemap_value_holes.abs()) + }; + + if is_cave() || is_hole_cave() { + blocks[x][y][z] = Block::Air; + if deco_heightmap[x][z] == Some(y as i32 + offset.y) { + deco_heightmap[x][z] = None + } + } + } + } + } + } + + //Add decorations + for x in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + //get height + let Some(height) = deco_heightmap[x][z] else { continue }; + //check for air + // if blocks[x][local_y][z] == Block::Air { + // continue + // } + //place tall grass + if rng_map_a[x][z] < 0.03 { + if let Some(y) = local_y_position(height + 1, chunk_position) { + blocks[x][y][z] = Block::TallGrass; + } + } + //place trees! + if rng_map_a[x][z] < 0.001 { + //Place wood (no smart_place needed here!) + let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; + for tree_y in 0..tree_height { + if let Some(y) = local_y_position(height + 1 + tree_y, chunk_position) { + blocks[x][y][z] = Block::Wood; + } + } + + let tree_height = 4 + (rng_map_b[x][z] * 3.).round() as i32; + + //Place leaf blocks + if let Some(y) = local_y_position(height + 1, chunk_position) { + let tree_pos = ivec3(x as i32, y as i32, z as i32); + // Place wood (smart_place) + // for tree_y in 0..tree_height { + // smart_place(&mut blocks, tree_pos + tree_y * IVec3::Y, Block::Wood); + // } + // Part that wraps around the tree + { + let tree_leaf_height = tree_height - 3; + let leaf_width = 2; + for tree_y in tree_leaf_height..tree_height { + for tree_x in (-leaf_width)..=leaf_width { + for tree_z in (-leaf_width)..=leaf_width { + let tree_offset = ivec3(tree_x, tree_y, tree_z); + if tree_offset.xz() == IVec2::ZERO { continue } + smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); + } + } + } + } + //part above the tree + { + let leaf_above_height = 2; + let leaf_width = 1; + for tree_y in tree_height..(tree_height + leaf_above_height) { + for tree_x in (-leaf_width)..=leaf_width { + for tree_z in (-leaf_width)..=leaf_width { + let tree_offset = ivec3(tree_x, tree_y, tree_z); + smart_place(&mut blocks, tree_pos + tree_offset, Block::Leaf); + } + } + } + } + } + } + } + } + + (blocks, queue) + + // let mut cave_noise = FastNoise::seeded(seed); + // cave_noise.set_fractal_type(FractalType::FBM); + // cave_noise.set_frequency(0.1); + + // let mut dirt_noise = FastNoise::seeded(seed.rotate_left(1)); + // dirt_noise.set_fractal_type(FractalType::FBM); + // dirt_noise.set_frequency(0.1); + + // + + // if chunk_position.y >= 0 { + // if chunk_position.y == 0 { + // for x in 0..CHUNK_SIZE { + // for z in 0..CHUNK_SIZE { + // blocks[x][0][z] = Block::Dirt; + // blocks[x][1][z] = Block::Grass; + // } + // } + // } + // } else { + // for x in 0..CHUNK_SIZE { + // for y in 0..CHUNK_SIZE { + // for z in 0..CHUNK_SIZE { + // let position = ivec3(x as i32, y as i32, z as i32) + offset; + // let v_cave_noise = cave_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32 - 10.0).clamp(0., 1.); + // let v_dirt_noise = dirt_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32).clamp(0., 1.); + // if v_cave_noise > 0.5 { + // blocks[x][y][z] = Block::Stone; + // } else if v_dirt_noise > 0.5 { + // blocks[x][y][z] = Block::Dirt; + // } + // } + // } + // } + // } + // blocks + +} diff --git a/kubi/Cargo.toml b/kubi/Cargo.toml new file mode 100644 index 0000000..f2ca9d0 --- /dev/null +++ b/kubi/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "kubi" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +kubi-shared = { path = "../kubi-shared" } +kubi-logging = { path = "../kubi-logging" } +log = "*" +glium = "0.32" +glam = { version = "0.23", features = ["debug-glam-assert", "fast-math"] } +image = { version = "0.24", default_features = false, features = ["png"] } +strum = { version = "0.24", features = ["derive"] } +hashbrown = "0.13" +rayon = "1.6" +shipyard = { git = "https://github.com/leudz/shipyard", rev = "eb189f66", default-features = false, features = ["std", "proc", "thread_local"] } +nohash-hasher = "0.2.0" +anyhow = "1.0" +flume = "0.10" +gilrs = { version = "0.10", default_features = false, features = ["xinput"] } +uflow = "0.7" +postcard = { version = "1.0", features = ["alloc"] } + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3" } + +[features] +default = [] +parallel = ["shipyard/parallel"] +nightly = ["glam/core-simd", "kubi-shared/nightly"] diff --git a/shaders/colored.frag b/kubi/shaders/colored.frag similarity index 66% rename from shaders/colored.frag rename to kubi/shaders/colored.frag index 3e09c7a..b7a0615 100644 --- a/shaders/colored.frag +++ b/kubi/shaders/colored.frag @@ -1,4 +1,6 @@ -#version 150 core +#version 300 es + +precision highp float; out vec4 out_color; uniform vec4 color; diff --git a/shaders/colored.vert b/kubi/shaders/colored.vert similarity index 83% rename from shaders/colored.vert rename to kubi/shaders/colored.vert index 083513e..c6b3ef8 100644 --- a/shaders/colored.vert +++ b/kubi/shaders/colored.vert @@ -1,4 +1,6 @@ -#version 150 core +#version 300 es + +precision highp float; in vec3 position; uniform mat4 model; diff --git a/kubi/shaders/gui/progressbar.frag b/kubi/shaders/gui/progressbar.frag new file mode 100644 index 0000000..e598692 --- /dev/null +++ b/kubi/shaders/gui/progressbar.frag @@ -0,0 +1,17 @@ +#version 300 es + +precision highp float; + +in vec2 v_uv; +out vec4 out_color; +uniform float progress; +uniform vec4 color; +uniform vec4 bg_color; + +void main() { + if (v_uv.x <= progress) { + out_color = color; + } else { + out_color = bg_color; + } +} diff --git a/kubi/shaders/gui/progressbar.vert b/kubi/shaders/gui/progressbar.vert new file mode 100644 index 0000000..2b43f0c --- /dev/null +++ b/kubi/shaders/gui/progressbar.vert @@ -0,0 +1,12 @@ +#version 300 es + +in vec2 position; +out vec2 v_uv; +uniform mat4 ui_view; +uniform mat3 transform; + +void main() { + v_uv = position; + vec2 transformed = (transform * vec3(position, 1.)).xy; + gl_Position = ui_view * vec4(transformed, 0., 1.); +} diff --git a/kubi/shaders/selection_box.frag b/kubi/shaders/selection_box.frag new file mode 100644 index 0000000..5311d6f --- /dev/null +++ b/kubi/shaders/selection_box.frag @@ -0,0 +1,11 @@ +#version 300 es + +precision highp float; + +out vec4 color; +uniform vec4 u_color; + +void main() { + color = u_color; + // color -= vec4(0, 0, 0, 0.1 * sin(gl_FragCoord.x) * cos(gl_FragCoord.y)); +} diff --git a/shaders/selection_box.vert b/kubi/shaders/selection_box.vert similarity index 82% rename from shaders/selection_box.vert rename to kubi/shaders/selection_box.vert index f825013..049488d 100644 --- a/shaders/selection_box.vert +++ b/kubi/shaders/selection_box.vert @@ -1,4 +1,6 @@ -#version 150 core +#version 300 es + +precision highp float; in vec3 position; uniform ivec3 u_position; diff --git a/shaders/world.frag b/kubi/shaders/world.frag similarity index 68% rename from shaders/world.frag rename to kubi/shaders/world.frag index e07b5e1..f6054c2 100644 --- a/shaders/world.frag +++ b/kubi/shaders/world.frag @@ -1,4 +1,7 @@ -#version 150 core +#version 300 es + +precision highp float; +precision lowp sampler2DArray; in vec3 v_normal; in vec2 v_uv; @@ -9,6 +12,10 @@ uniform sampler2DArray tex; void main() { // base color from texture color = texture(tex, vec3(v_uv, v_tex_index)); + // discard fully transparent pixels + if (color.w <= 0.0) { + discard; + } //basic "lighting" float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z); color *= vec4(vec3(light), 1.); diff --git a/shaders/world.vert b/kubi/shaders/world.vert similarity index 92% rename from shaders/world.vert rename to kubi/shaders/world.vert index de3d2e7..996a1ec 100644 --- a/shaders/world.vert +++ b/kubi/shaders/world.vert @@ -1,4 +1,6 @@ -#version 150 core +#version 300 es + +precision highp float; //TODO pack this data: // uint position_normal_uv diff --git a/kubi/src/block_placement.rs b/kubi/src/block_placement.rs new file mode 100644 index 0000000..888311c --- /dev/null +++ b/kubi/src/block_placement.rs @@ -0,0 +1,94 @@ +use shipyard::{UniqueViewMut, UniqueView, View, IntoIter, ViewMut, EntitiesViewMut, Component, Workload, IntoWorkload}; +use glium::glutin::event::VirtualKeyCode; +use kubi_shared::{ + block::Block, + queue::QueuedBlock, +}; +use crate::{ + player::MainPlayer, + world::{raycast::{LookingAtBlock, RAYCAST_STEP}, queue::BlockUpdateQueue}, + input::{Inputs, PrevInputs, RawKbmInputState}, + events::{EventComponent, player_actions::PlayerActionEvent}, +}; + +#[derive(Component)] +pub struct PlayerHolding(pub Block); +impl Default for PlayerHolding { + fn default() -> Self { + Self(Block::Cobblestone) + } +} + +const BLOCK_KEY_MAP: &[(VirtualKeyCode, Block)] = &[ + (VirtualKeyCode::Key1, Block::Cobblestone), + (VirtualKeyCode::Key2, Block::Planks), + (VirtualKeyCode::Key3, Block::Dirt), + (VirtualKeyCode::Key4, Block::Grass), + (VirtualKeyCode::Key5, Block::Sand), + (VirtualKeyCode::Key6, Block::Stone), + (VirtualKeyCode::Key7, Block::Torch), + (VirtualKeyCode::Key8, Block::Leaf), +]; + +fn pick_block_with_number_keys( + main_player: View, + mut holding: ViewMut, + input: UniqueView, +) { + let Some((_, mut holding)) = (&main_player, &mut holding).iter().next() else { return }; + for &(key, block) in BLOCK_KEY_MAP { + if input.keyboard_state.contains(&key) { + holding.0 = block; + return + } + } +} + +fn block_placement_system( + main_player: View, + holding: View, + raycast: View, + input: UniqueView, + prev_input: UniqueView, + mut block_event_queue: UniqueViewMut, + mut entities: EntitiesViewMut, + mut events: ViewMut, + mut player_events: ViewMut, +) { + let action_place = input.action_b && !prev_input.0.action_b; + let action_break = input.action_a && !prev_input.0.action_a; + if action_place ^ action_break { + //get components + let Some((_, ray, block)) = (&main_player, &raycast, &holding).iter().next() else { return }; + let Some(ray) = ray.0 else { return }; + //get coord and block type + let (place_position, place_block) = if action_place { + if block.0 == Block::Air { return } + let position = (ray.position - ray.direction * (RAYCAST_STEP + 0.001)).floor().as_ivec3(); + (position, block.0) + } else { + (ray.block_position, Block::Air) + }; + //queue place + block_event_queue.push(QueuedBlock { + position: place_position, + block_type: place_block, + soft: place_block != Block::Air, + }); + //send event + entities.add_entity( + (&mut events, &mut player_events), + (EventComponent, PlayerActionEvent::UpdatedBlock { + position: place_position, + block: place_block, + }) + ); + } +} + +pub fn update_block_placement() -> Workload { + ( + pick_block_with_number_keys, + block_placement_system + ).into_workload() +} diff --git a/src/camera.rs b/kubi/src/camera.rs similarity index 100% rename from src/camera.rs rename to kubi/src/camera.rs diff --git a/src/camera/frustum.rs b/kubi/src/camera/frustum.rs similarity index 96% rename from src/camera/frustum.rs rename to kubi/src/camera/frustum.rs index 9042dd4..7fc00f3 100644 --- a/src/camera/frustum.rs +++ b/kubi/src/camera/frustum.rs @@ -8,7 +8,7 @@ // three layers of stolen code, yay! use glam::{Vec3A, Vec4, Mat3A, vec3a, Vec3, vec4}; -use shipyard::{ViewMut, IntoIter, View}; +use shipyard::{ViewMut, IntoIter, View, track}; use crate::transform::Transform; use super::Camera; @@ -122,9 +122,9 @@ fn intersection(planes: &[Vec4; pub fn update_frustum( mut cameras: ViewMut, - transforms: View + transforms: View ) { - for (camera, _) in (&mut cameras, transforms.inserted_or_modified()).iter() { - camera.frustum = Frustum::compute(camera); + for (mut camera, _) in (&mut cameras, transforms.inserted_or_modified()).iter() { + camera.frustum = Frustum::compute(&camera); } } diff --git a/kubi/src/camera/matrices.rs b/kubi/src/camera/matrices.rs new file mode 100644 index 0000000..b6ded27 --- /dev/null +++ b/kubi/src/camera/matrices.rs @@ -0,0 +1,48 @@ +use glam::{Vec3, Mat4}; +use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator}; +use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent}; +use super::Camera; + +//maybe parallelize these two? + +fn update_view_matrix( + mut vm_camera: ViewMut, + v_transform: View +) { + for (mut camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() { + let (_, rotation, translation) = transform.0.to_scale_rotation_translation(); + let direction = (rotation.normalize() * Vec3::NEG_Z).normalize(); + camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up); + } +} + +fn update_perspective_matrix( + mut vm_camera: ViewMut, + size: UniqueView, +) { + for mut camera in (&mut vm_camera).iter() { + camera.perspective_matrix = Mat4::perspective_rh_gl( + camera.fov, + size.0.x as f32 / size.0.y as f32, + camera.z_near, + camera.z_far, + ) + } +} + +fn need_perspective_calc( + v_camera: View, + resize_event: View, +) -> bool { + (resize_event.len() > 0) || + (v_camera.iter().any(|camera| { + camera.perspective_matrix == Mat4::default() + })) +} + +pub fn update_matrices() -> Workload { + ( + update_view_matrix, + update_perspective_matrix.run_if(need_perspective_calc), + ).into_workload() +} diff --git a/kubi/src/color.rs b/kubi/src/color.rs new file mode 100644 index 0000000..5e0728b --- /dev/null +++ b/kubi/src/color.rs @@ -0,0 +1,12 @@ +use glam::{Vec4, vec4}; + +#[inline(always)] +pub fn color_rgba(r: u8, g: u8, b: u8, a: u8) -> Vec4 { + vec4(r as f32 / 255., g as f32 / 255., b as f32 / 255., a as f32 / 255.) +} + +#[inline(always)] +pub fn color_hex(c: u32) -> Vec4 { + let c = c.to_be_bytes(); + color_rgba(c[0], c[1], c[2], c[3]) +} diff --git a/kubi/src/connecting_screen.rs b/kubi/src/connecting_screen.rs new file mode 100644 index 0000000..047311b --- /dev/null +++ b/kubi/src/connecting_screen.rs @@ -0,0 +1,12 @@ +use kubi_shared::networking::state::ClientJoinState; +use shipyard::{UniqueViewMut, UniqueView}; +use crate::state::{NextState, GameState}; + +pub fn switch_to_loading_if_connected( + mut next_state: UniqueViewMut, + client_state: UniqueView, +) { + if *client_state == ClientJoinState::Joined { + next_state.0 = Some(GameState::LoadingWorld); + } +} diff --git a/src/control_flow.rs b/kubi/src/control_flow.rs similarity index 86% rename from src/control_flow.rs rename to kubi/src/control_flow.rs index 253dbdb..9dad6e1 100644 --- a/src/control_flow.rs +++ b/kubi/src/control_flow.rs @@ -1,12 +1,12 @@ use shipyard::{UniqueView, UniqueViewMut, Unique, AllStoragesView}; use glium::glutin::{event::VirtualKeyCode, event_loop::ControlFlow}; -use crate::input::RawInputState; +use crate::input::RawKbmInputState; #[derive(Unique)] pub struct SetControlFlow(pub Option); pub fn exit_on_esc( - raw_inputs: UniqueView, + raw_inputs: UniqueView, mut control_flow: UniqueViewMut ) { if raw_inputs.keyboard_state.contains(&VirtualKeyCode::Escape) { diff --git a/src/cursor_lock.rs b/kubi/src/cursor_lock.rs similarity index 98% rename from src/cursor_lock.rs rename to kubi/src/cursor_lock.rs index 2c3303b..6512017 100644 --- a/src/cursor_lock.rs +++ b/kubi/src/cursor_lock.rs @@ -3,7 +3,6 @@ use crate::rendering::Renderer; use glium::glutin::window::CursorGrabMode; #[derive(Unique)] -#[track(All)] pub struct CursorLock(pub bool); pub fn update_cursor_lock_state( diff --git a/src/delta_time.rs b/kubi/src/delta_time.rs similarity index 100% rename from src/delta_time.rs rename to kubi/src/delta_time.rs diff --git a/src/events.rs b/kubi/src/events.rs similarity index 76% rename from src/events.rs rename to kubi/src/events.rs index b7154da..45fcfff 100644 --- a/src/events.rs +++ b/kubi/src/events.rs @@ -1,6 +1,7 @@ use glam::UVec2; -use shipyard::{World, Component, AllStoragesViewMut, SparseSet}; +use shipyard::{World, Component, AllStoragesViewMut, SparseSet, NonSendSync, UniqueView}; use glium::glutin::event::{Event, DeviceEvent, DeviceId, WindowEvent}; +use crate::rendering::Renderer; pub mod player_actions; @@ -50,6 +51,19 @@ pub fn process_glutin_events(world: &mut World, event: &Event<'_, ()>) { } } +pub fn initial_resize_event( + mut storages: AllStoragesViewMut, +) { + let (w, h) = { + let renderer = storages.borrow::>>().unwrap(); + renderer.display.get_framebuffer_dimensions() + }; + storages.add_entity(( + EventComponent, + WindowResizedEvent(UVec2::new(w, h)) + )); +} + pub fn clear_events( mut all_storages: AllStoragesViewMut, ) { diff --git a/src/events/player_actions.rs b/kubi/src/events/player_actions.rs similarity index 90% rename from src/events/player_actions.rs rename to kubi/src/events/player_actions.rs index 09b8a0c..7f87606 100644 --- a/src/events/player_actions.rs +++ b/kubi/src/events/player_actions.rs @@ -1,8 +1,7 @@ -use shipyard::{Component, View, ViewMut, EntitiesViewMut, IntoIter}; +use shipyard::{Component, View, ViewMut, EntitiesViewMut, IntoIter, track}; use glam::{IVec3, Quat, Vec3}; - +use kubi_shared::block::Block; use crate::{ - world::block::Block, player::MainPlayer, transform::Transform }; @@ -21,7 +20,7 @@ pub enum PlayerActionEvent { } pub fn generate_move_events( - transforms: View, + transforms: View, player: View, mut entities: EntitiesViewMut, mut events: ViewMut, diff --git a/src/fly_controller.rs b/kubi/src/fly_controller.rs similarity index 87% rename from src/fly_controller.rs rename to kubi/src/fly_controller.rs index 5b303f4..57276cb 100644 --- a/src/fly_controller.rs +++ b/kubi/src/fly_controller.rs @@ -1,5 +1,5 @@ use glam::{Vec3, Mat4, Quat, EulerRot, Vec2}; -use shipyard::{Component, View, ViewMut, IntoIter, UniqueView, Workload, IntoWorkload}; +use shipyard::{Component, View, ViewMut, IntoIter, UniqueView, Workload, IntoWorkload, track}; use std::f32::consts::PI; use crate::{transform::Transform, input::Inputs, settings::GameSettings, delta_time::DeltaTime}; @@ -13,11 +13,11 @@ pub fn update_controllers() -> Workload { ).into_workload() } -const MAX_PITCH: f32 = PI/2. - 0.025; +const MAX_PITCH: f32 = PI/2. - 0.05; fn update_look( controllers: View, - mut transforms: ViewMut, + mut transforms: ViewMut, inputs: UniqueView, settings: UniqueView, dt: UniqueView, @@ -37,12 +37,12 @@ fn update_look( fn update_movement( controllers: View, - mut transforms: ViewMut, + mut transforms: ViewMut, inputs: UniqueView, dt: UniqueView, ) { if inputs.movement == Vec2::ZERO { return } - let movement = inputs.movement.normalize_or_zero() * 30. * dt.0.as_secs_f32(); + let movement = inputs.movement * 30. * dt.0.as_secs_f32(); for (_, mut transform) in (&controllers, &mut transforms).iter() { let (scale, rotation, mut translation) = transform.0.to_scale_rotation_translation(); translation += (rotation * Vec3::NEG_Z).normalize() * movement.y; diff --git a/kubi/src/gui.rs b/kubi/src/gui.rs new file mode 100644 index 0000000..e45af3a --- /dev/null +++ b/kubi/src/gui.rs @@ -0,0 +1,78 @@ +use shipyard::{Component, Unique, Workload, IntoWorkload, AllStoragesView, View, UniqueViewMut, IntoIter}; +use glam::{Vec4, Mat4}; +use crate::{color::color_hex, events::WindowResizedEvent}; + +pub mod text_widget; +pub mod progressbar; + +use progressbar::render_progressbars; + +//TODO compute gui scale on window resize +#[derive(Unique, Clone, Copy, Debug, Default)] +pub struct GuiView(pub Mat4); + +#[derive(Component, Clone, Copy, Debug, Default)] +pub struct GuiComponent; + +#[derive(Component, Clone, Copy, Debug)] +pub struct PrimaryColor(pub Vec4); +impl Default for PrimaryColor { + fn default() -> Self { + Self(color_hex(0x156cddff)) + } +} + +#[derive(Component, Clone, Copy, Debug)] +pub struct SecondaryColor(pub Vec4); +impl Default for SecondaryColor { + fn default() -> Self { + Self(color_hex(0xc9d5e4ff)) + } +} + +fn update_gui_view( + mut view: UniqueViewMut, + resize: View, +) { + let Some(&size) = resize.iter().next() else { + return + }; + let [w, h] = size.0.to_array(); + view.0 = Mat4::orthographic_rh_gl(0.0, w as f32, h as f32, 0.0, -1.0, 1.0); +} + +pub fn init_gui( + storages: AllStoragesView +) { + storages.add_unique(GuiView::default()); +} + +pub fn update_gui() -> Workload { + ( + update_gui_view + ).into_workload() +} + +pub fn render_gui() -> Workload { + ( + render_progressbars + ).into_workload() +} + +// pub fn gui_testing( +// mut storages: AllStoragesViewMut, +// ) { +// storages.add_entity(( +// GuiComponent, +// Transform2d(Mat3::from_scale_angle_translation( +// vec2(1920., 16.), +// 0., +// vec2(0., 0.) +// )), +// ProgressbarComponent { +// progress: 0.33 +// }, +// PrimaryColor::default(), +// SecondaryColor::default(), +// )); +// } diff --git a/kubi/src/gui/progressbar.rs b/kubi/src/gui/progressbar.rs new file mode 100644 index 0000000..0fe9b26 --- /dev/null +++ b/kubi/src/gui/progressbar.rs @@ -0,0 +1,46 @@ +use shipyard::{UniqueView, UniqueViewMut, NonSendSync, View, Component, IntoIter, IntoWithId, Get, track}; +use glium::{Surface, uniform, DrawParameters}; +use crate::{ + prefabs::ProgressbarShaderPrefab, + rendering::{ + RenderTarget, + primitives::rect::RectPrimitive + }, + transform::Transform2d, +}; +use super::{GuiComponent, PrimaryColor, SecondaryColor, GuiView}; + +#[derive(Component, Debug, Clone, Copy, Default)] +pub struct ProgressbarComponent { + pub progress: f32 +} + +pub fn render_progressbars( + mut target: NonSendSync>, + rect: NonSendSync>, + program: NonSendSync>, + view: UniqueView, + components: View, + transforms: View, + progressbars: View, + primary: View, + secondary: View, +) { + for (eid, (_, transform, progress)) in (&components, &transforms, &progressbars).iter().with_id() { + let primary_color = primary.get(eid).copied().unwrap_or_default(); + let secondary_color = secondary.get(eid).copied().unwrap_or_default(); + target.0.draw( + &rect.0, + &rect.1, + &program.0, + &uniform! { + transform: transform.0.to_cols_array_2d(), + ui_view: view.0.to_cols_array_2d(), + progress: progress.progress, + color: primary_color.0.to_array(), + bg_color: secondary_color.0.to_array(), + }, + &DrawParameters::default() + ).unwrap(); + } +} diff --git a/kubi/src/gui/text_widget.rs b/kubi/src/gui/text_widget.rs new file mode 100644 index 0000000..31c4422 --- /dev/null +++ b/kubi/src/gui/text_widget.rs @@ -0,0 +1 @@ +//TODO text widget diff --git a/kubi/src/init.rs b/kubi/src/init.rs new file mode 100644 index 0000000..c36a747 --- /dev/null +++ b/kubi/src/init.rs @@ -0,0 +1,21 @@ +use shipyard::{AllStoragesView, UniqueViewMut}; +use std::{env, net::SocketAddr}; +use crate::{ + networking::{GameType, ServerAddress}, + state::{GameState, NextState} +}; + +pub fn initialize_from_args( + all_storages: AllStoragesView, +) { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let address = args[1].parse::().expect("invalid address"); + all_storages.add_unique(GameType::Muliplayer); + all_storages.add_unique(ServerAddress(address)); + all_storages.borrow::>().unwrap().0 = Some(GameState::Connecting); + } else { + all_storages.add_unique(GameType::Singleplayer); + all_storages.borrow::>().unwrap().0 = Some(GameState::LoadingWorld); + } +} diff --git a/kubi/src/input.rs b/kubi/src/input.rs new file mode 100644 index 0000000..fe9fd57 --- /dev/null +++ b/kubi/src/input.rs @@ -0,0 +1,138 @@ +use gilrs::{Gilrs, GamepadId, Button, Event, Axis}; +use glam::{Vec2, DVec2, vec2}; +use glium::glutin::event::{DeviceEvent, VirtualKeyCode, ElementState}; +use hashbrown::HashSet; +use nohash_hasher::BuildNoHashHasher; +use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync}; +use crate::events::InputDeviceEvent; + +#[derive(Unique, Clone, Copy, Default, Debug)] +pub struct Inputs { + pub movement: Vec2, + pub look: Vec2, + pub action_a: bool, + pub action_b: bool, +} + +#[derive(Unique, Clone, Copy, Default, Debug)] +pub struct PrevInputs(pub Inputs); + +#[derive(Unique, Clone, Default, Debug)] +pub struct RawKbmInputState { + pub keyboard_state: HashSet>, + pub button_state: [bool; 32], + pub mouse_delta: DVec2 +} + +#[derive(Unique)] +pub struct GilrsWrapper(Gilrs); + +#[derive(Unique, Default, Clone, Copy)] +pub struct ActiveGamepad(Option); + +//maybe we should manage gamepad state ourselves just like keyboard? +//at least for the sake of consitency + +fn process_events( + device_events: View, + mut input_state: UniqueViewMut, +) { + input_state.mouse_delta = DVec2::ZERO; + for event in device_events.iter() { + match event.event { + DeviceEvent::MouseMotion { delta } => { + input_state.mouse_delta = DVec2::from(delta); + }, + DeviceEvent::Key(input) => { + if let Some(keycode) = input.virtual_keycode { + match input.state { + ElementState::Pressed => input_state.keyboard_state.insert(keycode), + ElementState::Released => input_state.keyboard_state.remove(&keycode), + }; + } + }, + DeviceEvent::Button { button, state } => { + if button < 32 { + input_state.button_state[button as usize] = matches!(state, ElementState::Pressed); + } + }, + _ => () + } + } +} + +fn process_gilrs_events( + mut gilrs: NonSendSync>, + mut active_gamepad: UniqueViewMut +) { + while let Some(Event { id, event: _, time: _ }) = gilrs.0.next_event() { + active_gamepad.0 = Some(id); + } +} + +fn input_start( + mut inputs: UniqueViewMut, + mut prev_inputs: UniqueViewMut, +) { + prev_inputs.0 = *inputs; + *inputs = Inputs::default(); +} + +fn update_input_state ( + raw_inputs: UniqueView, + mut inputs: UniqueViewMut, +) { + inputs.movement += Vec2::new( + raw_inputs.keyboard_state.contains(&VirtualKeyCode::D) as u32 as f32 - + raw_inputs.keyboard_state.contains(&VirtualKeyCode::A) as u32 as f32, + raw_inputs.keyboard_state.contains(&VirtualKeyCode::W) as u32 as f32 - + raw_inputs.keyboard_state.contains(&VirtualKeyCode::S) as u32 as f32 + ); + inputs.look += raw_inputs.mouse_delta.as_vec2(); + inputs.action_a |= raw_inputs.button_state[1]; + inputs.action_b |= raw_inputs.button_state[3]; +} + +fn update_input_state_gamepad ( + gilrs: NonSendSync>, + active_gamepad: UniqueView, + mut inputs: UniqueViewMut, +) { + if let Some(gamepad) = active_gamepad.0.map(|id| gilrs.0.gamepad(id)) { + let left_stick = vec2(gamepad.value(Axis::LeftStickX), gamepad.value(Axis::LeftStickY)); + let right_stick = vec2(gamepad.value(Axis::RightStickX), -gamepad.value(Axis::RightStickY)); + inputs.movement += left_stick; + inputs.look += right_stick; + inputs.action_a |= gamepad.is_pressed(Button::South); + inputs.action_b |= gamepad.is_pressed(Button::East); + } +} + +fn input_end( + mut inputs: UniqueViewMut, +) { + if inputs.movement.length() >= 1. { + inputs.movement = inputs.movement.normalize(); + } +} + +pub fn init_input ( + storages: AllStoragesView +) { + storages.add_unique_non_send_sync(GilrsWrapper(Gilrs::new().expect("Failed to initialize Gilrs"))); + storages.add_unique(ActiveGamepad::default()); + storages.add_unique(Inputs::default()); + storages.add_unique(PrevInputs::default()); + storages.add_unique(RawKbmInputState::default()); +} + +pub fn process_inputs() -> Workload { + ( + process_events, + process_gilrs_events, + input_start, + update_input_state, + update_input_state_gamepad, + input_end, + ).into_workload() +} diff --git a/kubi/src/loading_screen.rs b/kubi/src/loading_screen.rs new file mode 100644 index 0000000..550a7db --- /dev/null +++ b/kubi/src/loading_screen.rs @@ -0,0 +1,104 @@ +use shipyard::{UniqueView, UniqueViewMut, Workload, IntoWorkload, EntityId, Unique, AllStoragesViewMut, ViewMut, Get, SystemModificator, track}; +use glium::glutin::event::VirtualKeyCode; +use glam::{Mat3, vec2}; +use crate::{ + world::ChunkStorage, + state::{GameState, NextState, is_changing_state}, + transform::Transform2d, + gui::{ + GuiComponent, + progressbar::ProgressbarComponent + }, + rendering::{WindowSize, if_resized}, + input::RawKbmInputState, +}; + +#[derive(Unique, Clone, Copy)] +struct ProgressbarId(EntityId); + +fn spawn_loading_screen( + mut storages: AllStoragesViewMut, +) { + let size = *storages.borrow::>().unwrap(); + let entity = storages.add_entity(( + GuiComponent, + Transform2d(Mat3::from_scale_angle_translation( + vec2(size.0.x as f32, 16.), + 0., + vec2(0., 0.) + )), + ProgressbarComponent { + progress: 0.33 + }, + )); + storages.add_unique(ProgressbarId(entity)); +} + +fn resize_progress_bar( + size: UniqueView, + bar: UniqueView, + mut transforms: ViewMut +) { + let mut trans = (&mut transforms).get(bar.0).unwrap(); + trans.0.x_axis.x = size.0.x as f32; +} + +fn update_progress_bar_progress ( + world: UniqueView, + mut bar: ViewMut, + eid: UniqueView, +) { + let mut bar = (&mut bar).get(eid.0).unwrap(); + let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| { + acc + chunk.desired_state.matches_current(chunk.current_state) as usize + }); + let total = world.chunks.len(); + let progress = loaded as f32 / total as f32; + bar.progress = progress; +} + +fn switch_to_ingame_if_loaded( + world: UniqueView, + mut state: UniqueViewMut +) { + if world.chunks.is_empty() { + return + } + if world.chunks.iter().all(|(_, chunk)| { + chunk.desired_state.matches_current(chunk.current_state) + }) { + log::info!("Finished loading chunks"); + state.0 = Some(GameState::InGame); + } +} + +fn override_loading( + kbm_state: UniqueView, + mut state: UniqueViewMut +) { + if kbm_state.keyboard_state.contains(&VirtualKeyCode::F) { + state.0 = Some(GameState::InGame); + } +} + +fn despawn_loading_screen_if_switching_state( + mut storages: AllStoragesViewMut, +) { + let state = storages.borrow::>().unwrap().0.unwrap(); + if state != GameState::LoadingWorld { + let progress_bar = storages.borrow::>().unwrap().0; + storages.delete_entity(progress_bar); + storages.remove_unique::().unwrap(); + } +} + +pub fn update_loading_screen() -> Workload { + ( + spawn_loading_screen.run_if_missing_unique::(), + resize_progress_bar.run_if(if_resized), + update_progress_bar_progress, + override_loading, + switch_to_ingame_if_loaded, + despawn_loading_screen_if_switching_state.run_if(is_changing_state), + ).into_workload() +} diff --git a/src/main.rs b/kubi/src/main.rs similarity index 55% rename from src/main.rs rename to kubi/src/main.rs index 68a3e31..75503a0 100644 --- a/src/main.rs +++ b/kubi/src/main.rs @@ -1,10 +1,14 @@ -// allowed because systems often need a lot of arguments -#![allow(clippy::too_many_arguments)] +#![cfg_attr( + all(windows, not(debug_assertions)), + windows_subsystem = "windows" +)] +#![allow(clippy::too_many_arguments)] // allowed because systems often need a lot of arguments use shipyard::{ World, Workload, IntoWorkload, UniqueView, UniqueViewMut, - NonSendSync + NonSendSync, WorkloadModificator, + SystemModificator }; use glium::{ glutin::{ @@ -15,13 +19,12 @@ use glium::{ use glam::vec3; use std::time::Instant; -mod logging; +pub use kubi_shared::transform; pub(crate) mod rendering; pub(crate) mod world; pub(crate) mod player; pub(crate) mod prefabs; -pub(crate) mod transform; pub(crate) mod settings; pub(crate) mod camera; pub(crate) mod events; @@ -31,19 +34,30 @@ pub(crate) mod block_placement; pub(crate) mod delta_time; pub(crate) mod cursor_lock; pub(crate) mod control_flow; +pub(crate) mod state; +pub(crate) mod gui; +pub(crate) mod networking; +pub(crate) mod init; +pub(crate) mod color; +pub(crate) mod loading_screen; +pub(crate) mod connecting_screen; use world::{ init_game_world, loading::update_loaded_world_around_player, - raycast::update_raycasts + raycast::update_raycasts, + queue::apply_queued_blocks, + tasks::{inject_network_responses_into_manager_queue, ChunkTaskManager}, ChunkStorage }; -use player::spawn_player; +use player::{spawn_player, MainPlayer}; use prefabs::load_prefabs; use settings::load_settings; use camera::compute_cameras; use events::{ - clear_events, process_glutin_events, - player_actions::generate_move_events + clear_events, + process_glutin_events, + initial_resize_event, + player_actions::generate_move_events, }; use input::{init_input, process_inputs}; use fly_controller::update_controllers; @@ -52,49 +66,86 @@ use rendering::{ RenderTarget, BackgroundColor, clear_background, - primitives::init_simple_box_buffers, + init_window_size, + update_window_size, + primitives::init_primitives, selection_box::render_selection_box, world::draw_world, - world::draw_current_chunk_border, + world::draw_current_chunk_border, }; -use block_placement::block_placement_system; +use block_placement::update_block_placement; use delta_time::{DeltaTime, init_delta_time}; use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now}; use control_flow::{exit_on_esc, insert_control_flow_unique, SetControlFlow}; +use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting}; +use networking::{update_networking, is_multiplayer, disconnect_on_exit}; +use init::initialize_from_args; +use gui::{render_gui, init_gui, update_gui}; +use loading_screen::update_loading_screen; +use connecting_screen::switch_to_loading_if_connected; fn startup() -> Workload { ( + initial_resize_event, + init_window_size, load_settings, load_prefabs, - init_simple_box_buffers, + init_primitives, insert_lock_state, + init_state, + initialize_from_args, lock_cursor_now, init_input, - init_game_world, - spawn_player, + init_gui, insert_control_flow_unique, init_delta_time, ).into_workload() } fn update() -> Workload { ( - process_inputs, - update_controllers, - generate_move_events, - update_loaded_world_around_player, - update_raycasts, - block_placement_system, + update_window_size, update_cursor_lock_state, + process_inputs, + ( + init_game_world.run_if_missing_unique::(), + spawn_player.run_if_storage_empty::(), + ).into_sequential_workload().run_if(is_ingame_or_loading).tag("game_init"), + ( + update_networking, + inject_network_responses_into_manager_queue.run_if(is_ingame_or_loading).skip_if_missing_unique::(), + ).into_sequential_workload().run_if(is_multiplayer).tag("networking").after_all("game_init"), + ( + switch_to_loading_if_connected + ).into_workload().run_if(is_connecting).after_all("networking"), + ( + update_loading_screen, + ).into_workload().run_if(is_loading).after_all("game_init"), + ( + update_loaded_world_around_player, + ).into_workload().run_if(is_ingame_or_loading).after_all("game_init"), + ( + update_controllers, + generate_move_events, + update_raycasts, + update_block_placement, + apply_queued_blocks, + ).into_workload().run_if(is_ingame), compute_cameras, + update_gui, + update_state, exit_on_esc, + disconnect_on_exit.run_if(is_multiplayer), ).into_workload() } fn render() -> Workload { ( clear_background, - draw_world, - draw_current_chunk_border, - render_selection_box, + ( + draw_world, + draw_current_chunk_border, + render_selection_box, + ).into_sequential_workload().run_if(is_ingame), + render_gui, ).into_sequential_workload() } fn after_frame_end() -> Workload { @@ -103,15 +154,28 @@ fn after_frame_end() -> Workload { ).into_workload() } -fn main() { - logging::init(); +#[cfg(all(windows, not(debug_assertions)))] +fn attach_console() { + use winapi::um::wincon::{AttachConsole, ATTACH_PARENT_PROCESS}; + unsafe { AttachConsole(ATTACH_PARENT_PROCESS); } +} - //Create event loop - let event_loop = EventLoop::new(); +fn main() { + //Attach console on release builds on windows + #[cfg(all(windows, not(debug_assertions)))] attach_console(); + + //Print version + println!("{:─^54}", format!("[ ▄▀ Kubi client v. {} ]", env!("CARGO_PKG_VERSION"))); + + //Init env_logger + kubi_logging::init(); //Create a shipyard world let mut world = World::new(); + //Create event loop + let event_loop = EventLoop::new(); + //Add systems and uniques, Init and load things world.add_unique_non_send_sync(Renderer::init(&event_loop)); world.add_unique(BackgroundColor(vec3(0.5, 0.5, 1.))); @@ -148,9 +212,9 @@ fn main() { last_update = now; } - //Run update workflow + //Run update workflows world.run_workload(update).unwrap(); - + //Start rendering (maybe use custom views for this?) let target = { let renderer = world.borrow::>>().unwrap(); diff --git a/kubi/src/networking.rs b/kubi/src/networking.rs new file mode 100644 index 0000000..31450ac --- /dev/null +++ b/kubi/src/networking.rs @@ -0,0 +1,169 @@ +use shipyard::{Unique, AllStoragesView, UniqueView, UniqueViewMut, Workload, IntoWorkload, EntitiesViewMut, Component, ViewMut, SystemModificator, View, IntoIter, WorkloadModificator}; +use glium::glutin::event_loop::ControlFlow; +use std::net::SocketAddr; +use uflow::client::{Client, Config as ClientConfig, Event as ClientEvent}; +use kubi_shared::networking::{ + messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO}, + state::ClientJoinState +}; +use crate::{events::EventComponent, control_flow::SetControlFlow}; + +#[derive(Unique, Clone, Copy, PartialEq, Eq)] +pub enum GameType { + Singleplayer, + Muliplayer +} + +#[derive(Unique, Clone, Copy, PartialEq, Eq)] +pub struct ServerAddress(pub SocketAddr); + +#[derive(Unique)] +pub struct UdpClient(pub Client); + +#[derive(Component)] +pub struct NetworkEvent(pub ClientEvent); + +impl NetworkEvent { + ///Checks if postcard-encoded message has a type + pub fn is_message_of_type(&self) -> bool { + let ClientEvent::Receive(data) = &self.0 else { return false }; + if data.len() == 0 { return false } + data[0] == T + } +} + +#[derive(Component)] +pub struct NetworkMessageEvent(pub ServerToClientMessage); + +fn connect_client( + storages: AllStoragesView +) { + log::info!("Creating client"); + let address = storages.borrow::>().unwrap(); + let client = Client::connect(address.0, ClientConfig::default()).expect("Client connection failed"); + storages.add_unique(UdpClient(client)); + storages.add_unique(ClientJoinState::Disconnected); +} + +fn poll_client( + mut client: UniqueViewMut, + mut entities: EntitiesViewMut, + mut events: ViewMut, + mut network_events: ViewMut, +) { + entities.bulk_add_entity(( + &mut events, + &mut network_events, + ), client.0.step().map(|event| { + (EventComponent, NetworkEvent(event)) + })); +} + +fn set_client_join_state_to_connected( + mut join_state: UniqueViewMut +) { + log::info!("Setting ClientJoinState"); + *join_state = ClientJoinState::Connected; +} + +fn say_hello( + mut client: UniqueViewMut, +) { + log::info!("Authenticating"); + client.0.send( + postcard::to_allocvec( + &ClientToServerMessage::ClientHello { + username: "Sbeve".into(), + password: None + } + ).unwrap().into_boxed_slice(), + 0, + uflow::SendMode::Reliable + ); +} + +fn check_server_hello_response( + network_events: View, + mut join_state: UniqueViewMut +) { + for event in network_events.iter() { + let ClientEvent::Receive(data) = &event.0 else { + continue + }; + if !event.is_message_of_type::() { + continue + } + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + continue + }; + let ServerToClientMessage::ServerHello { init } = parsed_message else { + unreachable!() + }; + //TODO handle init data + *join_state = ClientJoinState::Joined; + log::info!("Joined the server!"); + return; + } +} + +pub fn update_networking() -> Workload { + ( + connect_client.run_if_missing_unique::(), + poll_client, + ( + set_client_join_state_to_connected, + say_hello, + ).into_workload().run_if(if_just_connected), + ( + check_server_hello_response, + ).into_workload().run_if(is_join_state::<{ClientJoinState::Connected as u8}>) + ).into_sequential_workload() //HACK Weird issues with shipyard removed +} + +pub fn disconnect_on_exit( + control_flow: UniqueView, + mut client: UniqueViewMut, +) { + if let Some(ControlFlow::ExitWithCode(_)) = control_flow.0 { + if client.0.is_active() { + client.0.flush(); + client.0.disconnect(); + while client.0.is_active() { client.0.step().for_each(|_|()); } + log::info!("Client disconnected"); + } else { + log::info!("Client inactive") + } + // if let Err(error) = client.0. { + // log::error!("failed to disconnect: {}", error); + // } else { + // log::info!("Client disconnected"); + // } + } +} + +// conditions + +fn if_just_connected( + network_events: View, +) -> bool { + network_events.iter().any(|event| matches!(&event.0, ClientEvent::Connect)) +} + +fn is_join_state( + join_state: UniqueView +) -> bool { + (*join_state as u8) == STATE +} + +pub fn is_multiplayer( + game_type: UniqueView +) -> bool { + *game_type == GameType::Muliplayer +} + +pub fn is_singleplayer( + game_type: UniqueView +) -> bool { + *game_type == GameType::Singleplayer +} diff --git a/src/player.rs b/kubi/src/player.rs similarity index 60% rename from src/player.rs rename to kubi/src/player.rs index a6afaed..0ae0a9a 100644 --- a/src/player.rs +++ b/kubi/src/player.rs @@ -1,13 +1,12 @@ -use shipyard::{Component, AllStoragesViewMut}; +use shipyard::{Component, AllStoragesViewMut, View, IntoIter}; use crate::{ transform::Transform, camera::Camera, fly_controller::FlyController, - world::raycast::LookingAtBlock, + world::raycast::LookingAtBlock, + block_placement::PlayerHolding, }; - -#[derive(Component)] -pub struct Player; +pub use kubi_shared::player::Player; #[derive(Component)] pub struct MainPlayer; @@ -16,12 +15,13 @@ pub fn spawn_player ( mut storages: AllStoragesViewMut ) { log::info!("spawning player"); - storages.add_entity(( + let entity_id = storages.add_entity(( Player, MainPlayer, Transform::default(), Camera::default(), FlyController, LookingAtBlock::default(), + PlayerHolding::default(), )); } diff --git a/src/prefabs.rs b/kubi/src/prefabs.rs similarity index 78% rename from src/prefabs.rs rename to kubi/src/prefabs.rs index 8b90291..118b93c 100644 --- a/src/prefabs.rs +++ b/kubi/src/prefabs.rs @@ -1,6 +1,6 @@ use shipyard::{NonSendSync, UniqueView, Unique, AllStoragesView}; use glium::{texture::{SrgbTexture2dArray, MipmapsOption}, Program}; -use strum::EnumIter; +use kubi_shared::block::{Block, BlockTexture}; use crate::rendering::Renderer; mod texture; @@ -13,23 +13,6 @@ pub trait AssetPaths { fn file_name(self) -> &'static str; } -#[derive(Clone, Copy, Debug, EnumIter)] -#[repr(u8)] -pub enum BlockTexture { - Stone = 0, - Dirt = 1, - GrassTop = 2, - GrassSide = 3, - Sand = 4, - Bedrock = 5, - Wood = 6, - WoodTop = 7, - Leaf = 8, - Torch = 9, - TallGrass = 10, - Snow = 11, - GrassSideSnow = 12, -} impl AssetPaths for BlockTexture { fn file_name(self) -> &'static str { match self { @@ -46,6 +29,9 @@ impl AssetPaths for BlockTexture { Self::TallGrass => "tall_grass.png", Self::Snow => "snow.png", Self::GrassSideSnow => "grass_side_snow.png", + Self::Cobblestone => "cobblestone.png", + Self::Planks => "planks.png", + Self::WaterSolid => "solid_water.png", } } } @@ -60,7 +46,10 @@ pub struct ChunkShaderPrefab(pub Program); pub struct SelBoxShaderPrefab(pub Program); #[derive(Unique)] -pub struct BasicColoredShaderPrefab(pub Program); +pub struct ColoredShaderPrefab(pub Program); + +#[derive(Unique)] +pub struct ProgressbarShaderPrefab(pub Program); pub fn load_prefabs( storages: AllStoragesView, @@ -92,7 +81,7 @@ pub fn load_prefabs( &renderer.display ) )); - storages.add_unique_non_send_sync(BasicColoredShaderPrefab( + storages.add_unique_non_send_sync(ColoredShaderPrefab( include_shader_prefab!( "colored", "../shaders/colored.vert", @@ -100,4 +89,12 @@ pub fn load_prefabs( &renderer.display ) )); + storages.add_unique_non_send_sync(ProgressbarShaderPrefab( + include_shader_prefab!( + "gui/progressbar", + "../shaders/gui/progressbar.vert", + "../shaders/gui/progressbar.frag", + &renderer.display + ) + )); } diff --git a/src/prefabs/shaders.rs b/kubi/src/prefabs/shaders.rs similarity index 100% rename from src/prefabs/shaders.rs rename to kubi/src/prefabs/shaders.rs diff --git a/src/prefabs/texture.rs b/kubi/src/prefabs/texture.rs similarity index 100% rename from src/prefabs/texture.rs rename to kubi/src/prefabs/texture.rs diff --git a/kubi/src/rendering.rs b/kubi/src/rendering.rs new file mode 100644 index 0000000..f0b621a --- /dev/null +++ b/kubi/src/rendering.rs @@ -0,0 +1,79 @@ +use shipyard::{Unique, NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, AllStoragesView}; +use glium::{ + Display, Surface, + Version, Api, + glutin::{ + event_loop::EventLoop, + window::WindowBuilder, + ContextBuilder, GlProfile + }, +}; +use glam::{Vec3, UVec2}; +use crate::events::WindowResizedEvent; + +pub mod primitives; +pub mod world; +pub mod selection_box; + +#[derive(Unique)] +pub struct RenderTarget(pub glium::Frame); + +#[derive(Unique)] +pub struct BackgroundColor(pub Vec3); + +#[derive(Unique, Clone, Copy)] +pub struct WindowSize(pub UVec2); + +#[derive(Unique)] +pub struct Renderer { + pub display: Display +} +impl Renderer { + pub fn init(event_loop: &EventLoop<()>) -> Self { + log::info!("initializing display"); + let wb = WindowBuilder::new() + .with_title("uwu") + .with_maximized(true); + let cb = ContextBuilder::new() + .with_depth_buffer(24) + .with_gl_profile(GlProfile::Core); + let display = Display::new(wb, cb, event_loop) + .expect("Failed to create a glium Display"); + log::info!("Renderer: {}", display.get_opengl_renderer_string()); + log::info!("OpenGL {}", display.get_opengl_version_string()); + log::info!("Supports GLES {:?}", display.get_supported_glsl_version()); + assert!(display.is_glsl_version_supported(&Version(Api::GlEs, 3, 0)), "GLES 3.0 is not supported"); + Self { display } + } +} + +pub fn clear_background( + mut target: NonSendSync>, + color: UniqueView, +) { + target.0.clear_color_srgb_and_depth((color.0.x, color.0.y, color.0.z, 1.), 1.); +} + +//not sure if this belongs here + +pub fn init_window_size( + storages: AllStoragesView, +) { + let size = storages.borrow::>().unwrap().iter().next().unwrap().0; + storages.add_unique(WindowSize(size)) +} + +pub fn update_window_size( + mut win_size: UniqueViewMut, + resize: View, +) { + if let Some(resize) = resize.iter().next() { + win_size.0 = resize.0; + } +} + +pub fn if_resized ( + resize: View, +) -> bool { + resize.len() > 0 +} diff --git a/kubi/src/rendering/primitives.rs b/kubi/src/rendering/primitives.rs new file mode 100644 index 0000000..6861b3e --- /dev/null +++ b/kubi/src/rendering/primitives.rs @@ -0,0 +1,27 @@ +use shipyard::{Workload, IntoWorkload}; +use glium::implement_vertex; + +pub mod cube; +pub mod rect; + +use cube::init_cube_primitive; +use rect::init_rect_primitive; + +#[derive(Clone, Copy, Default)] +pub struct PositionOnlyVertex { + pub position: [f32; 3], +} +implement_vertex!(PositionOnlyVertex, position); + +#[derive(Clone, Copy, Default)] +pub struct PositionOnlyVertex2d { + pub position: [f32; 2], +} +implement_vertex!(PositionOnlyVertex2d, position); + +pub fn init_primitives() -> Workload { + ( + init_cube_primitive, + init_rect_primitive, + ).into_workload() +} diff --git a/kubi/src/rendering/primitives/cube.rs b/kubi/src/rendering/primitives/cube.rs new file mode 100644 index 0000000..cee8f02 --- /dev/null +++ b/kubi/src/rendering/primitives/cube.rs @@ -0,0 +1,56 @@ +use shipyard::{AllStoragesView, NonSendSync, UniqueView, Unique}; +use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType}; +use crate::rendering::Renderer; +use super::PositionOnlyVertex; + +#[derive(Unique)] +pub struct CubePrimitive(pub VertexBuffer, pub IndexBuffer); + +const CUBE_VERTICES: &[PositionOnlyVertex] = &[ + // front + PositionOnlyVertex { position: [0.0, 0.0, 1.0] }, + PositionOnlyVertex { position: [1.0, 0.0, 1.0] }, + PositionOnlyVertex { position: [1.0, 1.0, 1.0] }, + PositionOnlyVertex { position: [0.0, 1.0, 1.0] }, + // back + PositionOnlyVertex { position: [0.0, 0.0, 0.0] }, + PositionOnlyVertex { position: [1.0, 0.0, 0.0] }, + PositionOnlyVertex { position: [1.0, 1.0, 0.0] }, + PositionOnlyVertex { position: [0.0, 1.0, 0.0] }, +]; +const CUBE_INDICES: &[u16] = &[ + // front + 0, 1, 2, + 2, 3, 0, + // right + 1, 5, 6, + 6, 2, 1, + // back + 7, 6, 5, + 5, 4, 7, + // left + 4, 0, 3, + 3, 7, 4, + // bottom + 4, 5, 1, + 1, 0, 4, + // top + 3, 2, 6, + 6, 7, 3 +]; + +pub(super) fn init_cube_primitive( + storages: AllStoragesView, + display: NonSendSync> +) { + let vert = VertexBuffer::new( + &display.display, + CUBE_VERTICES + ).unwrap(); + let index = IndexBuffer::new( + &display.display, + PrimitiveType::TrianglesList, + CUBE_INDICES + ).unwrap(); + storages.add_unique_non_send_sync(CubePrimitive(vert, index)); +} diff --git a/kubi/src/rendering/primitives/rect.rs b/kubi/src/rendering/primitives/rect.rs new file mode 100644 index 0000000..0215d14 --- /dev/null +++ b/kubi/src/rendering/primitives/rect.rs @@ -0,0 +1,31 @@ +use shipyard::{Unique, AllStoragesView, NonSendSync, UniqueView}; +use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType}; +use crate::rendering::Renderer; +use super::PositionOnlyVertex2d; + +#[derive(Unique)] +pub struct RectPrimitive(pub VertexBuffer, pub IndexBuffer); + +const RECT_VERTEX: &[PositionOnlyVertex2d] = &[ + PositionOnlyVertex2d { position: [0., 0.] }, + PositionOnlyVertex2d { position: [1., 0.] }, + PositionOnlyVertex2d { position: [0., 1.] }, + PositionOnlyVertex2d { position: [1., 1.] }, +]; +const RECT_INDEX: &[u16] = &[0, 1, 2, 1, 3, 2]; + +pub(super) fn init_rect_primitive( + storages: AllStoragesView, + display: NonSendSync> +) { + let vert = VertexBuffer::new( + &display.display, + RECT_VERTEX + ).unwrap(); + let index = IndexBuffer::new( + &display.display, + PrimitiveType::TrianglesList, + RECT_INDEX + ).unwrap(); + storages.add_unique_non_send_sync(RectPrimitive(vert, index)); +} diff --git a/src/rendering/selection_box.rs b/kubi/src/rendering/selection_box.rs similarity index 93% rename from src/rendering/selection_box.rs rename to kubi/src/rendering/selection_box.rs index b04f75c..1c8cc5a 100644 --- a/src/rendering/selection_box.rs +++ b/kubi/src/rendering/selection_box.rs @@ -12,7 +12,7 @@ use crate::{ }; use super::{ RenderTarget, - primitives::SimpleBoxBuffers, + primitives::cube::CubePrimitive, }; pub fn render_selection_box( @@ -20,7 +20,7 @@ pub fn render_selection_box( camera: View, mut target: NonSendSync>, program: NonSendSync>, - buffers: NonSendSync>, + buffers: NonSendSync>, ) { let camera = camera.iter().next().unwrap(); let Some(lookat) = lookat.iter().next() else { return }; diff --git a/src/rendering/world.rs b/kubi/src/rendering/world.rs similarity index 94% rename from src/rendering/world.rs rename to kubi/src/rendering/world.rs index f4770ec..77a3082 100644 --- a/src/rendering/world.rs +++ b/kubi/src/rendering/world.rs @@ -1,5 +1,5 @@ use glam::{Vec3, Mat4, Quat, ivec3}; -use shipyard::{NonSendSync, UniqueView, UniqueViewMut, View, IntoIter}; +use shipyard::{NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, track}; use glium::{ implement_vertex, uniform, Surface, DrawParameters, @@ -24,7 +24,7 @@ use crate::{ prefabs::{ ChunkShaderPrefab, BlockTexturesPrefab, - BasicColoredShaderPrefab, + ColoredShaderPrefab, }, world::{ ChunkStorage, @@ -32,9 +32,10 @@ use crate::{ chunk::CHUNK_SIZE, }, settings::GameSettings, }; -use super::{RenderTarget, primitives::SimpleBoxBuffers}; +use super::{RenderTarget, primitives::cube::CubePrimitive}; #[derive(Clone, Copy)] +#[repr(C)] pub struct ChunkVertex { pub position: [f32; 3], pub normal: [f32; 3], @@ -43,7 +44,6 @@ pub struct ChunkVertex { } implement_vertex!(ChunkVertex, position, normal, uv, tex_index); - pub fn draw_world( mut target: NonSendSync>, chunks: UniqueView, @@ -109,13 +109,12 @@ pub fn draw_world( } } -//this doesn't use culling! pub fn draw_current_chunk_border( mut target: NonSendSync>, player: View, - transforms: View, - buffers: NonSendSync>, - program: NonSendSync>, + transforms: View, + buffers: NonSendSync>, + program: NonSendSync>, camera: View, settings: UniqueView, ) { diff --git a/src/settings.rs b/kubi/src/settings.rs similarity index 95% rename from src/settings.rs rename to kubi/src/settings.rs index 97cdde9..02dff02 100644 --- a/src/settings.rs +++ b/kubi/src/settings.rs @@ -10,7 +10,7 @@ pub struct GameSettings { impl Default for GameSettings { fn default() -> Self { Self { - render_distance: 5, + render_distance: 6, mouse_sensitivity: 1., debug_draw_current_chunk_border: cfg!(debug_assertions), } diff --git a/kubi/src/state.rs b/kubi/src/state.rs new file mode 100644 index 0000000..a01d228 --- /dev/null +++ b/kubi/src/state.rs @@ -0,0 +1,58 @@ +use shipyard::{Unique, UniqueView, UniqueViewMut, AllStoragesView}; +use std::mem::take; + +#[derive(Unique, PartialEq, Eq, Default, Clone, Copy)] +pub enum GameState { + #[default] + Initial, + Connecting, + LoadingWorld, + InGame +} + +#[derive(Unique, PartialEq, Eq, Default, Clone, Copy)] +pub struct NextState(pub Option); + +pub fn init_state( + all_storages: AllStoragesView, +) { + all_storages.add_unique(GameState::default()); + all_storages.add_unique(NextState::default()); +} + +pub fn update_state( + mut state: UniqueViewMut, + mut next: UniqueViewMut, +) { + *state = take(&mut next.0).unwrap_or(*state); +} + +pub fn is_changing_state( + state: UniqueView +) -> bool { + state.0.is_some() +} + +pub fn is_connecting( + state: UniqueView +) -> bool { + *state == GameState::Connecting +} + +pub fn is_ingame( + state: UniqueView +) -> bool { + *state == GameState::InGame +} + +pub fn is_loading( + state: UniqueView +) -> bool { + matches!(*state, GameState::LoadingWorld) +} + +pub fn is_ingame_or_loading( + state: UniqueView +) -> bool { + matches!(*state, GameState::InGame | GameState::LoadingWorld) +} diff --git a/src/world.rs b/kubi/src/world.rs similarity index 92% rename from src/world.rs rename to kubi/src/world.rs index 2e5bdf2..50664d2 100644 --- a/src/world.rs +++ b/kubi/src/world.rs @@ -4,25 +4,21 @@ use glam::IVec3; use hashbrown::HashMap; use anyhow::{Result, Context}; +pub use kubi_shared::{worldgen, block::Block}; + pub mod chunk; -pub mod block; pub mod tasks; pub mod loading; pub mod mesh; pub mod neighbors; -pub mod worldgen; pub mod raycast; +pub mod queue; -use chunk::{Chunk, ChunkMesh}; +use chunk::{Chunk, ChunkMesh, CHUNK_SIZE}; use tasks::ChunkTaskManager; - -use self::{chunk::CHUNK_SIZE, block::Block}; - -//TODO separate world struct for render data -// because this is not send-sync +use queue::BlockUpdateQueue; #[derive(Default, Unique)] -#[track(Modification)] pub struct ChunkStorage { pub chunks: HashMap } @@ -109,4 +105,5 @@ pub fn init_game_world( storages.add_unique_non_send_sync(ChunkMeshStorage::new()); storages.add_unique(ChunkStorage::new()); storages.add_unique(ChunkTaskManager::new()); + storages.add_unique(BlockUpdateQueue::new()); } diff --git a/src/world/chunk.rs b/kubi/src/world/chunk.rs similarity index 69% rename from src/world/chunk.rs rename to kubi/src/world/chunk.rs index 672d767..f22801f 100644 --- a/src/world/chunk.rs +++ b/kubi/src/world/chunk.rs @@ -1,11 +1,8 @@ use glam::IVec3; use glium::{VertexBuffer, IndexBuffer}; -use super::block::Block; use crate::rendering::world::ChunkVertex; -pub const CHUNK_SIZE: usize = 32; - -pub type BlockData = Box<[[[Block; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]>; +pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData}; pub struct ChunkData { pub blocks: BlockData, @@ -42,6 +39,13 @@ pub enum DesiredChunkState { Rendered, ToUnload, } +impl DesiredChunkState { + pub fn matches_current(self, current: CurrentChunkState) -> bool { + (matches!(self, DesiredChunkState::Nothing) && matches!(current, CurrentChunkState::Nothing)) || + (matches!(self, DesiredChunkState::Loaded) && matches!(current, CurrentChunkState::Loaded)) || + (matches!(self, DesiredChunkState::Rendered) && matches!(current, CurrentChunkState::Rendered)) + } +} pub struct Chunk { pub position: IVec3, @@ -49,7 +53,7 @@ pub struct Chunk { pub mesh_index: Option, pub current_state: CurrentChunkState, pub desired_state: DesiredChunkState, - pub dirty: bool, + pub mesh_dirty: bool, } impl Chunk { pub fn new(position: IVec3) -> Self { @@ -59,7 +63,7 @@ impl Chunk { mesh_index: None, current_state: Default::default(), desired_state: Default::default(), - dirty: false, + mesh_dirty: false, } } } diff --git a/src/world/loading.rs b/kubi/src/world/loading.rs similarity index 60% rename from src/world/loading.rs rename to kubi/src/world/loading.rs index 28ec501..154782d 100644 --- a/src/world/loading.rs +++ b/kubi/src/world/loading.rs @@ -1,20 +1,26 @@ use glam::{IVec3, ivec3}; use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType}; -use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync}; +use kubi_shared::networking::messages::ClientToServerMessage; +use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track}; +use kubi_shared::queue::QueuedBlock; +use uflow::SendMode; use crate::{ player::MainPlayer, transform::Transform, settings::GameSettings, - rendering::Renderer + rendering::Renderer, + state::GameState, + networking::UdpClient, }; use super::{ ChunkStorage, ChunkMeshStorage, chunk::{Chunk, DesiredChunkState, CHUNK_SIZE, ChunkMesh, CurrentChunkState, ChunkData}, - tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask}, + tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask}, + queue::BlockUpdateQueue }; -//todo limit task starts insted -const MAX_CHUNK_OPS: usize = 8; +const MAX_CHUNK_OPS_INGAME: usize = 6; +const MAX_CHUNK_OPS: usize = 32; pub fn update_loaded_world_around_player() -> Workload { ( @@ -28,11 +34,11 @@ pub fn update_loaded_world_around_player() -> Workload { pub fn update_chunks_if_player_moved( v_settings: UniqueView, v_local_player: View, - v_transform: View, + v_transform: View, mut vm_world: UniqueViewMut, ) { //Check if the player actually moved - //TODO fix this also triggers on rotation, only activate when the player crosses the chnk border + //TODO fix this also triggers on rotation, only activate when the player crosses the chunk border let Some((_, transform)) = (&v_local_player, v_transform.inserted_or_modified()).iter().next() else { return }; @@ -116,6 +122,7 @@ fn unload_downgrade_chunks( fn start_required_tasks( task_manager: UniqueView, + mut udp_client: Option>, mut world: UniqueViewMut, ) { if !world.is_modified() { @@ -128,17 +135,27 @@ fn start_required_tasks( match chunk.desired_state { DesiredChunkState::Loaded | DesiredChunkState::Rendered if chunk.current_state == CurrentChunkState::Nothing => { //start load task - task_manager.spawn_task(ChunkTask::LoadChunk { - seed: 0xbeef_face_dead_cafe, - position - }); + if let Some(client) = &mut udp_client { + client.0.send( + postcard::to_allocvec(&ClientToServerMessage::ChunkSubRequest { + chunk: position.to_array() + }).unwrap().into_boxed_slice(), + 0, + SendMode::Reliable + ); + } else { + task_manager.spawn_task(ChunkTask::LoadChunk { + seed: 0xbeef_face_dead_cafe, + position + }); + } //Update chunk state let chunk = world.chunks.get_mut(&position).unwrap(); chunk.current_state = CurrentChunkState::Loading; // =========== //log::trace!("Started loading chunk {position}"); }, - DesiredChunkState::Rendered if (chunk.current_state == CurrentChunkState::Loaded || chunk.dirty) => { + DesiredChunkState::Rendered if (chunk.current_state == CurrentChunkState::Loaded || chunk.mesh_dirty) => { //get needed data let Some(neighbors) = world.neighbors_all(position) else { continue @@ -150,12 +167,12 @@ fn start_required_tasks( task_manager.spawn_task(ChunkTask::GenerateMesh { data, position }); //Update chunk state let chunk = world.chunks.get_mut(&position).unwrap(); - if chunk.dirty { + if chunk.mesh_dirty { chunk.current_state = CurrentChunkState::RecalculatingMesh; } else { chunk.current_state = CurrentChunkState::CalculatingMesh; } - chunk.dirty = false; + chunk.mesh_dirty = false; // =========== //log::trace!("Started generating mesh for chunk {position}"); } @@ -168,63 +185,80 @@ fn process_completed_tasks( task_manager: UniqueView, mut world: UniqueViewMut, mut meshes: NonSendSync>, - renderer: NonSendSync> + renderer: NonSendSync>, + state: UniqueView, + mut queue: UniqueViewMut, ) { - for _ in 0..MAX_CHUNK_OPS { - if let Some(res) = task_manager.receive() { - match res { - ChunkTaskResponse::LoadedChunk { position, chunk_data } => { - //check if chunk exists - let Some(chunk) = world.chunks.get_mut(&position) else { - log::warn!("blocks data discarded: chunk doesn't exist"); - return - }; + let mut ops: usize = 0; + while let Some(res) = task_manager.receive() { + match res { + ChunkTaskResponse::LoadedChunk { position, chunk_data, queued } => { + //check if chunk exists + let Some(chunk) = world.chunks.get_mut(&position) else { + log::warn!("blocks data discarded: chunk doesn't exist"); + return + }; - //check if chunk still wants it - if !matches!(chunk.desired_state, DesiredChunkState::Loaded | DesiredChunkState::Rendered) { - log::warn!("block data discarded: state undesirable: {:?}", chunk.desired_state); - return - } - - //set the block data - chunk.block_data = Some(ChunkData { - blocks: chunk_data - }); - - //update chunk state - chunk.current_state = CurrentChunkState::Loaded; - }, - ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } => { - //check if chunk exists - let Some(chunk) = world.chunks.get_mut(&position) else { - log::warn!("mesh discarded: chunk doesn't exist"); - return - }; - - //check if chunk still wants it - if chunk.desired_state != DesiredChunkState::Rendered { - log::warn!("mesh discarded: state undesirable: {:?}", chunk.desired_state); - return - } - - //apply the mesh - let vertex_buffer = VertexBuffer::new(&renderer.display, &vertices).unwrap(); - let index_buffer = IndexBuffer::new(&renderer.display, PrimitiveType::TrianglesList, &indexes).unwrap(); - let mesh = ChunkMesh { - vertex_buffer, - index_buffer, - }; - if let Some(index) = chunk.mesh_index { - meshes.update(index, mesh).expect("Mesh update failed"); - } else { - let mesh_index = meshes.insert(mesh); - chunk.mesh_index = Some(mesh_index); - } - - //update chunk state - chunk.current_state = CurrentChunkState::Rendered; + //check if chunk still wants it + if !matches!(chunk.desired_state, DesiredChunkState::Loaded | DesiredChunkState::Rendered) { + log::warn!("block data discarded: state undesirable: {:?}", chunk.desired_state); + return } + + //set the block data + chunk.block_data = Some(ChunkData { + blocks: chunk_data + }); + + //update chunk state + chunk.current_state = CurrentChunkState::Loaded; + + //push queued blocks + //TODO use extend + for item in queued { + queue.push(item); + } + + //increase ops counter + ops += 1; + }, + ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } => { + //check if chunk exists + let Some(chunk) = world.chunks.get_mut(&position) else { + log::warn!("mesh discarded: chunk doesn't exist"); + return + }; + + //check if chunk still wants it + if chunk.desired_state != DesiredChunkState::Rendered { + log::warn!("mesh discarded: state undesirable: {:?}", chunk.desired_state); + return + } + + //apply the mesh + let vertex_buffer = VertexBuffer::new(&renderer.display, &vertices).unwrap(); + let index_buffer = IndexBuffer::new(&renderer.display, PrimitiveType::TrianglesList, &indexes).unwrap(); + let mesh = ChunkMesh { + vertex_buffer, + index_buffer, + }; + if let Some(index) = chunk.mesh_index { + meshes.update(index, mesh).expect("Mesh update failed"); + } else { + let mesh_index = meshes.insert(mesh); + chunk.mesh_index = Some(mesh_index); + } + + //update chunk state + chunk.current_state = CurrentChunkState::Rendered; + + //increase ops counter + ops += 1; } } + if ops >= match *state { + GameState::InGame => MAX_CHUNK_OPS_INGAME, + _ => MAX_CHUNK_OPS, + } { break } } } diff --git a/kubi/src/world/mesh.rs b/kubi/src/world/mesh.rs new file mode 100644 index 0000000..79c2aa7 --- /dev/null +++ b/kubi/src/world/mesh.rs @@ -0,0 +1,92 @@ +use glam::{IVec3, ivec3}; +use strum::IntoEnumIterator; +use kubi_shared::block::{Block, RenderType}; +use crate::world::chunk::CHUNK_SIZE; +use crate::rendering::world::ChunkVertex; + +pub mod data; +mod builder; + +use data::MeshGenData; +use builder::{MeshBuilder, CubeFace, DiagonalFace}; + +pub fn generate_mesh(data: MeshGenData) -> (Vec, Vec) { + let get_block = |pos: IVec3| -> Block { + if pos.x < 0 { + data.block_data_neg_x[(CHUNK_SIZE as i32 + pos.x) as usize][pos.y as usize][pos.z as usize] + } else if pos.x >= CHUNK_SIZE as i32 { + data.block_data_pos_x[pos.x as usize - CHUNK_SIZE][pos.y as usize][pos.z as usize] + } else if pos.y < 0 { + data.block_data_neg_y[pos.x as usize][(CHUNK_SIZE as i32 + pos.y) as usize][pos.z as usize] + } else if pos.y >= CHUNK_SIZE as i32 { + data.block_data_pos_y[pos.x as usize][pos.y as usize - CHUNK_SIZE][pos.z as usize] + } else if pos.z < 0 { + data.block_data_neg_z[pos.x as usize][pos.y as usize][(CHUNK_SIZE as i32 + pos.z) as usize] + } else if pos.z >= CHUNK_SIZE as i32 { + data.block_data_pos_z[pos.x as usize][pos.y as usize][pos.z as usize - CHUNK_SIZE] + } else { + data.block_data[pos.x as usize][pos.y as usize][pos.z as usize] + } + }; + + let mut builder = MeshBuilder::new(); + + for x in 0..CHUNK_SIZE as i32 { + for y in 0..CHUNK_SIZE as i32 { + for z in 0..CHUNK_SIZE as i32 { + let coord = ivec3(x, y, z); + let block = get_block(coord); + let descriptor = block.descriptor(); + match descriptor.render { + RenderType::None => continue, + RenderType::SolidBlock(textures) | RenderType::BinaryTransparency(textures) => { + for face in CubeFace::iter() { + let facing_direction = face.normal(); + let facing_coord = coord + facing_direction; + let facing_block = get_block(facing_coord); + let facing_descriptor = facing_block.descriptor(); + let face_obstructed = match descriptor.render { + RenderType::SolidBlock(_) => matches!(facing_descriptor.render, RenderType::SolidBlock(_)), + RenderType::BinaryTransparency(_) => { + match facing_descriptor.render { + RenderType::SolidBlock(_) => true, + RenderType::BinaryTransparency(_) => block == facing_block, + _ => false, + } + }, + _ => unreachable!(), + }; + if !face_obstructed { + let face_texture = match face { + CubeFace::Top => textures.top, + CubeFace::Front => textures.front, + CubeFace::Left => textures.left, + CubeFace::Right => textures.right, + CubeFace::Back => textures.back, + CubeFace::Bottom => textures.bottom, + }; + builder.add_face(face, coord, face_texture as u8); + } + } + }, + RenderType::CrossShape(textures) => { + builder.add_diagonal_face( + coord, + DiagonalFace::LeftZ, + textures.0.front as u8, + textures.0.back as u8 + ); + builder.add_diagonal_face( + coord, + DiagonalFace::RigthZ, + textures.1.front as u8, + textures.1.back as u8 + ); + }, + } + } + } + } + + builder.finish() +} diff --git a/kubi/src/world/mesh/builder.rs b/kubi/src/world/mesh/builder.rs new file mode 100644 index 0000000..fb5949e --- /dev/null +++ b/kubi/src/world/mesh/builder.rs @@ -0,0 +1,177 @@ +use strum::EnumIter; +use glam::{Vec3, vec3, IVec3, ivec3}; +use crate::rendering::world::ChunkVertex; + +const INV_SQRT_2: f32 = 0.70710678118655; // 1 / 2.sqrt() + +#[repr(usize)] +#[derive(Clone, Copy, Debug, EnumIter)] +pub enum CubeFace { + Top = 0, + Front = 4, + Left = 2, + Right = 3, + Back = 1, + Bottom = 5, +} +impl CubeFace { + pub const fn normal(self) -> IVec3 { + CUBE_FACE_NORMALS_IVEC3[self as usize] + } +} + +const CUBE_FACE_VERTICES: [[Vec3; 4]; 6] = [ + [vec3(0., 1., 0.), vec3(0., 1., 1.), vec3(1., 1., 0.), vec3(1., 1., 1.)], + [vec3(0., 0., 0.), vec3(0., 1., 0.), vec3(1., 0., 0.), vec3(1., 1., 0.)], + [vec3(0., 0., 1.), vec3(0., 1., 1.), vec3(0., 0., 0.), vec3(0., 1., 0.)], + [vec3(1., 0., 0.), vec3(1., 1., 0.), vec3(1., 0., 1.), vec3(1., 1., 1.)], + [vec3(1., 0., 1.), vec3(1., 1., 1.), vec3(0., 0., 1.), vec3(0., 1., 1.)], + [vec3(0., 0., 1.), vec3(0., 0., 0.), vec3(1., 0., 1.), vec3(1., 0., 0.)], +]; +const CUBE_FACE_NORMALS_IVEC3: [IVec3; 6] = [ + ivec3( 0, 1, 0), + ivec3( 0, 0, -1), + ivec3(-1, 0, 0), + ivec3( 1, 0, 0), + ivec3( 0, 0, 1), + ivec3( 0, -1, 0) +]; +const CUBE_FACE_NORMALS: [Vec3; 6] = [ + vec3(0., 1., 0.), + vec3(0., 0., -1.), + vec3(-1.,0., 0.), + vec3(1., 0., 0.), + vec3(0., 0., 1.), + vec3(0., -1.,0.) +]; +const CUBE_FACE_INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3]; + +#[repr(usize)] +pub enum DiagonalFace { + RigthZ = 0, + LeftZ = 1, +} +const CROSS_FACES: [[Vec3; 4]; 2] = [ + [ + vec3(0., 0., 0.), + vec3(0., 1., 0.), + vec3(1., 0., 1.), + vec3(1., 1., 1.), + ], + [ + vec3(0., 0., 1.), + vec3(0., 1., 1.), + vec3(1., 0., 0.), + vec3(1., 1., 0.), + ] +]; +const CROSS_FACE_NORMALS: [Vec3; 2] = [ + vec3(-INV_SQRT_2, 0., INV_SQRT_2), + vec3(INV_SQRT_2, 0., INV_SQRT_2), +]; +const CROSS_FACE_NORMALS_BACK: [Vec3; 2] = [ + vec3(INV_SQRT_2, 0., -INV_SQRT_2), + vec3(-INV_SQRT_2, 0., -INV_SQRT_2), +]; +const CROSS_FACE_INDICES: [u32; 12] = [ + 0, 1, 2, 2, 1, 3, //Front side + 6, 5, 4, 7, 5, 6, //Back side +]; + + +const UV_COORDS: [[f32; 2]; 4] = [ + [0., 0.], + [0., 1.], + [1., 0.], + [1., 1.], +]; + +#[derive(Default)] +pub struct MeshBuilder { + vertex_buffer: Vec, + index_buffer: Vec, + idx_counter: u32, +} +impl MeshBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) { + let coord = coord.as_vec3(); + let face_index = face as usize; + + //Push vertices + let norm = CUBE_FACE_NORMALS[face_index]; + let vert = CUBE_FACE_VERTICES[face_index]; + self.vertex_buffer.reserve(4); + for i in 0..4 { + self.vertex_buffer.push(ChunkVertex { + position: (coord + vert[i]).to_array(), + normal: norm.to_array(), + uv: UV_COORDS[i], + tex_index: texture + }); + } + + //Push indices + self.index_buffer.extend_from_slice(&CUBE_FACE_INDICES.map(|x| x + self.idx_counter)); + + //Increment idx counter + self.idx_counter += 4; + } + + pub fn add_diagonal_face(&mut self, coord: IVec3, face_type: DiagonalFace, front_texture: u8, back_texture: u8) { + //Push vertices + let face_type = face_type as usize; + let vertices = CROSS_FACES[face_type]; + let normal_front = CROSS_FACE_NORMALS[face_type].to_array(); + let normal_back = CROSS_FACE_NORMALS_BACK[face_type].to_array(); + self.vertex_buffer.reserve(8); + for i in 0..4 { //push front vertices + self.vertex_buffer.push(ChunkVertex { + position: (coord.as_vec3() + vertices[i]).to_array(), + normal: normal_front, + uv: UV_COORDS[i], + tex_index: front_texture + }) + } + for i in 0..4 { //push back vertices + self.vertex_buffer.push(ChunkVertex { + position: (coord.as_vec3() + vertices[i]).to_array(), + normal: normal_back, + uv: UV_COORDS[i], + tex_index: back_texture + }) + } + + //Push indices + self.index_buffer.extend_from_slice(&CROSS_FACE_INDICES.map(|x| x + self.idx_counter)); + + //Increment idx counter + self.idx_counter += 8; + } + + pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) { + //push vertices + self.vertex_buffer.extend(vertices.iter().map(|vertex| { + let mut vertex = *vertex; + vertex.position[0] += position.x; + vertex.position[0] += position.y; + vertex.position[0] += position.z; + vertex + })); + //push indices + if let Some(indices) = indices { + self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter)); + } else { + self.index_buffer.extend(0..(self.vertex_buffer.len() as u32)); + } + //increment idx counter + self.idx_counter += vertices.len() as u32; + } + + pub fn finish(self) -> (Vec, Vec) { + (self.vertex_buffer, self.index_buffer) + } +} diff --git a/src/world/mesh/data.rs b/kubi/src/world/mesh/data.rs similarity index 100% rename from src/world/mesh/data.rs rename to kubi/src/world/mesh/data.rs diff --git a/src/world/neighbors.rs b/kubi/src/world/neighbors.rs similarity index 100% rename from src/world/neighbors.rs rename to kubi/src/world/neighbors.rs diff --git a/kubi/src/world/queue.rs b/kubi/src/world/queue.rs new file mode 100644 index 0000000..eb9f346 --- /dev/null +++ b/kubi/src/world/queue.rs @@ -0,0 +1,57 @@ +use glam::{IVec3, ivec3}; +use kubi_shared::{block::Block, chunk::CHUNK_SIZE, queue::QueuedBlock}; +use shipyard::{UniqueViewMut, Unique}; +use super::ChunkStorage; + +#[derive(Unique, Default, Clone)] +pub struct BlockUpdateQueue { + queue: Vec +} +impl BlockUpdateQueue { + pub fn new() -> Self { + Self::default() + } + pub fn push(&mut self, event: QueuedBlock) { + self.queue.push(event) + } +} + +pub fn apply_queued_blocks( + mut queue: UniqueViewMut, + mut world: UniqueViewMut +) { + //maybe i need to check for desired/current state here before marking as dirty? + queue.queue.retain(|&event| { + if let Some(block) = world.get_block_mut(event.position) { + if event.soft && *block != Block::Air { + return false + } + *block = event.block_type; + //mark chunk as dirty + let (chunk_pos, block_pos) = ChunkStorage::to_chunk_coords(event.position); + let chunk = world.chunks.get_mut(&chunk_pos).expect("This error should never happen, if it does then something is super fucked up and the whole project needs to be burnt down."); + chunk.mesh_dirty = true; + //If block pos is close to the border, some neighbors may be dirty! + const DIRECTIONS: [IVec3; 6] = [ + ivec3(1, 0, 0), + ivec3(-1, 0, 0), + ivec3(0, 1, 0), + ivec3(0, -1, 0), + ivec3(0, 0, 1), + ivec3(0, 0, -1), + ]; + for direction in DIRECTIONS { + let outside_chunk = |x| !(0..CHUNK_SIZE as i32).contains(x); + let chunk_dirty = (block_pos + direction).to_array().iter().any(outside_chunk); + if chunk_dirty { + let dir_chunk_pos = chunk_pos + direction; + if let Some(dir_chunk) = world.chunks.get_mut(&dir_chunk_pos) { + dir_chunk.mesh_dirty = true; + } + } + } + return false + } + true + }); +} diff --git a/src/world/raycast.rs b/kubi/src/world/raycast.rs similarity index 82% rename from src/world/raycast.rs rename to kubi/src/world/raycast.rs index 733a197..c0d1c3c 100644 --- a/src/world/raycast.rs +++ b/kubi/src/world/raycast.rs @@ -1,10 +1,10 @@ use glam::{Vec3, IVec3}; -use shipyard::{View, Component, ViewMut, IntoIter, UniqueView}; +use shipyard::{View, Component, ViewMut, IntoIter, UniqueView, track}; +use kubi_shared::block::Block; use crate::transform::Transform; +use super::ChunkStorage; -use super::{ChunkStorage, block::Block}; - -const RAYCAST_STEP: f32 = 0.25; +pub const RAYCAST_STEP: f32 = 0.25; #[derive(Clone, Copy, Debug)] pub struct RaycastReport { @@ -49,7 +49,7 @@ impl ChunkStorage { pub struct LookingAtBlock(pub Option); pub fn update_raycasts( - transform: View, + transform: View, mut raycast: ViewMut, world: UniqueView, ) { @@ -57,9 +57,9 @@ pub fn update_raycasts( if !(world.is_inserted_or_modified() || (transform.inserted_or_modified(), &raycast).iter().next().is_some()) { return } - for (transform, report) in (&transform, &mut raycast).iter() { + for (transform, mut report) in (&transform, &mut raycast).iter() { let (_, rotation, position) = transform.0.to_scale_rotation_translation(); - let direction = (rotation * Vec3::NEG_Z).normalize(); + let direction = (rotation.normalize() * Vec3::NEG_Z).normalize(); *report = LookingAtBlock(world.raycast(position, direction, Some(30.))); } } diff --git a/kubi/src/world/tasks.rs b/kubi/src/world/tasks.rs new file mode 100644 index 0000000..3a50ea4 --- /dev/null +++ b/kubi/src/world/tasks.rs @@ -0,0 +1,106 @@ +use flume::{Sender, Receiver}; +use glam::IVec3; +use kubi_shared::{ + networking::messages::{S_CHUNK_RESPONSE, ServerToClientMessage}, + queue::QueuedBlock +}; +use shipyard::{Unique, UniqueView, View, IntoIter}; +use rayon::{ThreadPool, ThreadPoolBuilder}; +use uflow::client::Event as ClientEvent; +use super::{ + chunk::BlockData, + mesh::{generate_mesh, data::MeshGenData}, + worldgen::generate_world, +}; +use crate::{ + rendering::world::ChunkVertex, + networking::NetworkEvent, +}; + +pub enum ChunkTask { + LoadChunk { + seed: u64, + position: IVec3 + }, + GenerateMesh { + position: IVec3, + data: MeshGenData + } +} +pub enum ChunkTaskResponse { + LoadedChunk { + position: IVec3, + chunk_data: BlockData, + queued: Vec + }, + GeneratedMesh { + position: IVec3, + vertices: Vec, + indexes: Vec + }, +} + +#[derive(Unique)] +pub struct ChunkTaskManager { + channel: (Sender, Receiver), + pool: ThreadPool, +} +impl ChunkTaskManager { + pub fn new() -> Self { + Self { + channel: flume::unbounded::(), //maybe put a bound or even bound(0)? + pool: ThreadPoolBuilder::new().num_threads(4).build().unwrap() + } + } + pub fn add_sussy_response(&self, response: ChunkTaskResponse) { + // this WILL get stuck if the channel is bounded + // don't make the channel bounded ever + self.channel.0.send(response).unwrap() + } + pub fn spawn_task(&self, task: ChunkTask) { + let sender = self.channel.0.clone(); + self.pool.spawn(move || { + let _ = sender.send(match task { + ChunkTask::GenerateMesh { position, data } => { + let (vertices, indexes) = generate_mesh(data); + ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } + }, + ChunkTask::LoadChunk { position, seed } => { + let (chunk_data, queued) = generate_world(position, seed); + ChunkTaskResponse::LoadedChunk { position, chunk_data, queued } + } + }); + }); + } + pub fn receive(&self) -> Option { + self.channel.1.try_recv().ok() + } +} + +//TODO get rid of this, this is awfulll +pub fn inject_network_responses_into_manager_queue( + manager: UniqueView, + events: View +) { + for event in events.iter() { + if event.is_message_of_type::() { + let NetworkEvent(ClientEvent::Receive(data)) = &event else { unreachable!() }; + let ServerToClientMessage::ChunkResponse { + chunk, data, queued + } = postcard::from_bytes(data).expect("Chunk decode failed") else { unreachable!() }; + manager.add_sussy_response(ChunkTaskResponse::LoadedChunk { + position: IVec3::from_array(chunk), + chunk_data: data, + queued + }); + } + // if let ClientEvent::MessageReceived(ServerToClientMessage::ChunkResponse { &chunk, data, queued }) = &event.0 { + // let position = IVec3::from_array(chunk); + // manager.add_sussy_response(ChunkTaskResponse::LoadedChunk { + // position, + // chunk_data: data.clone(), + // queued + // }); + // } + } +} diff --git a/shaders/selection_box.frag b/shaders/selection_box.frag deleted file mode 100644 index 42b54ed..0000000 --- a/shaders/selection_box.frag +++ /dev/null @@ -1,9 +0,0 @@ -#version 150 core - -out vec4 color; -uniform vec4 u_color; - -void main() { - color = u_color; - color -= vec4(0, 0, 0, 0.1 * sin(gl_FragCoord.x) * cos(gl_FragCoord.y)); -} diff --git a/src/block_placement.rs b/src/block_placement.rs deleted file mode 100644 index aec44a3..0000000 --- a/src/block_placement.rs +++ /dev/null @@ -1,48 +0,0 @@ -use shipyard::{UniqueViewMut, UniqueView, View, IntoIter, ViewMut, EntitiesViewMut}; -use crate::{ - player::MainPlayer, - world::{raycast::LookingAtBlock, ChunkStorage, block::Block}, - input::{Inputs, PrevInputs}, - events::{EventComponent, player_actions::PlayerActionEvent}, -}; - -pub fn block_placement_system( - main_player: View, - raycast: View, - input: UniqueView, - prev_input: UniqueView, - mut world: UniqueViewMut, - mut entities: EntitiesViewMut, - mut events: ViewMut, - mut player_events: ViewMut, -) { - let action_place = input.action_b && !prev_input.0.action_b; - let action_break = input.action_a && !prev_input.0.action_a; - if action_place ^ action_break { - //get raycast info - let Some(ray) = (&main_player, &raycast).iter().next().unwrap().1/**/.0 else { return }; - //update block - let (place_position, place_block) = if action_place { - let position = (ray.position - ray.direction * 0.5).floor().as_ivec3(); - let Some(block) = world.get_block_mut(position) else { return }; - *block = Block::Dirt; - (position, *block) - } else { - let Some(block) = world.get_block_mut(ray.block_position) else { return }; - *block = Block::Air; - (ray.block_position, *block) - }; - //mark chunk as dirty - let (chunk_pos, _) = ChunkStorage::to_chunk_coords(place_position); - let chunk = world.chunks.get_mut(&chunk_pos).unwrap(); - chunk.dirty = true; - //send event - entities.add_entity( - (&mut events, &mut player_events), - (EventComponent, PlayerActionEvent::UpdatedBlock { - position: place_position, - block: place_block, - }) - ); - } -} diff --git a/src/camera/matrices.rs b/src/camera/matrices.rs deleted file mode 100644 index b2086f6..0000000 --- a/src/camera/matrices.rs +++ /dev/null @@ -1,42 +0,0 @@ -use glam::{Vec3, Mat4}; -use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload}; -use crate::{transform::Transform, events::WindowResizedEvent}; -use super::Camera; - -//maybe parallelize these two? - -fn update_view_matrix( - mut vm_camera: ViewMut, - v_transform: View -) { - for (camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() { - let (_, rotation, translation) = transform.0.to_scale_rotation_translation(); - let direction = rotation * Vec3::NEG_Z; - camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up); - } -} - -fn update_perspective_matrix( - mut vm_camera: ViewMut, - resize: View, -) { - //TODO update on launch - let Some(&size) = resize.iter().next() else { - return - }; - for camera in (&mut vm_camera).iter() { - camera.perspective_matrix = Mat4::perspective_rh_gl( - camera.fov, - size.0.x as f32 / size.0.y as f32, - camera.z_near, - camera.z_far, - ) - } -} - -pub fn update_matrices() -> Workload { - ( - update_view_matrix, - update_perspective_matrix, - ).into_workload() -} diff --git a/src/input.rs b/src/input.rs deleted file mode 100644 index 7f408e9..0000000 --- a/src/input.rs +++ /dev/null @@ -1,84 +0,0 @@ -use glam::{Vec2, DVec2}; -use glium::glutin::event::{DeviceEvent, VirtualKeyCode, ElementState}; -use hashbrown::HashSet; -use nohash_hasher::BuildNoHashHasher; -use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView}; -use crate::events::InputDeviceEvent; - -#[derive(Unique, Clone, Copy, Default, Debug)] -pub struct Inputs { - pub movement: Vec2, - pub look: Vec2, - pub action_a: bool, - pub action_b: bool, -} - -#[derive(Unique, Clone, Copy, Default, Debug)] -pub struct PrevInputs(pub Inputs); - -#[derive(Unique, Clone, Default, Debug)] -pub struct RawInputState { - pub keyboard_state: HashSet>, - pub button_state: [bool; 32], - pub mouse_delta: DVec2 -} - -pub fn process_events( - device_events: View, - mut input_state: UniqueViewMut, -) { - input_state.mouse_delta = DVec2::ZERO; - for event in device_events.iter() { - match event.event { - DeviceEvent::MouseMotion { delta } => { - input_state.mouse_delta = DVec2::from(delta); - }, - DeviceEvent::Key(input) => { - if let Some(keycode) = input.virtual_keycode { - match input.state { - ElementState::Pressed => input_state.keyboard_state.insert(keycode), - ElementState::Released => input_state.keyboard_state.remove(&keycode), - }; - } - }, - DeviceEvent::Button { button, state } => { - if button < 32 { - input_state.button_state[button as usize] = matches!(state, ElementState::Pressed); - } - }, - _ => () - } - } -} - -pub fn update_input_states ( - raw_inputs: UniqueView, - mut inputs: UniqueViewMut, - mut prev_inputs: UniqueViewMut, -) { - prev_inputs.0 = *inputs; - inputs.movement = Vec2::new( - raw_inputs.keyboard_state.contains(&VirtualKeyCode::D) as u32 as f32 - - raw_inputs.keyboard_state.contains(&VirtualKeyCode::A) as u32 as f32, - raw_inputs.keyboard_state.contains(&VirtualKeyCode::W) as u32 as f32 - - raw_inputs.keyboard_state.contains(&VirtualKeyCode::S) as u32 as f32 - ).normalize_or_zero(); - inputs.look = raw_inputs.mouse_delta.as_vec2(); - inputs.action_a = raw_inputs.button_state[1]; - inputs.action_b = raw_inputs.button_state[3]; -} - -pub fn init_input ( - storages: AllStoragesView -) { - storages.add_unique(Inputs::default()); - storages.add_unique(PrevInputs::default()); - storages.add_unique(RawInputState::default()); -} - -pub fn process_inputs() -> Workload { - ( - process_events, - update_input_states - ).into_workload() -} diff --git a/src/rendering.rs b/src/rendering.rs deleted file mode 100644 index 9389e76..0000000 --- a/src/rendering.rs +++ /dev/null @@ -1,46 +0,0 @@ -use shipyard::{Unique, NonSendSync, UniqueView, UniqueViewMut}; -use glium::{ - Display, Surface, - glutin::{ - event_loop::EventLoop, - window::WindowBuilder, - ContextBuilder, GlProfile - }, -}; -use glam::Vec3; - -pub mod primitives; -pub mod world; -pub mod selection_box; - -#[derive(Unique)] -pub struct RenderTarget(pub glium::Frame); - -#[derive(Unique)] -pub struct BackgroundColor(pub Vec3); - -#[derive(Unique)] -pub struct Renderer { - pub display: Display -} -impl Renderer { - pub fn init(event_loop: &EventLoop<()>) -> Self { - log::info!("initializing display"); - let wb = WindowBuilder::new() - .with_title("uwu") - .with_maximized(true); - let cb = ContextBuilder::new() - .with_depth_buffer(24) - .with_gl_profile(GlProfile::Core); - let display = Display::new(wb, cb, event_loop) - .expect("Failed to create a glium Display"); - Self { display } - } -} - -pub fn clear_background( - mut target: NonSendSync>, - color: UniqueView, -) { - target.0.clear_color_srgb_and_depth((color.0.x, color.0.y, color.0.z, 1.), 1.); -} diff --git a/src/rendering/primitives.rs b/src/rendering/primitives.rs deleted file mode 100644 index ce05f83..0000000 --- a/src/rendering/primitives.rs +++ /dev/null @@ -1,80 +0,0 @@ -use glium::{implement_vertex, VertexBuffer, IndexBuffer, index::PrimitiveType}; -use shipyard::{NonSendSync, UniqueView, AllStoragesView, Unique}; -use super::Renderer; - -pub const CUBE_VERTICES: &[f32] = &[ - // front - 0.0, 0.0, 1.0, - 1.0, 0.0, 1.0, - 1.0, 1.0, 1.0, - 0.0, 1.0, 1.0, - // back - 0.0, 0.0, 0.0, - 1.0, 0.0, 0.0, - 1.0, 1.0, 0.0, - 0.0, 1.0, 0.0 -]; -pub const CUBE_INDICES: &[u16] = &[ - // front - 0, 1, 2, - 2, 3, 0, - // right - 1, 5, 6, - 6, 2, 1, - // back - 7, 6, 5, - 5, 4, 7, - // left - 4, 0, 3, - 3, 7, 4, - // bottom - 4, 5, 1, - 1, 0, 4, - // top - 3, 2, 6, - 6, 7, 3 -]; - -#[derive(Clone, Copy, Default)] -pub struct PositionOnlyVertex { - pub position: [f32; 3], -} -implement_vertex!(PositionOnlyVertex, position); - -const fn box_vertices() -> [PositionOnlyVertex; CUBE_VERTICES.len() / 3] { - let mut arr = [PositionOnlyVertex { position: [0., 0., 0.] }; CUBE_VERTICES.len() / 3]; - let mut ptr = 0; - loop { - arr[ptr] = PositionOnlyVertex { - position: [ - CUBE_VERTICES[ptr * 3], - CUBE_VERTICES[(ptr * 3) + 1], - CUBE_VERTICES[(ptr * 3) + 2] - ] - }; - ptr += 1; - if ptr >= CUBE_VERTICES.len() / 3 { - return arr - } - } -} -const BOX_VERTICES: &[PositionOnlyVertex] = &box_vertices(); - -#[derive(Unique)] -pub struct SimpleBoxBuffers(pub VertexBuffer, pub IndexBuffer); - -pub fn init_simple_box_buffers( - storages: AllStoragesView, - display: NonSendSync> -) { - let vert = VertexBuffer::new( - &display.display, - BOX_VERTICES - ).unwrap(); - let index = IndexBuffer::new( - &display.display, - PrimitiveType::TrianglesList, - CUBE_INDICES - ).unwrap(); - storages.add_unique_non_send_sync(SimpleBoxBuffers(vert, index)); -} diff --git a/src/transform.rs b/src/transform.rs deleted file mode 100644 index 5087b6c..0000000 --- a/src/transform.rs +++ /dev/null @@ -1,6 +0,0 @@ -use shipyard::Component; -use glam::Mat4; - -#[derive(Component, Clone, Copy, Debug, Default)] -#[track(All)] -pub struct Transform(pub Mat4); diff --git a/src/world/block.rs b/src/world/block.rs deleted file mode 100644 index 92fba77..0000000 --- a/src/world/block.rs +++ /dev/null @@ -1,105 +0,0 @@ -use strum::EnumIter; -use crate::prefabs::BlockTexture; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] -#[repr(u8)] -pub enum Block { - Air, - Stone, - Dirt, - Grass, - Sand, -} -impl Block { - pub const fn descriptor(self) -> BlockDescriptor { - match self { - Self::Air => BlockDescriptor { - name: "air", - render: RenderType::None, - collision: CollisionType::None, - raycast_collision: false, - }, - Self::Stone => BlockDescriptor { - name: "stone", - render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Stone)), - collision: CollisionType::Solid, - raycast_collision: true, - }, - Self::Dirt => BlockDescriptor { - name: "dirt", - render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Dirt)), - collision: CollisionType::Solid, - raycast_collision: true, - }, - Self::Grass => BlockDescriptor { - name: "grass", - render: RenderType::SolidBlock(CubeTexture::top_sides_bottom( - BlockTexture::GrassTop, - BlockTexture::GrassSide, - BlockTexture::Dirt - )), - collision: CollisionType::Solid, - raycast_collision: true, - }, - Self::Sand => BlockDescriptor { - name: "sand", - render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Sand)), - collision: CollisionType::Solid, - raycast_collision: true, - }, - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct BlockDescriptor { - pub name: &'static str, - pub render: RenderType, - pub collision: CollisionType, - pub raycast_collision: bool, -} -// impl BlockDescriptor { -// pub fn of(block: Block) -> Self { -// block.descriptor() -// } -// } - -#[derive(Clone, Copy, Debug)] -pub struct CubeTexture { - pub top: BlockTexture, - pub bottom: BlockTexture, - pub left: BlockTexture, - pub right: BlockTexture, - pub front: BlockTexture, - pub back: BlockTexture, -} -impl CubeTexture { - pub const fn top_sides_bottom(top: BlockTexture, sides: BlockTexture, bottom: BlockTexture) -> Self { - Self { - top, - bottom, - left: sides, - right: sides, - front: sides, - back: sides, - } - } - pub const fn horizontal_vertical(horizontal: BlockTexture, vertical: BlockTexture) -> Self { - Self::top_sides_bottom(vertical, horizontal, vertical) - } - pub const fn all(texture: BlockTexture) -> Self { - Self::horizontal_vertical(texture, texture) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CollisionType { - None, - Solid, -} - -#[derive(Clone, Copy, Debug)] -pub enum RenderType { - None, - SolidBlock(CubeTexture) -} diff --git a/src/world/mesh.rs b/src/world/mesh.rs deleted file mode 100644 index f991594..0000000 --- a/src/world/mesh.rs +++ /dev/null @@ -1,138 +0,0 @@ -use strum::{EnumIter, IntoEnumIterator}; -use glam::{Vec3A, vec3a, IVec3, ivec3}; -use std::mem::discriminant; -use super::{chunk::CHUNK_SIZE, block::{Block, RenderType}}; -use crate::rendering::world::ChunkVertex; - -pub mod data; -use data::MeshGenData; - -#[repr(usize)] -#[derive(Clone, Copy, Debug, EnumIter)] -pub enum CubeFace { - Top = 0, - Front = 1, - Left = 2, - Right = 3, - Back = 4, - Bottom = 5, -} -const CUBE_FACE_VERTICES: [[Vec3A; 4]; 6] = [ - [vec3a(0., 1., 0.), vec3a(0., 1., 1.), vec3a(1., 1., 0.), vec3a(1., 1., 1.)], - [vec3a(0., 0., 0.), vec3a(0., 1., 0.), vec3a(1., 0., 0.), vec3a(1., 1., 0.)], - [vec3a(0., 0., 1.), vec3a(0., 1., 1.), vec3a(0., 0., 0.), vec3a(0., 1., 0.)], - [vec3a(1., 0., 0.), vec3a(1., 1., 0.), vec3a(1., 0., 1.), vec3a(1., 1., 1.)], - [vec3a(1., 0., 1.), vec3a(1., 1., 1.), vec3a(0., 0., 1.), vec3a(0., 1., 1.)], - [vec3a(0., 0., 1.), vec3a(0., 0., 0.), vec3a(1., 0., 1.), vec3a(1., 0., 0.)], -]; -const CUBE_FACE_NORMALS: [Vec3A; 6] = [ - vec3a(0., 1., 0.), - vec3a(0., 0., -1.), - vec3a(-1.,0., 0.), - vec3a(1., 0., 0.), - vec3a(0., 0., 1.), - vec3a(0., -1.,0.) -]; -const CUBE_FACE_INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3]; -const UV_COORDS: [[f32; 2]; 4] = [ - [0., 0.], - [0., 1.], - [1., 0.], - [1., 1.], -]; - -#[derive(Default)] -struct MeshBuilder { - vertex_buffer: Vec, - index_buffer: Vec, - idx_counter: u32, -} -impl MeshBuilder { - pub fn new() -> Self { - Self::default() - } - - pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) { - let coord = coord.as_vec3a(); - let face_index = face as usize; - - //Push vertexes - let norm = CUBE_FACE_NORMALS[face_index]; - let vert = CUBE_FACE_VERTICES[face_index]; - self.vertex_buffer.reserve(4); - for i in 0..4 { - self.vertex_buffer.push(ChunkVertex { - position: (coord + vert[i]).to_array(), - normal: norm.to_array(), - uv: UV_COORDS[i], - tex_index: texture - }); - } - - //Push indices - self.index_buffer.extend_from_slice(&CUBE_FACE_INDICES.map(|x| x + self.idx_counter)); - self.idx_counter += 4; - } - - pub fn finish(self) -> (Vec, Vec) { - (self.vertex_buffer, self.index_buffer) - } -} - -pub fn generate_mesh(data: MeshGenData) -> (Vec, Vec) { - let get_block = |pos: IVec3| -> Block { - if pos.x < 0 { - data.block_data_neg_x[(CHUNK_SIZE as i32 + pos.x) as usize][pos.y as usize][pos.z as usize] - } else if pos.x >= CHUNK_SIZE as i32 { - data.block_data_pos_x[pos.x as usize - CHUNK_SIZE][pos.y as usize][pos.z as usize] - } else if pos.y < 0 { - data.block_data_neg_y[pos.x as usize][(CHUNK_SIZE as i32 + pos.y) as usize][pos.z as usize] - } else if pos.y >= CHUNK_SIZE as i32 { - data.block_data_pos_y[pos.x as usize][pos.y as usize - CHUNK_SIZE][pos.z as usize] - } else if pos.z < 0 { - data.block_data_neg_z[pos.x as usize][pos.y as usize][(CHUNK_SIZE as i32 + pos.z) as usize] - } else if pos.z >= CHUNK_SIZE as i32 { - data.block_data_pos_z[pos.x as usize][pos.y as usize][pos.z as usize - CHUNK_SIZE] - } else { - data.block_data[pos.x as usize][pos.y as usize][pos.z as usize] - } - }; - - let mut builder = MeshBuilder::new(); - - for x in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - let coord = ivec3(x as i32, y as i32, z as i32); - let block = get_block(coord); - let descriptor = block.descriptor(); - if matches!(descriptor.render, RenderType::None) { - continue - } - for face in CubeFace::iter() { - let facing = CUBE_FACE_NORMALS[face as usize].as_ivec3(); - let facing_coord = coord + facing; - let show = discriminant(&get_block(facing_coord).descriptor().render) != discriminant(&descriptor.render); - if show { - match descriptor.render { - RenderType::SolidBlock(textures) => { - let face_texture = match face { - CubeFace::Top => textures.top, - CubeFace::Front => textures.front, - CubeFace::Left => textures.left, - CubeFace::Right => textures.right, - CubeFace::Back => textures.back, - CubeFace::Bottom => textures.bottom, - }; - builder.add_face(face, coord, face_texture as u8); - }, - _ => unimplemented!() - } - } - } - } - } - } - - builder.finish() -} diff --git a/src/world/tasks.rs b/src/world/tasks.rs deleted file mode 100644 index 2c73c16..0000000 --- a/src/world/tasks.rs +++ /dev/null @@ -1,64 +0,0 @@ -use flume::{Sender, Receiver}; -use glam::IVec3; -use shipyard::Unique; -use rayon::{ThreadPool, ThreadPoolBuilder}; -use super::{ - chunk::BlockData, - mesh::{generate_mesh, data::MeshGenData}, - worldgen::generate_world, -}; -use crate::rendering::world::ChunkVertex; - -pub enum ChunkTask { - LoadChunk { - seed: u64, - position: IVec3 - }, - GenerateMesh { - position: IVec3, - data: MeshGenData - } -} -pub enum ChunkTaskResponse { - LoadedChunk { - position: IVec3, - chunk_data: BlockData, - }, - GeneratedMesh { - position: IVec3, - vertices: Vec, - indexes: Vec - }, -} - -#[derive(Unique)] -pub struct ChunkTaskManager { - channel: (Sender, Receiver), - pool: ThreadPool, -} -impl ChunkTaskManager { - pub fn new() -> Self { - Self { - channel: flume::unbounded::(), //maybe put a bound or even bound(0)? - pool: ThreadPoolBuilder::new().num_threads(4).build().unwrap() - } - } - pub fn spawn_task(&self, task: ChunkTask) { - let sender = self.channel.0.clone(); - self.pool.spawn(move || { - let _ = sender.send(match task { - ChunkTask::GenerateMesh { position, data } => { - let (vertices, indexes) = generate_mesh(data); - ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } - }, - ChunkTask::LoadChunk { position, seed } => { - let chunk_data = generate_world(position, seed); - ChunkTaskResponse::LoadedChunk { position, chunk_data } - } - }); - }); - } - pub fn receive(&self) -> Option { - self.channel.1.try_recv().ok() - } -} diff --git a/src/world/worldgen.rs b/src/world/worldgen.rs deleted file mode 100644 index 471055c..0000000 --- a/src/world/worldgen.rs +++ /dev/null @@ -1,49 +0,0 @@ -use glam::{IVec3, ivec3}; -use bracket_noise::prelude::*; -use super::{ - chunk::{BlockData, CHUNK_SIZE}, - block::Block -}; - -pub fn generate_world(chunk_position: IVec3, seed: u64) -> BlockData { - let offset = chunk_position * CHUNK_SIZE as i32; - - let mut cave_noise = FastNoise::seeded(seed); - cave_noise.set_fractal_type(FractalType::FBM); - cave_noise.set_frequency(0.1); - - let mut dirt_noise = FastNoise::seeded(seed.rotate_left(1)); - dirt_noise.set_fractal_type(FractalType::FBM); - dirt_noise.set_frequency(0.1); - - let mut blocks = Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]); - - if chunk_position.y >= 0 { - if chunk_position.y == 0 { - for x in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - blocks[x][0][z] = Block::Dirt; - blocks[x][1][z] = Block::Grass; - } - } - } - } else { - for x in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - let position = ivec3(x as i32, y as i32, z as i32) + offset; - let v_cave_noise = cave_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32 - 10.0).clamp(0., 1.); - let v_dirt_noise = dirt_noise.get_noise3d(position.x as f32, position.y as f32, position.z as f32) * (-position.y as f32).clamp(0., 1.); - if v_cave_noise > 0.5 { - blocks[x][y][z] = Block::Stone; - } else if v_dirt_noise > 0.5 { - blocks[x][y][z] = Block::Dirt; - } - } - } - } - } - - - blocks -}