mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Merge branch 'master' into docker
This commit is contained in:
73
.github/workflows/rust.yml
vendored
73
.github/workflows/rust.yml
vendored
@@ -8,19 +8,74 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: Rust project - latest
|
||||
format:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
- run: cargo build --verbose
|
||||
- run: cargo test --verbose
|
||||
- run: cargo clippy --all-targets --all-features --no-default-features -- -D warnings
|
||||
- run: cargo fmt --check
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
|
||||
- run: cargo fmt --check
|
||||
clippy:
|
||||
name: Run lints
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
|
||||
- run: cargo clippy --all-targets --all-features --no-default-features -- -D warnings
|
||||
build_and_test:
|
||||
name: Build project and test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
|
||||
- run: cargo build --verbose
|
||||
- run: cargo test --verbose
|
||||
build_release:
|
||||
name: Build project in release
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
|
||||
- run: cargo build --verbose --release
|
||||
clippy_release:
|
||||
name: Run lints in release mode
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
|
||||
- run: cargo clippy --release --all-targets --all-features --no-default-features -- -D warnings
|
||||
295
Cargo.lock
generated
295
Cargo.lock
generated
@@ -154,6 +154,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
@@ -192,9 +198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
@@ -267,6 +273,16 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.9"
|
||||
@@ -297,7 +313,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"mintex",
|
||||
"parking_lot",
|
||||
"rustc-hash",
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thousands",
|
||||
@@ -331,31 +347,12 @@ version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastnbt"
|
||||
version = "2.5.0"
|
||||
@@ -367,12 +364,6 @@ dependencies = [
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
||||
|
||||
[[package]]
|
||||
name = "fastsnbt"
|
||||
version = "0.2.0"
|
||||
@@ -411,21 +402,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
@@ -675,22 +651,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -805,12 +766,6 @@ version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@@ -885,20 +840,15 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.12"
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1009,50 +959,6 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
@@ -1144,12 +1050,6 @@ dependencies = [
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.13"
|
||||
@@ -1194,6 +1094,7 @@ dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"crossbeam-channel",
|
||||
"ctrlc",
|
||||
"dhat",
|
||||
"digest 0.11.0-pre.9",
|
||||
"hmac",
|
||||
@@ -1229,6 +1130,7 @@ dependencies = [
|
||||
name = "pumpkin-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"fastnbt",
|
||||
"serde",
|
||||
"uuid",
|
||||
@@ -1313,6 +1215,54 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand",
|
||||
"ring",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
@@ -1389,7 +1339,6 @@ checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
@@ -1398,29 +1347,31 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
@@ -1481,17 +1432,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
@@ -1500,6 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
@@ -1539,44 +1484,12 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.205"
|
||||
@@ -1792,19 +1705,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
@@ -1906,16 +1806,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.0"
|
||||
@@ -2080,12 +1970,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -2183,6 +2067,15 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
||||
@@ -7,3 +7,4 @@ edition.workspace = true
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||
uuid.workspace = true
|
||||
colored = "2"
|
||||
@@ -1,3 +1,4 @@
|
||||
use colored::{ColoredString, Colorize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Text color
|
||||
@@ -13,6 +14,32 @@ pub enum Color {
|
||||
Named(NamedColor),
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn console_color(&self, text: &str) -> ColoredString {
|
||||
match self {
|
||||
Color::Reset => text.clear(),
|
||||
Color::Named(color) => match color {
|
||||
NamedColor::Black => text.black(),
|
||||
NamedColor::DarkBlue => text.blue(),
|
||||
NamedColor::DarkGreen => text.green(),
|
||||
NamedColor::DarkAqua => text.cyan(),
|
||||
NamedColor::DarkRed => text.red(),
|
||||
NamedColor::DarkPurple => text.purple(),
|
||||
NamedColor::Gold => text.yellow(),
|
||||
NamedColor::Gray => text.bright_black(),
|
||||
NamedColor::DarkGray => text.bright_black(), // ?
|
||||
NamedColor::Blue => text.bright_blue(),
|
||||
NamedColor::Green => text.bright_green(),
|
||||
NamedColor::Aqua => text.cyan(),
|
||||
NamedColor::Red => text.red(),
|
||||
NamedColor::LightPurple => text.bright_purple(),
|
||||
NamedColor::Yellow => text.bright_yellow(),
|
||||
NamedColor::White => text.white(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Named Minecraft color
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::borrow::Cow;
|
||||
|
||||
use click::ClickEvent;
|
||||
use color::Color;
|
||||
use colored::Colorize;
|
||||
use fastnbt::SerOpts;
|
||||
use hover::HoverEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,6 +39,36 @@ impl<'a> TextComponent<'a> {
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_pretty_console(self) -> String {
|
||||
let style = self.style;
|
||||
let color = style.color;
|
||||
let mut text = match self.content {
|
||||
TextContent::Text { text } => text.into_owned(),
|
||||
TextContent::Translate { translate, with: _ } => translate.into_owned(),
|
||||
TextContent::EntityNames {
|
||||
selector,
|
||||
separator: _,
|
||||
} => selector.into_owned(),
|
||||
TextContent::Keybind { keybind } => keybind.into_owned(),
|
||||
};
|
||||
if let Some(color) = color {
|
||||
text = color.console_color(&text).to_string();
|
||||
}
|
||||
if style.bold.is_some() {
|
||||
text = text.bold().to_string();
|
||||
}
|
||||
if style.italic.is_some() {
|
||||
text = text.italic().to_string();
|
||||
}
|
||||
if style.underlined.is_some() {
|
||||
text = text.underline().to_string();
|
||||
}
|
||||
if style.strikethrough.is_some() {
|
||||
text = text.strikethrough().to_string();
|
||||
}
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> serde::Serialize for TextComponent<'a> {
|
||||
|
||||
@@ -3,7 +3,7 @@ use num_derive::ToPrimitive;
|
||||
pub mod player;
|
||||
|
||||
/// https://wiki.vg/Inventory
|
||||
#[derive(Debug, ToPrimitive)]
|
||||
#[derive(Debug, ToPrimitive, Clone)]
|
||||
pub enum WindowType {
|
||||
// not used
|
||||
Generic9x1,
|
||||
@@ -41,3 +41,13 @@ pub enum WindowType {
|
||||
CartographyTable,
|
||||
Stonecutter,
|
||||
}
|
||||
|
||||
impl WindowType {
|
||||
pub const fn default_title(&self) -> &'static str {
|
||||
// TODO: Add titles here:
|
||||
/*match self {
|
||||
_ => "WINDOW TITLE",
|
||||
}*/
|
||||
"WINDOW TITLE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ impl PlayerInventory {
|
||||
1..=4 => self.crafting[slot - 1] = item,
|
||||
5..=8 => {
|
||||
match item {
|
||||
None => self.armor[slot - 4] = None,
|
||||
None => self.armor[slot - 5] = None,
|
||||
Some(item) => {
|
||||
// TODO: Replace asserts with error handling
|
||||
match slot - 5 {
|
||||
@@ -95,4 +95,13 @@ impl PlayerInventory {
|
||||
debug_assert!((0..9).contains(&self.selected));
|
||||
self.items[self.selected + 36 - 9].as_ref()
|
||||
}
|
||||
|
||||
pub fn slots(&self) -> Vec<Option<&Item>> {
|
||||
let mut slots = vec![self.crafting_output.as_ref()];
|
||||
slots.extend(self.crafting.iter().map(|c| c.as_ref()));
|
||||
slots.extend(self.armor.iter().map(|c| c.as_ref()));
|
||||
slots.extend(self.items.iter().map(|c| c.as_ref()));
|
||||
slots.push(self.offhand.as_ref());
|
||||
slots
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{BitSet, VarInt, VarLongType};
|
||||
use crate::{BitSet, FixedBitSet, VarInt, VarLongType};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use core::str;
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
@@ -107,6 +107,10 @@ impl ByteBuffer {
|
||||
uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID")
|
||||
}
|
||||
|
||||
pub fn get_fixed_bitset(&mut self, bits: usize) -> FixedBitSet {
|
||||
self.copy_to_bytes(bits.div_ceil(8))
|
||||
}
|
||||
|
||||
pub fn put_bool(&mut self, v: bool) {
|
||||
if v {
|
||||
self.buffer.put_u8(1);
|
||||
|
||||
@@ -48,6 +48,14 @@ impl ser::Error for SerializerError {
|
||||
}
|
||||
}
|
||||
|
||||
// General notes on the serializer:
|
||||
//
|
||||
// Primitives are written as-is
|
||||
// Strings automatically pre-pend a varint
|
||||
// Enums are written as a varint of the index
|
||||
// Structs are ignored
|
||||
// Iterables' values are written in order, but NO information (e.g. size) about the
|
||||
// iterable itself is written (list sizes should be a seperate field)
|
||||
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializerError;
|
||||
@@ -114,14 +122,15 @@ impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
self.output.put_var_int(&variant_index.into());
|
||||
value.serialize(self)
|
||||
}
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
self.output.put_bool(false);
|
||||
@@ -160,7 +169,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
unimplemented!()
|
||||
}
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
unimplemented!()
|
||||
Ok(self)
|
||||
}
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
@@ -172,11 +181,13 @@ impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
unimplemented!()
|
||||
// Serialize ENUM index as varint
|
||||
self.output.put_var_int(&variant_index.into());
|
||||
Ok(self)
|
||||
}
|
||||
fn serialize_u128(self, _v: u128) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
@@ -206,10 +217,12 @@ impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
todo!()
|
||||
// For ENUMs, only write enum index as varint
|
||||
self.output.put_var_int(&variant_index.into());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,15 +250,15 @@ impl<'a> ser::SerializeTuple for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializerError;
|
||||
|
||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<(), Self::Error>
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
todo!()
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,16 @@ use crate::VarInt;
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x5A)]
|
||||
pub struct CEntityVelocity<'a> {
|
||||
entitiy_id: &'a VarInt,
|
||||
entity_id: &'a VarInt,
|
||||
velocity_x: i16,
|
||||
velocity_y: i16,
|
||||
velocity_z: i16,
|
||||
}
|
||||
|
||||
impl<'a> CEntityVelocity<'a> {
|
||||
pub fn new(entitiy_id: &'a VarInt, velocity_x: f32, velocity_y: f32, velocity_z: f32) -> Self {
|
||||
pub fn new(entity_id: &'a VarInt, velocity_x: f32, velocity_y: f32, velocity_z: f32) -> Self {
|
||||
Self {
|
||||
entitiy_id,
|
||||
entity_id,
|
||||
velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
|
||||
@@ -6,12 +6,12 @@ use crate::VarInt;
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x24)]
|
||||
pub struct CHurtAnimation<'a> {
|
||||
entitiy_id: &'a VarInt,
|
||||
entity_id: &'a VarInt,
|
||||
yaw: f32,
|
||||
}
|
||||
|
||||
impl<'a> CHurtAnimation<'a> {
|
||||
pub fn new(entitiy_id: &'a VarInt, yaw: f32) -> Self {
|
||||
Self { entitiy_id, yaw }
|
||||
pub fn new(entity_id: &'a VarInt, yaw: f32) -> Self {
|
||||
Self { entity_id, yaw }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{bytebuf::ByteBuffer, uuid::UUID, BitSet, ClientPacket, VarInt};
|
||||
|
||||
use crate::{uuid::UUID, BitSet, VarInt};
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x39)]
|
||||
pub struct CPlayerChatMessage<'a> {
|
||||
sender: UUID,
|
||||
@@ -17,12 +15,7 @@ pub struct CPlayerChatMessage<'a> {
|
||||
previous_messages_count: VarInt,
|
||||
previous_messages: &'a [PreviousMessage<'a>], // max 20
|
||||
unsigned_content: Option<TextComponent<'a>>,
|
||||
/// See `FilterType`
|
||||
filter_type: VarInt,
|
||||
|
||||
// TODO: Implement
|
||||
#[allow(dead_code)]
|
||||
filter_type_bits: Option<BitSet<'a>>,
|
||||
filter_type: FilterType<'a>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
@@ -39,8 +32,7 @@ impl<'a> CPlayerChatMessage<'a> {
|
||||
salt: i64,
|
||||
previous_messages: &'a [PreviousMessage<'a>],
|
||||
unsigned_content: Option<TextComponent<'a>>,
|
||||
filter_type: VarInt,
|
||||
filter_type_bits: Option<BitSet<'a>>,
|
||||
filter_type: FilterType<'a>,
|
||||
chat_type: VarInt,
|
||||
sender_name: TextComponent<'a>,
|
||||
target_name: Option<TextComponent<'a>>,
|
||||
@@ -56,7 +48,6 @@ impl<'a> CPlayerChatMessage<'a> {
|
||||
previous_messages,
|
||||
unsigned_content,
|
||||
filter_type,
|
||||
filter_type_bits,
|
||||
chat_type,
|
||||
sender_name,
|
||||
target_name,
|
||||
@@ -64,64 +55,19 @@ impl<'a> CPlayerChatMessage<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ClientPacket for CPlayerChatMessage<'a> {
|
||||
fn write(&self, bytebuf: &mut ByteBuffer) {
|
||||
bytebuf.put_uuid(self.sender.0);
|
||||
bytebuf.put_var_int(&self.index);
|
||||
bytebuf.put_option(&self.message_signature, |p, v| p.put_slice(v));
|
||||
bytebuf.put_string(self.message);
|
||||
bytebuf.put_i64(self.timestamp);
|
||||
bytebuf.put_i64(self.salt);
|
||||
|
||||
if self.previous_messages_count.0 > 20 {
|
||||
// TODO: Assert this is <=20
|
||||
}
|
||||
|
||||
bytebuf.put_var_int(&self.previous_messages_count);
|
||||
for previous_message in self.previous_messages {
|
||||
bytebuf.put_var_int(&previous_message.message_id);
|
||||
if let Some(prev_sig) = previous_message.signature {
|
||||
// TODO: validate whether this should be None or not
|
||||
bytebuf.put_slice(prev_sig);
|
||||
}
|
||||
}
|
||||
|
||||
bytebuf.put_option(&self.unsigned_content, |p, v| {
|
||||
p.put_slice(v.encode().as_slice())
|
||||
});
|
||||
|
||||
bytebuf.put_var_int(&self.filter_type);
|
||||
match FilterType::from_i32(self.filter_type.0) {
|
||||
Some(FilterType::PassThrough) => (),
|
||||
Some(FilterType::FullyFiltered) => {
|
||||
// TODO: Implement
|
||||
}
|
||||
Some(FilterType::PartiallyFiltered) => {
|
||||
// TODO: Implement
|
||||
}
|
||||
None => {
|
||||
// TODO: Implement
|
||||
}
|
||||
}
|
||||
|
||||
bytebuf.put_var_int(&self.chat_type);
|
||||
bytebuf.put_slice(self.sender_name.encode().as_slice());
|
||||
bytebuf.put_option(&self.target_name, |p, v| p.put_slice(v.encode().as_slice()));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PreviousMessage<'a> {
|
||||
message_id: VarInt,
|
||||
signature: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive)]
|
||||
pub enum FilterType {
|
||||
#[derive(Serialize)]
|
||||
#[repr(i32)]
|
||||
pub enum FilterType<'a> {
|
||||
/// Message is not filtered at all
|
||||
PassThrough,
|
||||
PassThrough = 0,
|
||||
/// Message is fully filtered
|
||||
FullyFiltered,
|
||||
FullyFiltered = 1,
|
||||
/// Only some characters in the message are filtered
|
||||
PartiallyFiltered,
|
||||
PartiallyFiltered(BitSet<'a>) = 2,
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use crate::VarInt;
|
||||
#[packet(0x42)]
|
||||
pub struct CRemoveEntities<'a> {
|
||||
count: VarInt,
|
||||
entitiy_ids: &'a [VarInt],
|
||||
entity_ids: &'a [VarInt],
|
||||
}
|
||||
|
||||
impl<'a> CRemoveEntities<'a> {
|
||||
pub fn new(entitiy_ids: &'a [VarInt]) -> Self {
|
||||
pub fn new(entity_ids: &'a [VarInt]) -> Self {
|
||||
Self {
|
||||
count: VarInt(entitiy_ids.len() as i32),
|
||||
entitiy_ids,
|
||||
count: VarInt(entity_ids.len() as i32),
|
||||
entity_ids,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
pumpkin-protocol/src/client/play/c_set_container_content.rs
Normal file
26
pumpkin-protocol/src/client/play/c_set_container_content.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use crate::slot::Slot;
|
||||
use crate::VarInt;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x13)]
|
||||
pub struct CSetContainerContent<'a> {
|
||||
window_id: u8,
|
||||
state_id: VarInt,
|
||||
count: VarInt,
|
||||
slot_data: &'a [Slot],
|
||||
carried_item: &'a Slot,
|
||||
}
|
||||
|
||||
impl<'a> CSetContainerContent<'a> {
|
||||
pub fn new(window_id: u8, state_id: VarInt, slots: &'a [Slot], carried_item: &'a Slot) -> Self {
|
||||
Self {
|
||||
window_id,
|
||||
state_id,
|
||||
count: slots.len().into(),
|
||||
slot_data: slots,
|
||||
carried_item,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
pumpkin-protocol/src/client/play/c_set_container_slot.rs
Normal file
23
pumpkin-protocol/src/client/play/c_set_container_slot.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use crate::slot::Slot;
|
||||
use crate::VarInt;
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x15)]
|
||||
pub struct CSetContainerSlot<'a> {
|
||||
window_id: i8,
|
||||
state_id: VarInt,
|
||||
slot: i16,
|
||||
slot_data: &'a Slot,
|
||||
}
|
||||
|
||||
impl<'a> CSetContainerSlot<'a> {
|
||||
pub fn new(window_id: i8, state_id: i32, slot: usize, slot_data: &'a Slot) -> Self {
|
||||
Self {
|
||||
window_id,
|
||||
state_id: state_id.into(),
|
||||
slot: slot.try_into().unwrap(),
|
||||
slot_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use crate::VarInt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x40)]
|
||||
pub struct CSyncPlayerPostion {
|
||||
pub struct CSyncPlayerPosition {
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
@@ -15,7 +15,7 @@ pub struct CSyncPlayerPostion {
|
||||
teleport_id: VarInt,
|
||||
}
|
||||
|
||||
impl CSyncPlayerPostion {
|
||||
impl CSyncPlayerPosition {
|
||||
pub fn new(
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x6C)]
|
||||
pub struct CSystemChatMessge<'a> {
|
||||
pub struct CSystemChatMessage<'a> {
|
||||
content: TextComponent<'a>,
|
||||
overlay: bool,
|
||||
}
|
||||
|
||||
impl<'a> CSystemChatMessge<'a> {
|
||||
impl<'a> CSystemChatMessage<'a> {
|
||||
pub fn new(content: TextComponent<'a>, overlay: bool) -> Self {
|
||||
Self { content, overlay }
|
||||
}
|
||||
|
||||
15
pumpkin-protocol/src/client/play/c_unload_chunk.rs
Normal file
15
pumpkin-protocol/src/client/play/c_unload_chunk.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use pumpkin_macros::packet;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[packet(0x21)]
|
||||
pub struct CUnloadChunk {
|
||||
z: i32,
|
||||
x: i32,
|
||||
}
|
||||
|
||||
impl CUnloadChunk {
|
||||
pub fn new(x: i32, z: i32) -> Self {
|
||||
Self { z, x }
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,17 @@ mod c_player_chat_message;
|
||||
mod c_player_info_update;
|
||||
mod c_player_remove;
|
||||
mod c_remove_entities;
|
||||
mod c_set_container_content;
|
||||
mod c_set_container_slot;
|
||||
mod c_set_held_item;
|
||||
mod c_set_title;
|
||||
mod c_spawn_player;
|
||||
mod c_subtitle;
|
||||
mod c_sync_player_position;
|
||||
mod c_system_chat_message;
|
||||
mod c_update_entitiy_pos_rot;
|
||||
mod c_unload_chunk;
|
||||
mod c_update_entity_pos;
|
||||
mod c_update_entity_pos_rot;
|
||||
mod c_update_entity_rot;
|
||||
mod c_worldevent;
|
||||
mod player_action;
|
||||
@@ -58,14 +61,17 @@ pub use c_player_chat_message::*;
|
||||
pub use c_player_info_update::*;
|
||||
pub use c_player_remove::*;
|
||||
pub use c_remove_entities::*;
|
||||
pub use c_set_container_content::*;
|
||||
pub use c_set_container_slot::*;
|
||||
pub use c_set_held_item::*;
|
||||
pub use c_set_title::*;
|
||||
pub use c_spawn_player::*;
|
||||
pub use c_subtitle::*;
|
||||
pub use c_sync_player_position::*;
|
||||
pub use c_system_chat_message::*;
|
||||
pub use c_update_entitiy_pos_rot::*;
|
||||
pub use c_unload_chunk::*;
|
||||
pub use c_update_entity_pos::*;
|
||||
pub use c_update_entity_pos_rot::*;
|
||||
pub use c_update_entity_rot::*;
|
||||
pub use c_worldevent::*;
|
||||
pub use player_action::*;
|
||||
|
||||
@@ -21,6 +21,7 @@ pub const MAX_PACKET_SIZE: i32 = 2097152;
|
||||
pub type Identifier = String;
|
||||
pub type VarIntType = i32;
|
||||
pub type VarLongType = i64;
|
||||
pub type FixedBitSet = bytes::Bytes;
|
||||
|
||||
pub struct BitSet<'a>(pub VarInt, pub &'a [i64]);
|
||||
|
||||
@@ -142,7 +143,7 @@ pub enum PacketError {
|
||||
#[error("packet length is out of bounds")]
|
||||
OutOfBounds,
|
||||
#[error("malformed packet length VarInt")]
|
||||
MailformedLength,
|
||||
MalformedLength,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
||||
@@ -28,7 +28,7 @@ impl PacketDecoder {
|
||||
let packet_len = match VarInt::decode_partial(&mut r) {
|
||||
Ok(len) => len,
|
||||
Err(VarIntDecodeError::Incomplete) => return Ok(None),
|
||||
Err(VarIntDecodeError::TooLarge) => Err(PacketError::MailformedLength)?,
|
||||
Err(VarIntDecodeError::TooLarge) => Err(PacketError::MalformedLength)?,
|
||||
};
|
||||
|
||||
if !(0..=MAX_PACKET_SIZE).contains(&packet_len) {
|
||||
|
||||
@@ -3,7 +3,7 @@ use pumpkin_macros::packet;
|
||||
|
||||
use crate::{
|
||||
bytebuf::{ByteBuffer, DeserializerError},
|
||||
ServerPacket, VarInt,
|
||||
FixedBitSet, ServerPacket, VarInt,
|
||||
};
|
||||
|
||||
// derive(Deserialize)]
|
||||
@@ -13,9 +13,8 @@ pub struct SChatMessage {
|
||||
pub timestamp: i64,
|
||||
pub salt: i64,
|
||||
pub signature: Option<Bytes>,
|
||||
pub messagee_count: VarInt,
|
||||
// TODO: Properly implement BitSet decoding
|
||||
// acknowledged: BitSet,
|
||||
pub message_count: VarInt,
|
||||
pub acknowledged: FixedBitSet,
|
||||
}
|
||||
|
||||
// TODO
|
||||
@@ -26,7 +25,8 @@ impl ServerPacket for SChatMessage {
|
||||
timestamp: bytebuf.get_i64(),
|
||||
salt: bytebuf.get_i64(),
|
||||
signature: bytebuf.get_option(|v| v.copy_to_bytes(256)),
|
||||
messagee_count: bytebuf.get_var_int(),
|
||||
message_count: bytebuf.get_var_int(),
|
||||
acknowledged: bytebuf.get_fixed_bitset(20),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{bytebuf::DeserializerError, ServerPacket, VarInt};
|
||||
|
||||
#[packet(0x25)]
|
||||
pub struct SPlayerCommand {
|
||||
pub entitiy_id: VarInt,
|
||||
pub entity_id: VarInt,
|
||||
pub action: VarInt,
|
||||
pub jump_boost: VarInt,
|
||||
}
|
||||
@@ -16,8 +16,8 @@ pub enum Action {
|
||||
LeaveBed,
|
||||
StartSprinting,
|
||||
StopSprinting,
|
||||
StartHourseJump,
|
||||
StopHourseJump,
|
||||
StartHorseJump,
|
||||
StopHorseJump,
|
||||
OpenVehicleInventory,
|
||||
StartFlyingElytra,
|
||||
}
|
||||
@@ -25,7 +25,7 @@ pub enum Action {
|
||||
impl ServerPacket for SPlayerCommand {
|
||||
fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Result<Self, DeserializerError> {
|
||||
Ok(Self {
|
||||
entitiy_id: bytebuf.get_var_int(),
|
||||
entity_id: bytebuf.get_var_int(),
|
||||
action: bytebuf.get_var_int(),
|
||||
jump_boost: bytebuf.get_var_int(),
|
||||
})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::VarInt;
|
||||
use pumpkin_world::item::Item;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{
|
||||
de::{self, SeqAccess},
|
||||
Deserialize,
|
||||
Deserialize, Serialize, Serializer,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -76,6 +77,58 @@ impl<'de> Deserialize<'de> for Slot {
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Slot {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if self.item_count == 0.into() {
|
||||
let mut s = serializer.serialize_seq(Some(1))?;
|
||||
s.serialize_element(&self.item_count)?;
|
||||
s.end()
|
||||
} else {
|
||||
match (&self.num_components_to_add, &self.num_components_to_remove) {
|
||||
(Some(to_add), Some(to_remove)) => {
|
||||
let mut s = serializer.serialize_seq(Some(6))?;
|
||||
s.serialize_element(&self.item_count)?;
|
||||
s.serialize_element(self.item_id.as_ref().unwrap())?;
|
||||
s.serialize_element(to_add)?;
|
||||
s.serialize_element(to_remove)?;
|
||||
s.serialize_element(self.components_to_add.as_ref().unwrap())?;
|
||||
s.serialize_element(self.components_to_remove.as_ref().unwrap())?;
|
||||
s.end()
|
||||
}
|
||||
(None, Some(to_remove)) => {
|
||||
let mut s = serializer.serialize_seq(Some(5))?;
|
||||
s.serialize_element(&self.item_count)?;
|
||||
s.serialize_element(self.item_id.as_ref().unwrap())?;
|
||||
s.serialize_element(&VarInt(0))?;
|
||||
s.serialize_element(to_remove)?;
|
||||
s.serialize_element(self.components_to_remove.as_ref().unwrap())?;
|
||||
s.end()
|
||||
}
|
||||
(Some(to_add), None) => {
|
||||
let mut s = serializer.serialize_seq(Some(5))?;
|
||||
s.serialize_element(&self.item_count)?;
|
||||
s.serialize_element(self.item_id.as_ref().unwrap())?;
|
||||
s.serialize_element(to_add)?;
|
||||
s.serialize_element(&VarInt(0))?;
|
||||
s.serialize_element(self.components_to_add.as_ref().unwrap())?;
|
||||
s.end()
|
||||
}
|
||||
(None, None) => {
|
||||
let mut s = serializer.serialize_seq(Some(4))?;
|
||||
s.serialize_element(&self.item_count)?;
|
||||
s.serialize_element(&self.item_id.as_ref().unwrap())?;
|
||||
s.serialize_element(&VarInt(0))?;
|
||||
s.serialize_element(&VarInt(0))?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slot {
|
||||
pub fn to_item(self) -> Option<Item> {
|
||||
let item_id = self.item_id?.0.try_into().unwrap();
|
||||
@@ -84,12 +137,35 @@ impl Slot {
|
||||
item_count: self.item_count.0.try_into().unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl From<Slot> for Item {
|
||||
fn from(slot: Slot) -> Self {
|
||||
Item {
|
||||
item_count: slot.item_count.0.try_into().unwrap(),
|
||||
item_id: slot.item_id.unwrap().0.try_into().unwrap(),
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Slot {
|
||||
item_count: VarInt(0),
|
||||
item_id: None,
|
||||
num_components_to_add: None,
|
||||
num_components_to_remove: None,
|
||||
components_to_add: None,
|
||||
components_to_remove: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Item> for Slot {
|
||||
fn from(item: &Item) -> Self {
|
||||
Slot {
|
||||
item_count: item.item_count.into(),
|
||||
item_id: Some(item.item_id.into()),
|
||||
// TODO: add these
|
||||
num_components_to_add: None,
|
||||
num_components_to_remove: None,
|
||||
components_to_add: None,
|
||||
components_to_remove: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<&Item>> for Slot {
|
||||
fn from(item: Option<&Item>) -> Self {
|
||||
item.map(Slot::from).unwrap_or(Slot::empty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,14 @@ num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
num-bigint = "0.4.6"
|
||||
|
||||
ctrlc = "3.4"
|
||||
|
||||
# encryption
|
||||
rsa = "0.9.6"
|
||||
rsa-der = "0.3.0"
|
||||
|
||||
# authentication
|
||||
reqwest = { version = "0.12.5", features = ["json"]}
|
||||
reqwest = { version = "0.12.5", default-features=false, features = ["json", "rustls-tls", "http2", "macos-system-configuration"]}
|
||||
|
||||
sha1 = "0.10.6"
|
||||
digest = "=0.11.0-pre.9"
|
||||
|
||||
85
pumpkin/src/client/container.rs
Normal file
85
pumpkin/src/client/container.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_inventory::WindowType;
|
||||
use pumpkin_protocol::client::play::{COpenScreen, CSetContainerContent, CSetContainerSlot};
|
||||
use pumpkin_protocol::slot::Slot;
|
||||
use pumpkin_world::item::Item;
|
||||
|
||||
impl super::Client {
|
||||
pub fn open_container(
|
||||
&mut self,
|
||||
window_type: WindowType,
|
||||
minecraft_menu_id: &str,
|
||||
window_title: Option<&str>,
|
||||
items: Option<Vec<Option<&Item>>>,
|
||||
carried_item: Option<&Item>,
|
||||
) {
|
||||
let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY
|
||||
.get("minecraft:menu")
|
||||
.unwrap()
|
||||
.entries
|
||||
.get(minecraft_menu_id)
|
||||
.expect("Should be a valid menu id")
|
||||
.get("protocol_id")
|
||||
.unwrap())
|
||||
.into();
|
||||
let title = TextComponent::text(window_title.unwrap_or(window_type.default_title()));
|
||||
self.send_packet(&COpenScreen::new(
|
||||
(window_type.clone() as u8 + 1).into(),
|
||||
menu_protocol_id,
|
||||
title,
|
||||
));
|
||||
self.set_container_content(window_type, items, carried_item);
|
||||
}
|
||||
|
||||
pub fn set_container_content<'a>(
|
||||
&mut self,
|
||||
window_type: WindowType,
|
||||
items: Option<Vec<Option<&'a Item>>>,
|
||||
carried_item: Option<&'a Item>,
|
||||
) {
|
||||
let player = self.player.as_ref().unwrap();
|
||||
|
||||
let slots: Vec<Slot> = {
|
||||
if let Some(mut items) = items {
|
||||
items.extend(player.inventory.slots());
|
||||
items
|
||||
} else {
|
||||
player.inventory.slots()
|
||||
}
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
if let Some(item) = item {
|
||||
Slot::from(item)
|
||||
} else {
|
||||
Slot::empty()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let carried_item = {
|
||||
if let Some(item) = carried_item {
|
||||
item.into()
|
||||
} else {
|
||||
Slot::empty()
|
||||
}
|
||||
};
|
||||
let packet =
|
||||
CSetContainerContent::new(window_type as u8 + 1, 0.into(), &slots, &carried_item);
|
||||
self.send_packet(&packet);
|
||||
}
|
||||
|
||||
pub fn set_container_slot(
|
||||
&mut self,
|
||||
window_type: WindowType,
|
||||
slot: usize,
|
||||
item: Option<&Item>,
|
||||
) {
|
||||
self.send_packet(&CSetContainerSlot::new(
|
||||
window_type as i8,
|
||||
0,
|
||||
slot,
|
||||
&item.into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ use pumpkin_protocol::{
|
||||
client::{
|
||||
config::CConfigDisconnect,
|
||||
login::CLoginDisconnect,
|
||||
play::{CGameEvent, CPlayDisconnect, CSyncPlayerPostion, CSystemChatMessge},
|
||||
play::{CGameEvent, CPlayDisconnect, CSyncPlayerPosition, CSystemChatMessage},
|
||||
},
|
||||
packet_decoder::PacketDecoder,
|
||||
packet_encoder::PacketEncoder,
|
||||
@@ -43,6 +43,7 @@ use thiserror::Error;
|
||||
|
||||
pub mod authentication;
|
||||
mod client_packet;
|
||||
mod container;
|
||||
pub mod player_packet;
|
||||
|
||||
pub struct PlayerConfig {
|
||||
@@ -159,7 +160,7 @@ impl Client {
|
||||
entity.yaw = yaw;
|
||||
entity.pitch = pitch;
|
||||
player.awaiting_teleport = Some(id.into());
|
||||
self.send_packet(&CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into()));
|
||||
self.send_packet(&CSyncPlayerPosition::new(x, y, z, yaw, pitch, 0, id.into()));
|
||||
}
|
||||
|
||||
pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) {
|
||||
@@ -311,7 +312,7 @@ impl Client {
|
||||
SPlayPingRequest::PACKET_ID => {
|
||||
self.handle_play_ping_request(server, SPlayPingRequest::read(bytebuf).unwrap())
|
||||
}
|
||||
_ => log::error!("Failed to handle player packet id {}", packet.id.0),
|
||||
_ => log::error!("Failed to handle player packet id {:#04x}", packet.id.0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +362,7 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn send_system_message(&mut self, text: TextComponent) {
|
||||
self.send_packet(&CSystemChatMessge::new(text, false));
|
||||
self.send_packet(&CSystemChatMessage::new(text, false));
|
||||
}
|
||||
|
||||
/// Kicks the Client with a reason depending on the connection state
|
||||
|
||||
@@ -176,7 +176,7 @@ impl Client {
|
||||
pub fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) {
|
||||
let player = self.player.as_mut().unwrap();
|
||||
|
||||
if command.entitiy_id != player.entity.entity_id.into() {
|
||||
if command.entity_id != player.entity.entity_id.into() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -187,8 +187,8 @@ impl Client {
|
||||
pumpkin_protocol::server::play::Action::LeaveBed => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StartSprinting => player.sprinting = true,
|
||||
pumpkin_protocol::server::play::Action::StopSprinting => player.sprinting = false,
|
||||
pumpkin_protocol::server::play::Action::StartHourseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StopHourseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StartHorseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StopHorseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StartFlyingElytra => {} // TODO
|
||||
}
|
||||
@@ -204,7 +204,7 @@ impl Client {
|
||||
};
|
||||
let player = self.player.as_mut().unwrap();
|
||||
let id = player.entity_id();
|
||||
server.broadcast_packet_expect(
|
||||
server.broadcast_packet_except(
|
||||
&[&self.token],
|
||||
&CEntityAnimation::new(id.into(), animation as u8),
|
||||
)
|
||||
@@ -212,7 +212,13 @@ impl Client {
|
||||
|
||||
pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) {
|
||||
dbg!("got message");
|
||||
|
||||
let message = chat_message.message;
|
||||
if message.len() > 256 {
|
||||
self.kick("Oversized message");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: filter message & validation
|
||||
let gameprofile = self.gameprofile.as_ref().unwrap();
|
||||
|
||||
@@ -227,8 +233,7 @@ impl Client {
|
||||
chat_message.salt,
|
||||
&[],
|
||||
Some(TextComponent::text(&message)),
|
||||
pumpkin_protocol::VarInt(FilterType::PassThrough as i32),
|
||||
None,
|
||||
FilterType::PassThrough,
|
||||
1.into(),
|
||||
TextComponent::text(&gameprofile.name.clone()),
|
||||
None,
|
||||
@@ -305,14 +310,14 @@ impl Client {
|
||||
let packet = &CHurtAnimation::new(&entity_id, attacker_player.entity.yaw);
|
||||
self.send_packet(packet);
|
||||
client.send_packet(packet);
|
||||
server.broadcast_packet_expect(
|
||||
server.broadcast_packet_except(
|
||||
&[self.token.as_ref(), token.as_ref()],
|
||||
&CHurtAnimation::new(&entity_id, 10.0),
|
||||
)
|
||||
}
|
||||
if config.swing {}
|
||||
} else {
|
||||
self.kick("Interacted with invalid entitiy id")
|
||||
self.kick("Interacted with invalid entity id")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +405,12 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn handle_set_creative_slot(&mut self, _server: &mut Server, packet: SSetCreativeSlot) {
|
||||
let inventory = &mut self.player.as_mut().unwrap().inventory;
|
||||
let player = self.player.as_mut().unwrap();
|
||||
if player.gamemode != GameMode::Creative {
|
||||
self.kick("Invalid action, you can only do that if you are in creative");
|
||||
return;
|
||||
}
|
||||
let inventory = &mut player.inventory;
|
||||
|
||||
inventory.set_slot(packet.slot as usize, packet.clicked_item.to_item(), false);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::commands::CommandSender;
|
||||
use crate::commands::CommandSender::Player;
|
||||
use crate::entity::player::GameMode;
|
||||
|
||||
pub(crate) const NAME: &str = "gamemode";
|
||||
const NAMES: [&str; 1] = ["gamemode"];
|
||||
|
||||
const DESCRIPTION: &str = "Change a player's gamemode.";
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result<GameMode, Inva
|
||||
}
|
||||
|
||||
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(DESCRIPTION).with_child(
|
||||
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||
require(&|sender| sender.permission_lvl() >= 2).with_child(
|
||||
argument(ARG_GAMEMODE, consume_arg_gamemode)
|
||||
.with_child(
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
|
||||
use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
|
||||
use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
|
||||
use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs};
|
||||
use crate::commands::tree_builder::argument;
|
||||
use crate::commands::{dispatcher_init, CommandSender, DISPATCHER};
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
pub(crate) const NAME: &str = "help";
|
||||
pub(crate) const ALIAS: &str = "?";
|
||||
const NAMES: [&str; 3] = ["help", "h", "?"];
|
||||
|
||||
const DESCRIPTION: &str = "Print a help message.";
|
||||
|
||||
@@ -17,41 +16,35 @@ fn consume_arg_command(_src: &CommandSender, args: &mut RawArgs) -> Option<Strin
|
||||
|
||||
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
|
||||
|
||||
if dispatcher.commands.contains_key(s) {
|
||||
Some(s.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into())
|
||||
}
|
||||
|
||||
fn parse_arg_command<'a>(
|
||||
consumed_args: &'a ConsumedArgs,
|
||||
dispatcher: &'a CommandDispatcher,
|
||||
) -> Result<(&'a str, &'a CommandTree<'a>), InvalidTreeError> {
|
||||
) -> Result<&'a CommandTree<'a>, InvalidTreeError> {
|
||||
let command_name = consumed_args
|
||||
.get(ARG_COMMAND)
|
||||
.ok_or(InvalidConsumptionError(None))?;
|
||||
|
||||
if let Some(tree) = dispatcher.commands.get::<&str>(&command_name.as_str()) {
|
||||
Ok((command_name, tree))
|
||||
} else {
|
||||
Err(InvalidConsumptionError(Some(command_name.into())))
|
||||
}
|
||||
dispatcher
|
||||
.get_tree(command_name)
|
||||
.map_err(|_| InvalidConsumptionError(Some(command_name.into())))
|
||||
}
|
||||
|
||||
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(DESCRIPTION)
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(
|
||||
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, args| {
|
||||
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
|
||||
|
||||
let (name, tree) = parse_arg_command(args, dispatcher)?;
|
||||
let tree = parse_arg_command(args, dispatcher)?;
|
||||
|
||||
sender.send_message(TextComponent::text(&format!(
|
||||
"{} - {} Usage:{}",
|
||||
name,
|
||||
"{} - {} Usage: {}",
|
||||
tree.names.join("/"),
|
||||
tree.description,
|
||||
tree.paths_formatted(name)
|
||||
tree
|
||||
)));
|
||||
|
||||
Ok(())
|
||||
@@ -60,12 +53,19 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
.execute(&|sender, _args| {
|
||||
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
|
||||
|
||||
for (name, tree) in &dispatcher.commands {
|
||||
let mut keys: Vec<&str> = dispatcher.commands.keys().copied().collect();
|
||||
keys.sort();
|
||||
|
||||
for key in keys {
|
||||
let Command::Tree(tree) = &dispatcher.commands[key] else {
|
||||
continue;
|
||||
};
|
||||
|
||||
sender.send_message(TextComponent::text(&format!(
|
||||
"{} - {} Usage:{}",
|
||||
name,
|
||||
"{} - {} Usage: {}",
|
||||
tree.names.join("/"),
|
||||
tree.description,
|
||||
tree.paths_formatted(name)
|
||||
tree
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ use pumpkin_protocol::CURRENT_MC_PROTOCOL;
|
||||
|
||||
use crate::commands::tree::CommandTree;
|
||||
|
||||
pub(crate) const NAME: &str = "pumpkin";
|
||||
const NAMES: [&str; 1] = ["pumpkin"];
|
||||
|
||||
const DESCRIPTION: &str = "Display information about Pumpkin.";
|
||||
|
||||
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(DESCRIPTION).execute(&|sender, _| {
|
||||
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, _| {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let description = env!("CARGO_PKG_DESCRIPTION");
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
use pumpkin_core::text::color::NamedColor;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
use crate::commands::tree::CommandTree;
|
||||
use crate::commands::tree_builder::require;
|
||||
|
||||
pub(crate) const NAME: &str = "stop";
|
||||
const NAMES: [&str; 1] = ["stop"];
|
||||
|
||||
const DESCRIPTION: &str = "Stop the server.";
|
||||
|
||||
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
CommandTree::new(DESCRIPTION).with_child(
|
||||
require(&|sender| sender.permission_lvl() >= 4)
|
||||
.execute(&|_sender, _args| std::process::exit(0)),
|
||||
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||
require(&|sender| sender.permission_lvl() >= 4).execute(&|sender, _args| {
|
||||
sender
|
||||
.send_message(TextComponent::text("Stopping Server").color_named(NamedColor::Red));
|
||||
std::process::exit(0)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::commands::dispatcher::InvalidTreeError::{
|
||||
InvalidConsumptionError, InvalidRequirementError,
|
||||
};
|
||||
use crate::commands::tree::{CommandTree, ConsumedArgs, NodeType, RawArgs};
|
||||
use crate::commands::tree::{Command, CommandTree, ConsumedArgs, NodeType, RawArgs};
|
||||
use crate::commands::CommandSender;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub(crate) enum InvalidTreeError {
|
||||
}
|
||||
|
||||
pub(crate) struct CommandDispatcher<'a> {
|
||||
pub(crate) commands: HashMap<&'a str, CommandTree<'a>>,
|
||||
pub(crate) commands: HashMap<&'a str, Command<'a>>,
|
||||
}
|
||||
|
||||
/// Stores registered [CommandTree]s and dispatches commands to them.
|
||||
@@ -28,7 +28,7 @@ impl<'a> CommandDispatcher<'a> {
|
||||
let key = parts.next().ok_or("Empty Command")?;
|
||||
let raw_args: Vec<&str> = parts.rev().collect();
|
||||
|
||||
let tree = self.commands.get(key).ok_or("Command not found")?;
|
||||
let tree = self.get_tree(key)?;
|
||||
|
||||
// try paths until fitting path is found
|
||||
for path in tree.iter_paths() {
|
||||
@@ -49,10 +49,22 @@ impl<'a> CommandDispatcher<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Invalid Syntax. Usage:{}",
|
||||
tree.paths_formatted(key)
|
||||
))
|
||||
Err(format!("Invalid Syntax. Usage: {}", tree))
|
||||
}
|
||||
|
||||
pub(crate) fn get_tree(&'a self, key: &str) -> Result<&'a CommandTree<'a>, String> {
|
||||
let command = self.commands.get(key).ok_or("Command not found")?;
|
||||
|
||||
match command {
|
||||
Command::Tree(tree) => Ok(tree),
|
||||
Command::Alias(target) => {
|
||||
let Some(Command::Tree(tree)) = &self.commands.get(target) else {
|
||||
println!("Error while parsing command alias \"{key}\": pointing to \"{target}\" which is not a valid tree");
|
||||
return Err("Internal Error (See logs for details)".into());
|
||||
};
|
||||
Ok(tree)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_is_fitting_path(
|
||||
@@ -99,4 +111,17 @@ impl<'a> CommandDispatcher<'a> {
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Register a command with the dispatcher.
|
||||
pub(crate) fn register(&mut self, tree: CommandTree<'a>) {
|
||||
let mut names = tree.names.iter();
|
||||
|
||||
let primary_name = names.next().expect("at least one name must be provided");
|
||||
|
||||
for &name in names {
|
||||
self.commands.insert(name, Command::Alias(primary_name));
|
||||
}
|
||||
|
||||
self.commands.insert(primary_name, Command::Tree(tree));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ mod cmd_stop;
|
||||
mod dispatcher;
|
||||
mod tree;
|
||||
mod tree_builder;
|
||||
mod tree_format;
|
||||
|
||||
pub enum CommandSender<'a> {
|
||||
Rcon(&'a mut Vec<String>),
|
||||
@@ -23,9 +24,9 @@ impl<'a> CommandSender<'a> {
|
||||
pub fn send_message(&mut self, text: TextComponent) {
|
||||
match self {
|
||||
// TODO: add color and stuff to console
|
||||
CommandSender::Console => log::info!("{:?}", text.content),
|
||||
CommandSender::Console => log::info!("{}", text.to_pretty_console()),
|
||||
CommandSender::Player(c) => c.send_system_message(text),
|
||||
CommandSender::Rcon(s) => s.push(format!("{:?}", text.content)),
|
||||
CommandSender::Rcon(s) => s.push(text.to_pretty_console()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,15 +71,16 @@ static DISPATCHER: OnceLock<CommandDispatcher> = OnceLock::new();
|
||||
|
||||
/// create [CommandDispatcher] instance for [DISPATCHER]
|
||||
fn dispatcher_init<'a>() -> CommandDispatcher<'a> {
|
||||
let mut map = HashMap::new();
|
||||
let mut dispatcher = CommandDispatcher {
|
||||
commands: HashMap::new(),
|
||||
};
|
||||
|
||||
map.insert(cmd_pumpkin::NAME, cmd_pumpkin::init_command_tree());
|
||||
map.insert(cmd_gamemode::NAME, cmd_gamemode::init_command_tree());
|
||||
map.insert(cmd_stop::NAME, cmd_stop::init_command_tree());
|
||||
map.insert(cmd_help::NAME, cmd_help::init_command_tree());
|
||||
map.insert(cmd_help::ALIAS, cmd_help::init_command_tree());
|
||||
dispatcher.register(cmd_pumpkin::init_command_tree());
|
||||
dispatcher.register(cmd_gamemode::init_command_tree());
|
||||
dispatcher.register(cmd_stop::init_command_tree());
|
||||
dispatcher.register(cmd_help::init_command_tree());
|
||||
|
||||
CommandDispatcher { commands: map }
|
||||
dispatcher
|
||||
}
|
||||
|
||||
pub fn handle_command(sender: &mut CommandSender, cmd: &str) {
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use crate::commands::dispatcher::InvalidTreeError;
|
||||
use crate::commands::CommandSender;
|
||||
|
||||
/// see [crate::commands::tree_builder::argument]
|
||||
pub(crate) type RawArgs<'a> = Vec<&'a str>;
|
||||
|
||||
@@ -31,9 +32,15 @@ pub(crate) enum NodeType<'a> {
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) enum Command<'a> {
|
||||
Tree(CommandTree<'a>),
|
||||
Alias(&'a str),
|
||||
}
|
||||
|
||||
pub(crate) struct CommandTree<'a> {
|
||||
pub(crate) nodes: Vec<Node<'a>>,
|
||||
pub(crate) children: Vec<usize>,
|
||||
pub(crate) names: Vec<&'a str>,
|
||||
pub(crate) description: &'a str,
|
||||
}
|
||||
|
||||
@@ -51,57 +58,6 @@ impl<'a> CommandTree<'a> {
|
||||
todo,
|
||||
}
|
||||
}
|
||||
|
||||
/// format possible paths as [String], using ```name``` as the command name
|
||||
///
|
||||
/// todo: merge into single line
|
||||
pub(crate) fn paths_formatted(&'a self, name: &str) -> String {
|
||||
let paths: Vec<Vec<&NodeType>> = self
|
||||
.iter_paths()
|
||||
.map(|path| path.iter().map(|&i| &self.nodes[i].node_type).collect())
|
||||
.collect();
|
||||
|
||||
let len = paths
|
||||
.iter()
|
||||
.map(|path| {
|
||||
path.iter()
|
||||
.map(|node| match node {
|
||||
NodeType::ExecuteLeaf { .. } => 0,
|
||||
NodeType::Literal { string } => string.len() + 1,
|
||||
NodeType::Argument { name, .. } => name.len() + 3,
|
||||
NodeType::Require { .. } => 0,
|
||||
})
|
||||
.sum::<usize>()
|
||||
+ name.len()
|
||||
+ 2
|
||||
})
|
||||
.sum::<usize>();
|
||||
|
||||
let mut s = String::with_capacity(len);
|
||||
|
||||
for path in paths.iter() {
|
||||
s.push(if paths.len() > 1 { '\n' } else { ' ' });
|
||||
s.push('/');
|
||||
s.push_str(name);
|
||||
for node in path {
|
||||
match node {
|
||||
NodeType::Literal { string } => {
|
||||
s.push(' ');
|
||||
s.push_str(string);
|
||||
}
|
||||
NodeType::Argument { name, .. } => {
|
||||
s.push(' ');
|
||||
s.push('<');
|
||||
s.push_str(name);
|
||||
s.push('>');
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
struct TraverseAllPathsIter<'a> {
|
||||
|
||||
@@ -11,10 +11,23 @@ impl<'a> CommandTree<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn new(description: &'a str) -> Self {
|
||||
/// provide at least one name
|
||||
pub fn new<const NAME_COUNT: usize>(
|
||||
names: [&'a str; NAME_COUNT],
|
||||
description: &'a str,
|
||||
) -> Self {
|
||||
assert!(NAME_COUNT > 0);
|
||||
|
||||
let mut names_vec = Vec::with_capacity(NAME_COUNT);
|
||||
|
||||
for name in names {
|
||||
names_vec.push(name);
|
||||
}
|
||||
|
||||
Self {
|
||||
nodes: Vec::new(),
|
||||
children: Vec::new(),
|
||||
names: names_vec,
|
||||
description,
|
||||
}
|
||||
}
|
||||
|
||||
125
pumpkin/src/commands/tree_format.rs
Normal file
125
pumpkin/src/commands/tree_format.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use crate::commands::tree::{CommandTree, Node, NodeType};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
|
||||
trait IsVisible {
|
||||
/// whether node should be printed in help command/usage hint
|
||||
fn is_visible(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<'a> IsVisible for Node<'a> {
|
||||
fn is_visible(&self) -> bool {
|
||||
match self.node_type {
|
||||
NodeType::ExecuteLeaf { .. } => false,
|
||||
NodeType::Literal { .. } => true,
|
||||
NodeType::Argument { .. } => true,
|
||||
NodeType::Require { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Node<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self.node_type {
|
||||
NodeType::Literal { string } => {
|
||||
f.write_str(string)?;
|
||||
}
|
||||
NodeType::Argument { name, .. } => {
|
||||
f.write_char('<')?;
|
||||
f.write_str(name)?;
|
||||
f.write_char('>')?;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec<usize> {
|
||||
let mut new_children = Vec::with_capacity(children.len());
|
||||
|
||||
for &i in children {
|
||||
let node = &nodes[i];
|
||||
match &node.node_type {
|
||||
NodeType::Require { .. } => {
|
||||
new_children.extend(flatten_require_nodes(nodes, node.children.as_slice()))
|
||||
}
|
||||
_ => new_children.push(i),
|
||||
}
|
||||
}
|
||||
|
||||
new_children
|
||||
}
|
||||
|
||||
impl<'a> Display for CommandTree<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_char('/')?;
|
||||
f.write_str(self.names[0])?;
|
||||
|
||||
let mut todo = VecDeque::<&[usize]>::with_capacity(self.children.len());
|
||||
todo.push_back(&self.children);
|
||||
|
||||
loop {
|
||||
let Some(children) = todo.pop_front() else {
|
||||
break;
|
||||
};
|
||||
|
||||
let flattened_children = flatten_require_nodes(&self.nodes, children);
|
||||
let visible_children = flattened_children
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&i| self.nodes[i].is_visible())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if visible_children.is_empty() {
|
||||
break;
|
||||
};
|
||||
|
||||
f.write_char(' ')?;
|
||||
|
||||
let is_optional = flattened_children
|
||||
.iter()
|
||||
.map(|&i| &self.nodes[i].node_type)
|
||||
.any(|node| matches!(node, NodeType::ExecuteLeaf { .. }));
|
||||
|
||||
if is_optional {
|
||||
f.write_char('[')?;
|
||||
}
|
||||
|
||||
match visible_children.as_slice() {
|
||||
[] => unreachable!(),
|
||||
[i] => {
|
||||
let node = &self.nodes[*i];
|
||||
|
||||
node.fmt(f)?;
|
||||
|
||||
todo.push_back(&node.children);
|
||||
}
|
||||
_ => {
|
||||
// todo: handle cases where one of these nodes has visible children
|
||||
f.write_char('(')?;
|
||||
|
||||
let mut iter = visible_children.iter().map(|&i| &self.nodes[i]);
|
||||
|
||||
if let Some(node) = iter.next() {
|
||||
node.fmt(f)?;
|
||||
}
|
||||
|
||||
for node in iter {
|
||||
f.write_char('|')?;
|
||||
node.fmt(f)?;
|
||||
}
|
||||
|
||||
f.write_char(')')?;
|
||||
}
|
||||
}
|
||||
|
||||
if is_optional {
|
||||
f.write_char(']')?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
fn main() -> io::Result<()> {
|
||||
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let _profiler = dhat::Profiler::new_heap();
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
@@ -39,6 +41,17 @@ fn main() -> io::Result<()> {
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
ctrlc::set_handler(|| {
|
||||
log::warn!(
|
||||
"{}",
|
||||
TextComponent::text("Stopping Server")
|
||||
.color_named(NamedColor::Red)
|
||||
.to_pretty_console()
|
||||
);
|
||||
std::process::exit(0);
|
||||
})
|
||||
.unwrap();
|
||||
// ensure rayon is built outside of tokio scope
|
||||
rayon::ThreadPoolBuilder::new().build_global().unwrap();
|
||||
rt.block_on(async {
|
||||
|
||||
@@ -17,7 +17,6 @@ use mio::{event::Event, Poll, Token};
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::ByteBuffer,
|
||||
client::{
|
||||
config::CPluginMessage,
|
||||
play::{
|
||||
@@ -141,11 +140,11 @@ impl Server {
|
||||
if client.is_player() {
|
||||
let id = client.player.as_ref().unwrap().entity_id();
|
||||
let uuid = client.gameprofile.as_ref().unwrap().id;
|
||||
self.broadcast_packet_expect(
|
||||
self.broadcast_packet_except(
|
||||
&[&client.token],
|
||||
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
||||
);
|
||||
self.broadcast_packet_expect(&[&client.token], &CRemoveEntities::new(&[id.into()]))
|
||||
self.broadcast_packet_except(&[&client.token], &CRemoveEntities::new(&[id.into()]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +240,7 @@ impl Server {
|
||||
let gameprofile = client.gameprofile.as_ref().unwrap();
|
||||
|
||||
// spawn player for every client
|
||||
self.broadcast_packet_expect(
|
||||
self.broadcast_packet_except(
|
||||
&[&client.token],
|
||||
// TODO: add velo
|
||||
&CSpawnEntity::new(
|
||||
@@ -327,7 +326,8 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast_packet_expect<P>(&self, from: &[&Token], packet: &P)
|
||||
/// Sends a packet to all players except those specified in `from`
|
||||
pub fn broadcast_packet_except<P>(&self, from: &[&Token], packet: &P)
|
||||
where
|
||||
P: ClientPacket,
|
||||
{
|
||||
@@ -363,14 +363,15 @@ impl Server {
|
||||
chunk_z: 0.into(),
|
||||
});
|
||||
|
||||
while let Some((chunk_pos, chunk_data)) = chunk_receiver.recv().await {
|
||||
while let Some((_chunk_pos, chunk_data)) = chunk_receiver.recv().await {
|
||||
// dbg!(chunk_pos);
|
||||
let chunk_data = match chunk_data {
|
||||
Ok(d) => d,
|
||||
Err(_) => continue,
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
if chunk_pos == (0, 0) {
|
||||
if _chunk_pos == (0, 0) {
|
||||
use pumpkin_protocol::bytebuf::ByteBuffer;
|
||||
let mut test = ByteBuffer::empty();
|
||||
CChunkData(&chunk_data).write(&mut test);
|
||||
let len = test.buf().len();
|
||||
|
||||
Reference in New Issue
Block a user