From db02d0577829841e7f38bd00b3f90dfcbb9a165a Mon Sep 17 00:00:00 2001 From: osmarks Date: Sun, 9 Jun 2024 19:08:37 +0100 Subject: [PATCH] initial commit --- .gitignore | 2 + Cargo.lock | 893 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 22 ++ package-lock.json | 463 ++++++++++++++++++++++++ package.json | 8 + src/App.svelte | 171 +++++++++ src/app.js | 5 + src/build.js | 25 ++ src/main.rs | 671 ++++++++++++++++++++++++++++++++++ src/util.js | 0 static/app.css | 71 ++++ static/app.js | 802 +++++++++++++++++++++++++++++++++++++++++ static/index.html | 15 + 13 files changed, 3148 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/App.svelte create mode 100644 src/app.js create mode 100644 src/build.js create mode 100644 src/main.rs create mode 100644 src/util.js create mode 100644 static/app.css create mode 100644 static/app.js create mode 100644 static/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50b6338 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/node_modules \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..31c506b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,893 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +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.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "euclid" +version = "0.22.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "ewo3" +version = "0.1.0" +dependencies = [ + "anyhow", + "euclid", + "fastrand", + "futures-util", + "hecs", + "lazy_static", + "noise-functions", + "seahash", + "serde", + "serde_json", + "slab", + "tokio", + "tokio-macros 0.2.6", + "tokio-tungstenite", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hecs" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cbc675ee8d97b4d206a985137f8ad59666538f56f906474f554467a63c776d" +dependencies = [ + "hashbrown", + "serde", + "spin", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[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.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "noise-functions" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822a69eedf004ac2f492119af7a8203790b1c9115b9a9ef6bcd0cde5d6783565" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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 = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[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 = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros 2.3.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[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 = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6ab3665 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ewo3" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hecs = { version = "0.10", features = ["column-serialize"] } +euclid = { version = "0.22", features = ["serde"] } +fastrand = "2" +tokio-tungstenite = "0.23" +tokio = { version = "1", features = ["full"] } +futures-util = "0.3" +tokio-macros = { version = "0.2.0-alpha.6" } +anyhow = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +slab = "0.4" +lazy_static = "1" +seahash = "4" +noise-functions = "0.2" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..43877ea --- /dev/null +++ b/package-lock.json @@ -0,0 +1,463 @@ +{ + "name": "ewo3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "esbuild": "^0.12.15", + "esbuild-svelte": "^0.5.3", + "sass": "^1.68.0", + "svelte-preprocess-sass": "^2.0.1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/esbuild": { + "version": "0.12.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz", + "integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + } + }, + "node_modules/esbuild-svelte": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.3.tgz", + "integrity": "sha512-KByKD/yt8QaqKjLu32MG3MXBExJYlDM0QwzW3pzKLJR4eev0923DrUKRHPBBjB+OVirUtZnEJE/qitjdW/WyAw==", + "dev": true, + "dependencies": { + "svelte": "^3.38.3" + }, + "peerDependencies": { + "esbuild": ">=0.9.6" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/sass": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz", + "integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "3.38.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.3.tgz", + "integrity": "sha512-N7bBZJH0iF24wsalFZF+fVYMUOigaAUQMIcEKHO3jstK/iL8VmP9xE+P0/a76+FkNcWt+TDv2Gx1taUoUscrvw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/svelte-preprocess-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svelte-preprocess-filter/-/svelte-preprocess-filter-1.0.0.tgz", + "integrity": "sha512-92innv59nyEx24xbfcSurB5ocwC8qFdDtGli/JVMHzJsxyvV2yjQKIcbUqU9VIV5mKUWO2PoY93nncS2yF4ULQ==", + "dev": true + }, + "node_modules/svelte-preprocess-sass": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/svelte-preprocess-sass/-/svelte-preprocess-sass-2.0.1.tgz", + "integrity": "sha512-0y4FjRsRWcN7rJeNJnSfZ7LVAz6S7/j9Dg24XFRelr/rjMMjXORdEvXy4r38fUYmyk9Y7yjwlHCiqyGxMHhEbg==", + "dev": true, + "dependencies": { + "svelte-preprocess-filter": "^1.0.0" + }, + "peerDependencies": { + "sass": "^1.35.2" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + } + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "esbuild": { + "version": "0.12.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz", + "integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==", + "dev": true + }, + "esbuild-svelte": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.3.tgz", + "integrity": "sha512-KByKD/yt8QaqKjLu32MG3MXBExJYlDM0QwzW3pzKLJR4eev0923DrUKRHPBBjB+OVirUtZnEJE/qitjdW/WyAw==", + "dev": true, + "requires": { + "svelte": "^3.38.3" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "sass": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz", + "integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "svelte": { + "version": "3.38.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.3.tgz", + "integrity": "sha512-N7bBZJH0iF24wsalFZF+fVYMUOigaAUQMIcEKHO3jstK/iL8VmP9xE+P0/a76+FkNcWt+TDv2Gx1taUoUscrvw==", + "dev": true + }, + "svelte-preprocess-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svelte-preprocess-filter/-/svelte-preprocess-filter-1.0.0.tgz", + "integrity": "sha512-92innv59nyEx24xbfcSurB5ocwC8qFdDtGli/JVMHzJsxyvV2yjQKIcbUqU9VIV5mKUWO2PoY93nncS2yF4ULQ==", + "dev": true + }, + "svelte-preprocess-sass": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/svelte-preprocess-sass/-/svelte-preprocess-sass-2.0.1.tgz", + "integrity": "sha512-0y4FjRsRWcN7rJeNJnSfZ7LVAz6S7/j9Dg24XFRelr/rjMMjXORdEvXy4r38fUYmyk9Y7yjwlHCiqyGxMHhEbg==", + "dev": true, + "requires": { + "svelte-preprocess-filter": "^1.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..52255ed --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "esbuild": "^0.12.15", + "esbuild-svelte": "^0.5.3", + "sass": "^1.68.0", + "svelte-preprocess-sass": "^2.0.1" + } +} diff --git a/src/App.svelte b/src/App.svelte new file mode 100644 index 0000000..c4df8b9 --- /dev/null +++ b/src/App.svelte @@ -0,0 +1,171 @@ + + +

EWO3 Memetic Edition

+ +
+
+ {#each grid as row, y} +
+ {#each row as cell} +
{cell[0]}
+ {/each} +
+ {/each} +
+
+ {#if dead} + You have died to death. Restart. + {/if} + {#if players} + {players} connected players. + {/if} + {#if health} + Your health is {health}. + {/if} +
+
+ + + + diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..cab2363 --- /dev/null +++ b/src/app.js @@ -0,0 +1,5 @@ +import App from "./App.svelte" + +new App({ + target: document.body, +}) \ No newline at end of file diff --git a/src/build.js b/src/build.js new file mode 100644 index 0000000..2ade5ba --- /dev/null +++ b/src/build.js @@ -0,0 +1,25 @@ +const esbuild = require("esbuild") +const sveltePlugin = require("esbuild-svelte") +const path = require("path") +const { sass } = require("svelte-preprocess-sass") + +esbuild + .build({ + entryPoints: [path.join(__dirname, "app.js")], + bundle: true, + minify: false, + outfile: path.join(__dirname, "../static/app.js"), + plugins: [sveltePlugin({ + preprocess: { + style: sass() + } + })], + loader: { + ".woff": "file", + ".woff2": "file", + ".ttf": "file" + }, + logLevel: "info", + watch: process.argv.join(" ").includes("watch") + }) + .catch(() => process.exit(1)) diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..aa5286f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,671 @@ +use hecs::{Entity, World}; +use euclid::{Point3D, Point2D, Vector2D}; +use futures_util::{stream::TryStreamExt, SinkExt, StreamExt}; +use noise_functions::Sample3; +use tokio::net::{TcpListener, TcpStream}; +use tokio_tungstenite::tungstenite::protocol::Message; +use tokio::sync::{mpsc, Mutex}; +use anyhow::{Result, Context, anyhow}; +use std::{collections::{hash_map::Entry, HashMap}, hash::{Hash, Hasher}, net::SocketAddr, sync::Arc, thread::current, time::Duration}; +use slab::Slab; +use serde::{Serialize, Deserialize}; + +struct AxialWorldSpace; +struct CubicWorldSpace; +type Coord = Point2D; +type CubicCoord = Point3D; +type CoordVec = Vector2D; + +fn to_cubic(p0: Coord) -> CubicCoord { + CubicCoord::new(p0.x, p0.y, -p0.x - p0.y) +} + +fn hex_distance(p0: Coord, p1: Coord) -> i64 { + let ax_dist = p0 - p1; + (ax_dist.x.abs() + ax_dist.y.abs() + (ax_dist.x + ax_dist.y).abs()) / 2 +} + +fn on_axis(p: CoordVec) -> bool { + let p = to_cubic(Coord::origin() + p); + let mut zero_ax = 0; + if p.x == 0 { zero_ax += 1 } + if p.y == 0 { zero_ax += 1 } + if p.z == 0 { zero_ax += 1 } + zero_ax >= 1 +} + +async fn handle_connection(raw_stream: TcpStream, addr: SocketAddr, mut frames_rx: mpsc::Receiver, inputs_tx: mpsc::Sender) -> Result<()> { + let ws_stream = tokio_tungstenite::accept_async(raw_stream).await.context("websocket handshake failure")?; + let (mut outgoing, incoming) = ws_stream.split(); + + let broadcast_incoming = incoming.map_err(anyhow::Error::from).try_for_each(|msg| { + let inputs_tx = inputs_tx.clone(); + async move { + if msg.is_close() { return Err(anyhow!("connection closed")) } + + let input: Input = serde_json::from_str(msg.to_text()?)?; + + inputs_tx.send(input).await?; + + anyhow::Result::<(), anyhow::Error>::Ok(()) + } + }); + + let send_state = async move { + while let Some(frame) = frames_rx.recv().await { + outgoing.send(Message::Text(serde_json::to_string(&frame)?)).await?; + match frame { + Frame::Dead => return Ok(()), + _ => () + } + } + anyhow::Result::<(), anyhow::Error>::Ok(()) + }; + + tokio::select! { + result = broadcast_incoming => { + println!("{:?}", result) + }, + result = send_state => { + println!("{:?}", result) + } + }; + + println!("{} disconnected", &addr); + + Ok(()) +} + +#[derive(Serialize, Deserialize, Clone)] +enum Input { + UpLeft, + UpRight, + Left, + Right, + DownLeft, + DownRight, + Dig +} + +#[derive(Serialize, Deserialize, Clone)] +enum Frame { + Dead, + Display { nearby: Vec<(i64, i64, char, f32)>, health: f32 }, + PlayerCount(usize) +} + +struct Client { + inputs_rx: mpsc::Receiver, + frames_tx: mpsc::Sender, + entity: Entity +} + +struct GameState { + world: World, + clients: Slab, + ticks: u64 +} + +#[derive(Debug, Clone)] +enum Item { + +} + +#[derive(Debug, Clone)] +struct PlayerCharacter; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct Position(Coord); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct MovingInto(Coord); + +#[derive(Debug, Clone)] +struct Health(f32, f32); + +#[derive(Debug, Clone)] +struct Render(char); + +#[derive(Debug, Clone)] +struct Attack { damage: StochasticNumber, energy: f32 } + +#[derive(Debug, Clone)] +struct RangedAttack { damage: StochasticNumber, energy: f32, range: u64 } + +#[derive(Debug, Clone)] +struct DespawnOnTick(u64); + +#[derive(Debug, Clone)] +struct DespawnRandomly(u64); + +#[derive(Debug, Clone)] +struct EnemyTarget { spawn_range: std::ops::RangeInclusive, spawn_density: f32, spawn_rate_inv: usize, aggression_range: i64 } + +#[derive(Debug, Clone)] +struct Enemy; + +#[derive(Debug, Clone)] +struct MoveCost(StochasticNumber); + +#[derive(Debug, Clone)] +struct Collidable; + +#[derive(Debug, Clone)] +struct Velocity(CoordVec); + +#[derive(Debug, Clone)] +struct DeferredRandomly(u64, T); + +#[derive(Debug, Clone)] +struct Terrain; + +#[derive(Debug, Clone)] +struct Obstruction { entry_cost: StochasticNumber, exit_cost: StochasticNumber } + +#[derive(Debug, Clone)] +struct Energy { current: f32, regeneration_rate: f32, burst: f32 } + +#[derive(Debug, Clone)] +struct DespawnOnImpact; + +impl Energy { + fn try_consume(&mut self, cost: f32) -> bool { + if self.current >= -1e-12 { // numerics + self.current -= cost; + true + } else { + false + } + } +} + +const VIEW: i64 = 15; +const WALL: i64 = 128; +const RANDOM_DESPAWN_INV_RATE: u64 = 4000; +const DIRECTIONS: &[CoordVec] = &[CoordVec::new(0, -1), CoordVec::new(1, -1), CoordVec::new(-1, 0), CoordVec::new(1, 0), CoordVec::new(0, 1), CoordVec::new(-1, 1)]; + +#[derive(Debug, Clone, PartialEq, Eq)] +enum BaseTerrain { + Empty, + Occupied, + VeryOccupied +} + +impl BaseTerrain { + fn can_enter(&self) -> bool { + *self == BaseTerrain::Empty + } + + fn symbol(&self) -> Option { + match *self { + Self::Empty => None, + Self::Occupied => Some('#'), + Self::VeryOccupied => Some('█') + } + } +} + +const NOISE_SCALE: f32 = 0.05; + +fn get_base_terrain(pos: Coord) -> BaseTerrain { + let distance = hex_distance(pos, Coord::origin()); + if distance >= (WALL + 12) { + return BaseTerrain::VeryOccupied + } + if distance >= WALL { + return BaseTerrain::Occupied + } + let pos = to_cubic(pos); + let noise = noise_functions::CellDistance.ridged(2, 1.00, 0.20).seed(406).sample3([pos.x as f32 * NOISE_SCALE, pos.y as f32 * NOISE_SCALE, pos.z as f32 * NOISE_SCALE]); + if noise >= 0.3 { + return BaseTerrain::VeryOccupied + } + if noise >= 0.2 { + return BaseTerrain::Occupied + } + return BaseTerrain::Empty +} + +fn sample_range(range: i64) -> CoordVec { + let q = fastrand::i64(-range..=range); + let r = fastrand::i64((-range).max(-q-range)..=range.min(-q+range)); + CoordVec::new(q, r) +} + +struct EnemySpec { + symbol: char, + min_damage: f32, + damage_range: f32, + initial_health: f32, + move_delay: usize, + attack_cooldown: u64, + ranged: bool +} + +impl EnemySpec { + // Numbers ported from original EWO. Fudge constants added elsewhere. + fn random() -> EnemySpec { + match fastrand::usize(0..650) { + 0..=99 => EnemySpec { symbol: 'I', min_damage: 10.0, damage_range: 5.0, initial_health: 50.0, move_delay: 70, attack_cooldown: 10, ranged: false }, // IBIS + 100..=199 => EnemySpec { symbol: 'K', min_damage: 5.0, damage_range: 15.0, initial_health: 30.0, move_delay: 40, attack_cooldown: 10, ranged: false }, // KESTREL + 200..=299 => EnemySpec { symbol: 'S', min_damage: 5.0, damage_range: 5.0, initial_health: 20.0, move_delay: 50, attack_cooldown: 10, ranged: false }, // SNAKE + 300..=399 => EnemySpec { symbol: 'E', min_damage: 10.0, damage_range: 20.0, initial_health: 80.0, move_delay: 80, attack_cooldown: 10, ranged: false }, // EMU + 400..=499 => EnemySpec { symbol: 'O', min_damage: 8.0, damage_range: 17.0, initial_health: 150.0, move_delay: 100, attack_cooldown: 10, ranged: false }, // OGRE + 500..=599 => EnemySpec { symbol: 'R', min_damage: 5.0, damage_range: 5.0, initial_health: 15.0, move_delay: 40, attack_cooldown: 10, ranged: false }, // RAT + 600..=609 => EnemySpec { symbol: 'M' , min_damage: 20.0, damage_range: 10.0, initial_health: 150.0, move_delay: 70, attack_cooldown: 10, ranged: false }, // MOA + 610..=649 => EnemySpec { symbol: 'P', min_damage: 10.0, damage_range: 5.0, initial_health: 15.0, move_delay: 20, attack_cooldown: 10, ranged: true }, // PLATYPUS + _ => unreachable!() + } + } +} + +fn count_hexes(x: i64) -> i64 { + x*(x+1)*3+1 +} + +fn rng_from_hash(x: H) -> fastrand::Rng { + let mut h = seahash::SeaHasher::new(); + x.hash(&mut h); + fastrand::Rng::with_seed(h.finish()) +} + +fn consume_energy_if_available(e: &mut Option<&mut Energy>, cost: f32) -> bool { + e.is_none() || e.as_mut().unwrap().try_consume(cost) +} + +// Box-Muller transform +fn normal() -> f32 { + let u = fastrand::f32(); + let v = fastrand::f32(); + (v * std::f32::consts::TAU).cos() * (-2.0 * u.ln()).sqrt() +} + +fn normal_scaled(mu: f32, sigma: f32) -> f32 { + normal() * sigma + mu +} + +fn triangle_distribution(min: f32, max: f32, mode: f32) -> f32 { + let sample = fastrand::f32(); + let threshold = (mode - min) / (max - min); + if sample < threshold { + min + (sample * (max - min) * (mode - min)).sqrt() + } else { + max - ((1.0 - sample) * (max - min) * (max - mode)).sqrt() + } +} + +#[derive(Debug, Clone, Copy)] +enum StochasticNumber { + Constant(f32), + Triangle { min: f32, max: f32, mode: f32 } +} + +impl StochasticNumber { + fn sample(&self) -> f32 { + match self { + StochasticNumber::Constant(x) => *x, + StochasticNumber::Triangle { min, max, mode } => triangle_distribution(*min, *max, *mode) + } + } + + fn triangle_from_min_range(min: f32, range: f32) -> Self { + StochasticNumber::Triangle { min: min, max: min + range, mode: (min + range) / 2.0 } + } +} + +async fn game_tick(state: &mut GameState) -> Result<()> { + let mut terrain_positions = HashMap::new(); + let mut positions = HashMap::new(); + + for (entity, pos) in state.world.query_mut::>() { + positions.insert(pos.0, entity); + } + + for (entity, pos) in state.world.query_mut::>() { + terrain_positions.insert(pos.0, entity); + } + + let mut buffer = hecs::CommandBuffer::new(); + + // Spawn enemies + for (_entity, (Position(pos), EnemyTarget { spawn_range, spawn_density, spawn_rate_inv, .. })) in state.world.query::<(&Position, &EnemyTarget)>().iter() { + if fastrand::usize(0..*spawn_rate_inv) == 0 { + let c = count_hexes(*spawn_range.end()); + let mut newpos = *pos + sample_range(*spawn_range.end()); + let mut occupied = false; + for _ in 0..(c as f32 / spawn_density * 0.005).ceil() as usize { + if positions.contains_key(&newpos) { + occupied = true; + break; + } + newpos = *pos + sample_range(*spawn_range.end()); + } + if !occupied && get_base_terrain(newpos).can_enter() && hex_distance(newpos, *pos) >= *spawn_range.start() { + let spec = EnemySpec::random(); + if spec.ranged { + buffer.spawn(( + Render(spec.symbol), + Health(spec.initial_health, spec.initial_health), + Enemy, + RangedAttack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32, range: 4 }, + Position(newpos), + MoveCost(StochasticNumber::Triangle { min: 0.0, max: 2.0 * spec.move_delay as f32 / 3.0, mode: spec.move_delay as f32 / 3.0 }), + Collidable, + DespawnRandomly(RANDOM_DESPAWN_INV_RATE), + Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 } + )); + } else { + buffer.spawn(( + Render(spec.symbol), + Health(spec.initial_health, spec.initial_health), + Enemy, + Attack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32 }, + Position(newpos), + MoveCost(StochasticNumber::Triangle { min: 0.0, max: 2.0 * spec.move_delay as f32 / 3.0, mode: spec.move_delay as f32 / 3.0 }), + Collidable, + DespawnRandomly(RANDOM_DESPAWN_INV_RATE), + Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 } + )); + } + } + } + } + + // Process enemy motion and ranged attacks + for (entity, (Position(pos), ranged, energy)) in state.world.query::, Option<&mut Energy>), &Enemy>>().iter() { + for direction in DIRECTIONS.iter() { + if let Some(target) = positions.get(&(*pos + *direction)) { + if let Ok(_) = state.world.get::<&EnemyTarget>(*target) { + buffer.insert_one(entity, MovingInto(*pos + *direction)); + continue; + } + } + } + + let mut closest = None; + + // TODO we maybe need a spatial index for this + for (_entity, (target_pos, target)) in state.world.query::<(&Position, &EnemyTarget)>().iter() { + let distance = hex_distance(*pos, target_pos.0); + if distance < target.aggression_range { + match closest { + Some((_pos, old_distance)) if old_distance < distance => closest = Some((target_pos.0, distance)), + None => closest = Some((target_pos.0, distance)), + _ => () + } + } + } + + if let Some((target_pos, _)) = closest { + if let Some(ranged_attack) = ranged { + // slightly smart behaviour for ranged attacker: try to stay just within range + let direction = DIRECTIONS.iter().min_by_key(|dir| + (hex_distance(*pos + **dir, target_pos) - (ranged_attack.range as i64 - 1)).abs()).unwrap(); + buffer.insert_one(entity, MovingInto(*pos + *direction)); + // do ranged attack if valid + let atk_dir = target_pos - *pos; + if on_axis(atk_dir) && (energy.is_none() || energy.unwrap().try_consume(ranged_attack.energy)) { + let atk_dir = atk_dir.clamp(-CoordVec::one(), CoordVec::one()); + buffer.spawn(( + Render('*'), + Enemy, + Attack { damage: ranged_attack.damage, energy: 0.0 }, + Velocity(atk_dir), + Position(*pos), + DespawnOnTick(state.ticks.wrapping_add(ranged_attack.range)) + )); + } + } else { + let direction = DIRECTIONS.iter().min_by_key(|dir| hex_distance(*pos + **dir, target_pos)).unwrap(); + buffer.insert_one(entity, MovingInto(*pos + *direction)); + } + } else { + // wander randomly (ethical) + buffer.insert_one(entity, MovingInto(*pos + *fastrand::choice(DIRECTIONS).unwrap())); + } + } + + // Process velocity + for (entity, (Position(pos), Velocity(vel))) in state.world.query_mut::<(&Position, &Velocity)>() { + buffer.insert_one(entity, MovingInto(*pos + *vel)); + } + + buffer.run_on(&mut state.world); + + // Process inputs + for (_id, client) in state.clients.iter_mut() { + let mut next_movement = CoordVec::zero(); + loop { + let recv = client.inputs_rx.try_recv(); + match recv { + Err(e) if e == mpsc::error::TryRecvError::Empty => break, + Ok(input) => match input { + Input::UpLeft => next_movement = CoordVec::new(0, -1), + Input::UpRight => next_movement = CoordVec::new(1, -1), + Input::Left => next_movement = CoordVec::new(-1, 0), + Input::Right => next_movement = CoordVec::new(1, 0), + Input::DownRight => next_movement = CoordVec::new(0, 1), + Input::DownLeft => next_movement = CoordVec::new(-1, 1), + Input::Dig => { + + } + }, + Err(e) => return Err(e.into()) + } + } + let position = state.world.get::<&mut Position>(client.entity)?.0; + let target = position + next_movement; + if get_base_terrain(target).can_enter() && target != position { + state.world.insert_one(client.entity, MovingInto(target)).unwrap(); + } + } + + // Process motion and attacks + for (entity, (Position(current_pos), MovingInto(target_pos), damage, mut energy, move_cost, despawn_on_impact)) in state.world.query::<(&mut Position, &MovingInto, Option<&mut Attack>, Option<&mut Energy>, Option<&MoveCost>, Option<&DespawnOnImpact>)>().iter() { + let mut move_cost = move_cost.map(|x| x.0.sample()).unwrap_or(0.0); + if let Some(current_terrain) = terrain_positions.get(current_pos) { + move_cost += 1.0; + } + // TODO will break attacks kind of, desirable? Doubtful. + if let Some(target_terrain) = terrain_positions.get(target_pos) { + move_cost += 1.0; + } + + if get_base_terrain(*target_pos).can_enter() { + let entry = match positions.entry(*target_pos) { + Entry::Occupied(o) => { + let target_entity = *o.get(); + if let Ok(mut x) = state.world.get::<&mut Health>(target_entity) { + match damage { + Some(Attack { damage, energy: energy_cost }) => { + if consume_energy_if_available(&mut energy, *energy_cost) { + x.0 -= damage.sample(); + } + }, + _ => () + } + if despawn_on_impact.is_some() { + buffer.despawn(entity); + } + if x.0 <= 0.0 { + buffer.despawn(target_entity); + Some(Entry::Occupied(o)) + } else { + None + } + } else { + None // TODO: on pickup or something + } + }, + Entry::Vacant(v) => Some(Entry::Vacant(v)) + }; + if let Some(entry) = entry { + // TODO: perhaps this should be applied to attacks too? + if consume_energy_if_available(&mut energy, move_cost) { + *entry.or_insert(entity) = entity; + positions.remove(current_pos); + *current_pos = *target_pos; + } + } + } + buffer.remove_one::(entity); + } + + buffer.run_on(&mut state.world); + + for (_entity, energy) in state.world.query_mut::<&mut Energy>() { + energy.current = (energy.current + energy.regeneration_rate).min(energy.burst); + } + + // Process transient entities + for (entity, tick) in state.world.query_mut::<&DespawnOnTick>() { + if state.ticks == tick.0 { + buffer.despawn(entity); + } + } + + for (entity, DespawnRandomly(inv_rate)) in state.world.query_mut::<&DespawnRandomly>() { + if fastrand::u64(0..*inv_rate) == 0 { + buffer.despawn(entity); + } + } + + buffer.run_on(&mut state.world); + + // Send views to clients + for (_id, client) in state.clients.iter() { + client.frames_tx.send(Frame::PlayerCount(state.clients.len())).await?; + let mut nearby = vec![]; + if let Ok(pos) = state.world.get::<&Position>(client.entity) { + let pos = pos.0; + for q in -VIEW..=VIEW { + for r in (-VIEW).max(-q - VIEW)..= VIEW.min(-q+VIEW) { + let offset = CoordVec::new(q, r); + let pos = pos + offset; + if let Some(symbol) = get_base_terrain(pos).symbol() { + nearby.push((q, r, symbol, 1.0)); + } else { + if let Some(entity) = positions.get(&pos) { + let render = state.world.get::<&Render>(*entity)?; + let health = if let Ok(h) = state.world.get::<&Health>(*entity) { + h.0 / h.1 + } else { 1.0 }; + nearby.push((q, r, render.0, health)) + } else { + let mut rng = rng_from_hash(pos); + let bg = if rng.usize(0..10) == 0 { ',' } else { '.' }; + nearby.push((q, r, bg, rng.f32() * 0.1 + 0.9)) + } + } + } + } + let health = state.world.get::<&Health>(client.entity)?.0; + client.frames_tx.send(Frame::Display { nearby, health }).await?; + } else { + client.frames_tx.send(Frame::Dead).await?; + } + } + + state.ticks = state.ticks.wrapping_add(1); + + Ok(()) +} + +lazy_static::lazy_static! { + static ref IDENTS: Vec = { + let mut chars = vec![]; + for range in [ + 'Α'..='ω', + 'א'..='ת', + 'Ⓐ'..='ⓩ', + '🨀'..='🨅' + ] { + chars.extend(range); + } + chars.extend("𝔸𝕒𝔹𝕓ℂ𝕔𝔻𝕕ⅅⅆ𝔼𝕖ⅇ𝔽𝕗𝔾𝕘ℍ𝕙𝕀𝕚ⅈ𝕁𝕛ⅉ𝕂𝕜𝕃𝕝𝕄𝕞ℕ𝕟𝕆𝕠ℙ𝕡ℚ𝕢ℝ𝕣𝕊𝕤𝕋𝕥𝕌𝕦𝕍𝕧𝕎𝕨𝕏𝕩𝕐𝕪ℤ𝕫ℾℽℿℼ⅀𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡🩊".chars()); + chars + }; +} + +fn random_identifier() -> char { + *fastrand::choice(IDENTS.iter()).unwrap() +} + +fn add_new_player(state: &mut GameState) -> Result { + let pos = loop { + let pos = Coord::origin() + sample_range(WALL - 10); + if get_base_terrain(pos).can_enter() { + break pos; + } + }; + Ok(state.world.spawn(( + Position(pos), + PlayerCharacter, + Render(random_identifier()), + Collidable, + Attack { damage: StochasticNumber::Triangle { min: 20.0, max: 60.0, mode: 20.0 }, energy: 5.0 }, + Health(128.0, 128.0), + EnemyTarget { + spawn_density: 0.01, + spawn_range: 3..=10, + spawn_rate_inv: 20, + aggression_range: 5 + } + ))) +} + +#[tokio::main] +async fn main() -> Result<()> { + let addr = std::env::args().nth(1).unwrap_or_else(|| "0.0.0.0:8080".to_string()); + + let state = Arc::new(Mutex::new(GameState { + world: World::new(), + clients: Slab::new(), + ticks: 0 + })); + + let try_socket = TcpListener::bind(&addr).await; + let listener = try_socket.expect("Failed to bind"); + println!("Listening on: {}", addr); + + let state_ = state.clone(); + tokio::spawn(async move { + let state = state_.clone(); + let mut interval = tokio::time::interval(Duration::from_millis(56)); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + loop { + let mut state = state.lock().await; + if let Err(e) = game_tick(&mut state).await { + println!("{:?}", e); + } + interval.tick().await; + } + }); + + let state_ = state.clone(); + while let Ok((stream, addr)) = listener.accept().await { + let state_ = state_.clone(); + let (frames_tx, frames_rx) = mpsc::channel(10); + let (inputs_tx, inputs_rx) = mpsc::channel(10); + let (id, entity) = { + let mut state = state_.lock().await; + let entity = add_new_player(&mut state)?; // TODO + let client = Client { + inputs_rx, + frames_tx, + entity + }; + let id = state.clients.insert(client); + (id, entity) + }; + + tokio::spawn(async move { + println!("conn result {:?}", handle_connection(stream, addr, frames_rx, inputs_tx).await); + let mut state = state_.lock().await; + state.clients.remove(id); + let _ = state.world.despawn(entity); + }); + } + + Ok(()) +} \ No newline at end of file diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..e69de29 diff --git a/static/app.css b/static/app.css new file mode 100644 index 0000000..972297b --- /dev/null +++ b/static/app.css @@ -0,0 +1,71 @@ +/* fakecss:/home/osmarks/Programming/ewo3/src/App.esbuild-svelte-fake-css */ +* { + box-sizing: border-box; +} +html { + scrollbar-color: black lightgray; +} +body { + font-family: + "Fira Sans", + "Noto Sans", + "Segoe UI", + Verdana, + sans-serif; + font-weight: 300; + overflow-anchor: none; +} +strong { + font-weight: bold; +} +h1 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +h2 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +h3 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +h4 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +h5 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +h6 { + border-bottom: 1px solid gray; + margin: 0; + margin-bottom: 0.5em; + font-weight: 500; +} +ul { + list-style-type: square; + padding: 0; + padding-left: 1em; +} +.game-display.svelte-oncm9j .row.svelte-oncm9j { + white-space: nowrap; +} +.game-display.svelte-oncm9j .row .cell.svelte-oncm9j { + display: inline-block; + text-align: center; +} +.wrapper.svelte-oncm9j.svelte-oncm9j { + display: flex; +} diff --git a/static/app.js b/static/app.js new file mode 100644 index 0000000..be7465b --- /dev/null +++ b/static/app.js @@ -0,0 +1,802 @@ +(() => { + // node_modules/svelte/internal/index.mjs + function noop() { + } + function run(fn) { + return fn(); + } + function blank_object() { + return Object.create(null); + } + function run_all(fns) { + fns.forEach(run); + } + function is_function(thing) { + return typeof thing === "function"; + } + function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || (a && typeof a === "object" || typeof a === "function"); + } + function is_empty(obj) { + return Object.keys(obj).length === 0; + } + var tasks = new Set(); + var is_hydrating = false; + function start_hydrating() { + is_hydrating = true; + } + function end_hydrating() { + is_hydrating = false; + } + function upper_bound(low, high, key, value) { + while (low < high) { + const mid = low + (high - low >> 1); + if (key(mid) <= value) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + } + function init_hydrate(target) { + if (target.hydrate_init) + return; + target.hydrate_init = true; + const children2 = target.childNodes; + const m = new Int32Array(children2.length + 1); + const p = new Int32Array(children2.length); + m[0] = -1; + let longest = 0; + for (let i = 0; i < children2.length; i++) { + const current = children2[i].claim_order; + const seqLen = upper_bound(1, longest + 1, (idx) => children2[m[idx]].claim_order, current) - 1; + p[i] = m[seqLen] + 1; + const newLen = seqLen + 1; + m[newLen] = i; + longest = Math.max(newLen, longest); + } + const lis = []; + const toMove = []; + let last = children2.length - 1; + for (let cur = m[longest] + 1; cur != 0; cur = p[cur - 1]) { + lis.push(children2[cur - 1]); + for (; last >= cur; last--) { + toMove.push(children2[last]); + } + last--; + } + for (; last >= 0; last--) { + toMove.push(children2[last]); + } + lis.reverse(); + toMove.sort((a, b) => a.claim_order - b.claim_order); + for (let i = 0, j = 0; i < toMove.length; i++) { + while (j < lis.length && toMove[i].claim_order >= lis[j].claim_order) { + j++; + } + const anchor = j < lis.length ? lis[j] : null; + target.insertBefore(toMove[i], anchor); + } + } + function append(target, node) { + if (is_hydrating) { + init_hydrate(target); + if (target.actual_end_child === void 0 || target.actual_end_child !== null && target.actual_end_child.parentElement !== target) { + target.actual_end_child = target.firstChild; + } + if (node !== target.actual_end_child) { + target.insertBefore(node, target.actual_end_child); + } else { + target.actual_end_child = node.nextSibling; + } + } else if (node.parentNode !== target) { + target.appendChild(node); + } + } + function insert(target, node, anchor) { + if (is_hydrating && !anchor) { + append(target, node); + } else if (node.parentNode !== target || anchor && node.nextSibling !== anchor) { + target.insertBefore(node, anchor || null); + } + } + function detach(node) { + node.parentNode.removeChild(node); + } + function destroy_each(iterations, detaching) { + for (let i = 0; i < iterations.length; i += 1) { + if (iterations[i]) + iterations[i].d(detaching); + } + } + function element(name) { + return document.createElement(name); + } + function text(data) { + return document.createTextNode(data); + } + function space() { + return text(" "); + } + function listen(node, event, handler, options) { + node.addEventListener(event, handler, options); + return () => node.removeEventListener(event, handler, options); + } + function attr(node, attribute, value) { + if (value == null) + node.removeAttribute(attribute); + else if (node.getAttribute(attribute) !== value) + node.setAttribute(attribute, value); + } + function children(element2) { + return Array.from(element2.childNodes); + } + function set_data(text2, data) { + data = "" + data; + if (text2.wholeText !== data) + text2.data = data; + } + var active_docs = new Set(); + var current_component; + function set_current_component(component) { + current_component = component; + } + var dirty_components = []; + var binding_callbacks = []; + var render_callbacks = []; + var flush_callbacks = []; + var resolved_promise = Promise.resolve(); + var update_scheduled = false; + function schedule_update() { + if (!update_scheduled) { + update_scheduled = true; + resolved_promise.then(flush); + } + } + function add_render_callback(fn) { + render_callbacks.push(fn); + } + var flushing = false; + var seen_callbacks = new Set(); + function flush() { + if (flushing) + return; + flushing = true; + do { + for (let i = 0; i < dirty_components.length; i += 1) { + const component = dirty_components[i]; + set_current_component(component); + update(component.$$); + } + set_current_component(null); + dirty_components.length = 0; + while (binding_callbacks.length) + binding_callbacks.pop()(); + for (let i = 0; i < render_callbacks.length; i += 1) { + const callback = render_callbacks[i]; + if (!seen_callbacks.has(callback)) { + seen_callbacks.add(callback); + callback(); + } + } + render_callbacks.length = 0; + } while (dirty_components.length); + while (flush_callbacks.length) { + flush_callbacks.pop()(); + } + update_scheduled = false; + flushing = false; + seen_callbacks.clear(); + } + function update($$) { + if ($$.fragment !== null) { + $$.update(); + run_all($$.before_update); + const dirty = $$.dirty; + $$.dirty = [-1]; + $$.fragment && $$.fragment.p($$.ctx, dirty); + $$.after_update.forEach(add_render_callback); + } + } + var outroing = new Set(); + function transition_in(block, local) { + if (block && block.i) { + outroing.delete(block); + block.i(local); + } + } + var globals = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : global; + var boolean_attributes = new Set([ + "allowfullscreen", + "allowpaymentrequest", + "async", + "autofocus", + "autoplay", + "checked", + "controls", + "default", + "defer", + "disabled", + "formnovalidate", + "hidden", + "ismap", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "selected" + ]); + function mount_component(component, target, anchor, customElement) { + const { fragment, on_mount, on_destroy, after_update } = component.$$; + fragment && fragment.m(target, anchor); + if (!customElement) { + add_render_callback(() => { + const new_on_destroy = on_mount.map(run).filter(is_function); + if (on_destroy) { + on_destroy.push(...new_on_destroy); + } else { + run_all(new_on_destroy); + } + component.$$.on_mount = []; + }); + } + after_update.forEach(add_render_callback); + } + function destroy_component(component, detaching) { + const $$ = component.$$; + if ($$.fragment !== null) { + run_all($$.on_destroy); + $$.fragment && $$.fragment.d(detaching); + $$.on_destroy = $$.fragment = null; + $$.ctx = []; + } + } + function make_dirty(component, i) { + if (component.$$.dirty[0] === -1) { + dirty_components.push(component); + schedule_update(); + component.$$.dirty.fill(0); + } + component.$$.dirty[i / 31 | 0] |= 1 << i % 31; + } + function init(component, options, instance2, create_fragment2, not_equal, props, dirty = [-1]) { + const parent_component = current_component; + set_current_component(component); + const $$ = component.$$ = { + fragment: null, + ctx: null, + props, + update: noop, + not_equal, + bound: blank_object(), + on_mount: [], + on_destroy: [], + on_disconnect: [], + before_update: [], + after_update: [], + context: new Map(parent_component ? parent_component.$$.context : options.context || []), + callbacks: blank_object(), + dirty, + skip_bound: false + }; + let ready = false; + $$.ctx = instance2 ? instance2(component, options.props || {}, (i, ret, ...rest) => { + const value = rest.length ? rest[0] : ret; + if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { + if (!$$.skip_bound && $$.bound[i]) + $$.bound[i](value); + if (ready) + make_dirty(component, i); + } + return ret; + }) : []; + $$.update(); + ready = true; + run_all($$.before_update); + $$.fragment = create_fragment2 ? create_fragment2($$.ctx) : false; + if (options.target) { + if (options.hydrate) { + start_hydrating(); + const nodes = children(options.target); + $$.fragment && $$.fragment.l(nodes); + nodes.forEach(detach); + } else { + $$.fragment && $$.fragment.c(); + } + if (options.intro) + transition_in(component.$$.fragment); + mount_component(component, options.target, options.anchor, options.customElement); + end_hydrating(); + flush(); + } + set_current_component(parent_component); + } + var SvelteElement; + if (typeof HTMLElement === "function") { + SvelteElement = class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + connectedCallback() { + const { on_mount } = this.$$; + this.$$.on_disconnect = on_mount.map(run).filter(is_function); + for (const key in this.$$.slotted) { + this.appendChild(this.$$.slotted[key]); + } + } + attributeChangedCallback(attr2, _oldValue, newValue) { + this[attr2] = newValue; + } + disconnectedCallback() { + run_all(this.$$.on_disconnect); + } + $destroy() { + destroy_component(this, 1); + this.$destroy = noop; + } + $on(type, callback) { + const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); + callbacks.push(callback); + return () => { + const index = callbacks.indexOf(callback); + if (index !== -1) + callbacks.splice(index, 1); + }; + } + $set($$props) { + if (this.$$set && !is_empty($$props)) { + this.$$.skip_bound = true; + this.$$set($$props); + this.$$.skip_bound = false; + } + } + }; + } + var SvelteComponent = class { + $destroy() { + destroy_component(this, 1); + this.$destroy = noop; + } + $on(type, callback) { + const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); + callbacks.push(callback); + return () => { + const index = callbacks.indexOf(callback); + if (index !== -1) + callbacks.splice(index, 1); + }; + } + $set($$props) { + if (this.$$set && !is_empty($$props)) { + this.$$.skip_bound = true; + this.$$set($$props); + this.$$.skip_bound = false; + } + } + }; + + // src/App.svelte + var { window: window_1 } = globals; + function get_each_context(ctx, list, i) { + const child_ctx = ctx.slice(); + child_ctx[17] = list[i]; + child_ctx[19] = i; + return child_ctx; + } + function get_each_context_1(ctx, list, i) { + const child_ctx = ctx.slice(); + child_ctx[20] = list[i]; + return child_ctx; + } + function create_each_block_1(ctx) { + let div; + let t_value = ctx[20][0] + ""; + let t; + let div_style_value; + return { + c() { + div = element("div"); + t = text(t_value); + attr(div, "class", "cell svelte-oncm9j"); + attr(div, "style", div_style_value = `width: ${ctx[5]}px; height: ${ctx[6]}px; line-height: ${ctx[6]}px; opacity: ${ctx[20][1] * 100}%`); + }, + m(target, anchor) { + insert(target, div, anchor); + append(div, t); + }, + p(ctx2, dirty) { + if (dirty & 8 && t_value !== (t_value = ctx2[20][0] + "")) + set_data(t, t_value); + if (dirty & 8 && div_style_value !== (div_style_value = `width: ${ctx2[5]}px; height: ${ctx2[6]}px; line-height: ${ctx2[6]}px; opacity: ${ctx2[20][1] * 100}%`)) { + attr(div, "style", div_style_value); + } + }, + d(detaching) { + if (detaching) + detach(div); + } + }; + } + function create_each_block(ctx) { + let div; + let t; + let div_style_value; + let each_value_1 = ctx[17]; + let each_blocks = []; + for (let i = 0; i < each_value_1.length; i += 1) { + each_blocks[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i)); + } + return { + c() { + div = element("div"); + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].c(); + } + t = space(); + attr(div, "class", "row svelte-oncm9j"); + attr(div, "style", div_style_value = `height: ${ctx[6]}px; ` + (ctx[19] % 2 === 1 ? `padding-left: ${ctx[5] / 2}px` : "")); + }, + m(target, anchor) { + insert(target, div, anchor); + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].m(div, null); + } + append(div, t); + }, + p(ctx2, dirty) { + if (dirty & 104) { + each_value_1 = ctx2[17]; + let i; + for (i = 0; i < each_value_1.length; i += 1) { + const child_ctx = get_each_context_1(ctx2, each_value_1, i); + if (each_blocks[i]) { + each_blocks[i].p(child_ctx, dirty); + } else { + each_blocks[i] = create_each_block_1(child_ctx); + each_blocks[i].c(); + each_blocks[i].m(div, t); + } + } + for (; i < each_blocks.length; i += 1) { + each_blocks[i].d(1); + } + each_blocks.length = each_value_1.length; + } + }, + d(detaching) { + if (detaching) + detach(div); + destroy_each(each_blocks, detaching); + } + }; + } + function create_if_block_2(ctx) { + let t0; + let a; + let t2; + let mounted; + let dispose; + return { + c() { + t0 = text("You have died to death. "); + a = element("a"); + a.textContent = "Restart"; + t2 = text("."); + attr(a, "href", "#"); + }, + m(target, anchor) { + insert(target, t0, anchor); + insert(target, a, anchor); + insert(target, t2, anchor); + if (!mounted) { + dispose = listen(a, "click", ctx[4]); + mounted = true; + } + }, + p: noop, + d(detaching) { + if (detaching) + detach(t0); + if (detaching) + detach(a); + if (detaching) + detach(t2); + mounted = false; + dispose(); + } + }; + } + function create_if_block_1(ctx) { + let t0; + let t1; + return { + c() { + t0 = text(ctx[2]); + t1 = text(" connected players."); + }, + m(target, anchor) { + insert(target, t0, anchor); + insert(target, t1, anchor); + }, + p(ctx2, dirty) { + if (dirty & 4) + set_data(t0, ctx2[2]); + }, + d(detaching) { + if (detaching) + detach(t0); + if (detaching) + detach(t1); + } + }; + } + function create_if_block(ctx) { + let t0; + let t1; + let t2; + return { + c() { + t0 = text("Your health is "); + t1 = text(ctx[1]); + t2 = text("."); + }, + m(target, anchor) { + insert(target, t0, anchor); + insert(target, t1, anchor); + insert(target, t2, anchor); + }, + p(ctx2, dirty) { + if (dirty & 2) + set_data(t1, ctx2[1]); + }, + d(detaching) { + if (detaching) + detach(t0); + if (detaching) + detach(t1); + if (detaching) + detach(t2); + } + }; + } + function create_fragment(ctx) { + let h1; + let t1; + let div2; + let div0; + let t2; + let div1; + let t3; + let t4; + let mounted; + let dispose; + let each_value = ctx[3]; + let each_blocks = []; + for (let i = 0; i < each_value.length; i += 1) { + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); + } + let if_block0 = ctx[0] && create_if_block_2(ctx); + let if_block1 = ctx[2] && create_if_block_1(ctx); + let if_block2 = ctx[1] && create_if_block(ctx); + return { + c() { + h1 = element("h1"); + h1.textContent = "EWO3 Memetic Edition"; + t1 = space(); + div2 = element("div"); + div0 = element("div"); + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].c(); + } + t2 = space(); + div1 = element("div"); + if (if_block0) + if_block0.c(); + t3 = space(); + if (if_block1) + if_block1.c(); + t4 = space(); + if (if_block2) + if_block2.c(); + attr(div0, "class", "game-display svelte-oncm9j"); + attr(div1, "class", "controls"); + attr(div2, "class", "wrapper svelte-oncm9j"); + }, + m(target, anchor) { + insert(target, h1, anchor); + insert(target, t1, anchor); + insert(target, div2, anchor); + append(div2, div0); + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].m(div0, null); + } + append(div2, t2); + append(div2, div1); + if (if_block0) + if_block0.m(div1, null); + append(div1, t3); + if (if_block1) + if_block1.m(div1, null); + append(div1, t4); + if (if_block2) + if_block2.m(div1, null); + if (!mounted) { + dispose = [ + listen(window_1, "keydown", ctx[7]), + listen(window_1, "keyup", ctx[8]) + ]; + mounted = true; + } + }, + p(ctx2, [dirty]) { + if (dirty & 104) { + each_value = ctx2[3]; + let i; + for (i = 0; i < each_value.length; i += 1) { + const child_ctx = get_each_context(ctx2, each_value, i); + if (each_blocks[i]) { + each_blocks[i].p(child_ctx, dirty); + } else { + each_blocks[i] = create_each_block(child_ctx); + each_blocks[i].c(); + each_blocks[i].m(div0, null); + } + } + for (; i < each_blocks.length; i += 1) { + each_blocks[i].d(1); + } + each_blocks.length = each_value.length; + } + if (ctx2[0]) { + if (if_block0) { + if_block0.p(ctx2, dirty); + } else { + if_block0 = create_if_block_2(ctx2); + if_block0.c(); + if_block0.m(div1, t3); + } + } else if (if_block0) { + if_block0.d(1); + if_block0 = null; + } + if (ctx2[2]) { + if (if_block1) { + if_block1.p(ctx2, dirty); + } else { + if_block1 = create_if_block_1(ctx2); + if_block1.c(); + if_block1.m(div1, t4); + } + } else if (if_block1) { + if_block1.d(1); + if_block1 = null; + } + if (ctx2[1]) { + if (if_block2) { + if_block2.p(ctx2, dirty); + } else { + if_block2 = create_if_block(ctx2); + if_block2.c(); + if_block2.m(div1, null); + } + } else if (if_block2) { + if_block2.d(1); + if_block2 = null; + } + }, + i: noop, + o: noop, + d(detaching) { + if (detaching) + detach(h1); + if (detaching) + detach(t1); + if (detaching) + detach(div2); + destroy_each(each_blocks, detaching); + if (if_block0) + if_block0.d(); + if (if_block1) + if_block1.d(); + if (if_block2) + if_block2.d(); + mounted = false; + run_all(dispose); + } + }; + } + var GRIDSIZE = 33; + var SIZE = 16; + function instance($$self, $$props, $$invalidate) { + let dead = false; + let health; + let players; + let ws; + const connect = () => { + ws = new WebSocket(window.location.protocol === "https:" ? "wss://ewo.osmarks.net/" : "ws://localhost:8080/"); + ws.addEventListener("message", (ev) => { + const data = JSON.parse(ev.data); + if (data.Display) { + const newGrid = blankGrid(); + for (const [q, r, c, o] of data.Display.nearby) { + const col = q + (r - (r & 1)) / 2; + const row = r; + newGrid[row + OFFSET][col + OFFSET] = [c, o]; + } + $$invalidate(3, grid = newGrid); + $$invalidate(1, health = data.Display.health); + } + if (data === "Dead") { + $$invalidate(0, dead = true); + } + if (data.PlayerCount) { + $$invalidate(2, players = data.PlayerCount); + } + for (const key of keysDown) { + const input = INPUTS[key]; + if (input) { + ws.send(JSON.stringify(input)); + } + } + keysDown = new Set(Array.from(keysDown).map((k) => !keysCleared.has(k))); + keysCleared = new Set(); + }); + ws.addEventListener("close", (ev) => { + console.warn("oh no"); + }); + }; + const reconnect = () => { + if (ws) + ws.close(); + connect(); + }; + const restart = (ev) => { + ev.preventDefault(); + $$invalidate(0, dead = false); + reconnect(); + }; + const OFFSET = Math.floor(GRIDSIZE / 2); + const HORIZ = Math.sqrt(3) * SIZE; + const VERT = 3 / 2 * SIZE; + const blankGrid = () => new Array(GRIDSIZE).fill(null).map(() => new Array(GRIDSIZE).fill("\u200B")); + let grid = blankGrid(); + let keysDown = new Set(); + let keysCleared = new Set(); + const keydown = (ev) => { + keysDown.add(ev.key); + }; + const keyup = (ev) => { + keysCleared.add(ev.key); + }; + const INPUTS = { + "w": "UpLeft", + "e": "UpRight", + "a": "Left", + "d": "Right", + "z": "DownLeft", + "x": "DownRight" + }; + connect(); + return [dead, health, players, grid, restart, HORIZ, VERT, keydown, keyup]; + } + var App = class extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, {}); + } + }; + var App_default = App; + + // src/app.js + new App_default({ + target: document.body + }); +})(); diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..ca17854 --- /dev/null +++ b/static/index.html @@ -0,0 +1,15 @@ + + + + + + + + EWO3 + + + + + + +