1
0
mirror of https://github.com/osmarks/ewo3.git synced 2025-01-02 21:40:36 +00:00

initial commit

This commit is contained in:
osmarks 2024-06-09 19:08:37 +01:00
commit db02d05778
13 changed files with 3148 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/node_modules

893
Cargo.lock generated Normal file
View File

@ -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",
]

22
Cargo.toml Normal file
View File

@ -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"

463
package-lock.json generated Normal file
View File

@ -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"
}
}
}
}

8
package.json Normal file
View File

@ -0,0 +1,8 @@
{
"devDependencies": {
"esbuild": "^0.12.15",
"esbuild-svelte": "^0.5.3",
"sass": "^1.68.0",
"svelte-preprocess-sass": "^2.0.1"
}
}

171
src/App.svelte Normal file
View File

@ -0,0 +1,171 @@
<style lang="sass">
\:global(*)
box-sizing: border-box
\:global(html)
scrollbar-color: black lightgray
\:global(body)
font-family: "Fira Sans", "Noto Sans", "Segoe UI", Verdana, sans-serif
font-weight: 300
overflow-anchor: none
//margin: 0
//min-height: 100vh
\:global(strong)
font-weight: bold
@mixin header
border-bottom: 1px solid gray
margin: 0
margin-bottom: 0.5em
font-weight: 500
//a
//color: inherit
\:global(h1)
@include header
\:global(h2)
@include header
\:global(h3)
@include header
\:global(h4)
@include header
\:global(h5)
@include header
\:global(h6)
@include header
\:global(ul)
list-style-type: square
padding: 0
padding-left: 1em
input, button, select
border-radius: 0
border: 1px solid gray
padding: 0.5em
.game-display
.row
white-space: nowrap
.cell
display: inline-block
text-align: center
.wrapper
display: flex
</style>
<h1>EWO3 Memetic Edition</h1>
<div class="wrapper">
<div class="game-display">
{#each grid as row, y}
<div class="row" style={`height: ${VERT}px; ` + (y % 2 === 1 ? `padding-left: ${HORIZ/2}px` : "")}>
{#each row as cell}
<div class="cell" style={`width: ${HORIZ}px; height: ${VERT}px; line-height: ${VERT}px; opacity: ${cell[1] * 100}%`}>{cell[0]}</div>
{/each}
</div>
{/each}
</div>
<div class="controls">
{#if dead}
You have died to death. <a href="#" on:click={restart}>Restart</a>.
{/if}
{#if players}
{players} connected players.
{/if}
{#if health}
Your health is {health}.
{/if}
</div>
</div>
<svelte:window on:keydown={keydown} on:keyup={keyup}></svelte:window>
<script>
import * as util from "./util"
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]
}
grid = newGrid
health = data.Display.health
}
if (data === "Dead") {
dead = true
}
if (data.PlayerCount) {
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()
dead = false
reconnect()
}
const GRIDSIZE = 33
const OFFSET = Math.floor(GRIDSIZE/2)
const SIZE = 16
const HORIZ = Math.sqrt(3) * SIZE
const VERT = 3/2 * SIZE
const blankGrid = () => new Array(GRIDSIZE).fill(null).map(() => new Array(GRIDSIZE).fill(""))
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()
</script>

5
src/app.js Normal file
View File

@ -0,0 +1,5 @@
import App from "./App.svelte"
new App({
target: document.body,
})

25
src/build.js Normal file
View File

@ -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))

671
src/main.rs Normal file
View File

@ -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<i64, AxialWorldSpace>;
type CubicCoord = Point3D<i64, CubicWorldSpace>;
type CoordVec = Vector2D<i64, AxialWorldSpace>;
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<Frame>, inputs_tx: mpsc::Sender<Input>) -> 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<Input>,
frames_tx: mpsc::Sender<Frame>,
entity: Entity
}
struct GameState {
world: World,
clients: Slab<Client>,
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<i64>, 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<T: Clone + std::fmt::Debug + hecs::Bundle>(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<char> {
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<H: 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::<hecs::With<&Position, &Collidable>>() {
positions.insert(pos.0, entity);
}
for (entity, pos) in state.world.query_mut::<hecs::With<&Position, &Terrain>>() {
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::<hecs::With<(&Position, Option<&mut RangedAttack>, 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::<MovingInto>(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<char> = {
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<Entity> {
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(())
}

0
src/util.js Normal file
View File

71
static/app.css Normal file
View File

@ -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;
}

802
static/app.js Normal file
View File

@ -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
});
})();

15
static/index.html Normal file
View File

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en" height="100vh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<meta name="description" content="Emu War Online">
<title>EWO3</title>
</style>
<link rel="stylesheet" href="app.css">
</head>
<body>
<script src="app.js"></script>
</body>
</html>