Initial commit
This commit is contained in:
commit
e06c4882a0
47 changed files with 3246 additions and 0 deletions
12
.envrc
Normal file
12
.envrc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
|
||||||
|
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
|
||||||
|
fi
|
||||||
|
|
||||||
|
watch_file devenv.nix
|
||||||
|
watch_file devenv.lock
|
||||||
|
watch_file devenv.yaml
|
||||||
|
if ! use flake . --impure
|
||||||
|
then
|
||||||
|
echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
|
||||||
|
fi
|
||||||
|
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/target/
|
||||||
|
.direnv/
|
||||||
|
.devenv/
|
233
Cargo.lock
generated
Normal file
233
Cargo.lock
generated
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cesu8"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastnbt"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "git+https://github.com/owengage/fastnbt.git#e2a5d8a7001d4f074ae99fd21bb485667934baeb"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"cesu8",
|
||||||
|
"serde",
|
||||||
|
"serde_bytes",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potato"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"potato-protocol",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potato-protocol"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"fastnbt",
|
||||||
|
"potato-protocol-derive",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potato-protocol-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_bytes"
|
||||||
|
version = "0.11.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = ["potato", "potato-protocol", "potato-protocol-derive"]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
serde = { version = "1.0.218", features = ["derive"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uuid = "1.15.1"
|
316
flake.lock
generated
Normal file
316
flake.lock
generated
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"cachix": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"git-hooks": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1737621947,
|
||||||
|
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "cachix",
|
||||||
|
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "latest",
|
||||||
|
"repo": "cachix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devenv": {
|
||||||
|
"inputs": {
|
||||||
|
"cachix": "cachix",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nix": "nix",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1741068816,
|
||||||
|
"narHash": "sha256-JvaktGlQ/j+7+sbcl1OHcQmht7w+7AGDVmHldCezUkc=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "9f6da63c162ad86b6fb84edcbd8c447fdc411c3d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "main",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1741156584,
|
||||||
|
"narHash": "sha256-Xju6PhR09gR8cSS1s4FOHw4AhUUmrFDUs9Wj/9KFoGY=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "1271797d7c0537b4e5bdd4061a2954b846f2c29c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733328505,
|
||||||
|
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"devenv",
|
||||||
|
"nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712014858,
|
||||||
|
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1740849354,
|
||||||
|
"narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"git-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709087332,
|
||||||
|
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libgit2": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1697646580,
|
||||||
|
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||||
|
"owner": "libgit2",
|
||||||
|
"repo": "libgit2",
|
||||||
|
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "libgit2",
|
||||||
|
"repo": "libgit2",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"libgit2": "libgit2",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"nixpkgs-23-11": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"nixpkgs-regression": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"devenv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1734114420,
|
||||||
|
"narHash": "sha256-n52PUzub5jZWc8nI/sR7UICOheU8rNA+YZ73YaHeCBg=",
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"repo": "nix",
|
||||||
|
"rev": "bde6a1a0d1f2af86caa4d20d23eca019f3d57eee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"ref": "devenv-2.24",
|
||||||
|
"repo": "nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733212471,
|
||||||
|
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717432640,
|
||||||
|
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733477122,
|
||||||
|
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_4": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1741010256,
|
||||||
|
"narHash": "sha256-WZNlK/KX7Sni0RyqLSqLPbK8k08Kq7H7RijPJbq9KHM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ba487dbc9d04e0634c64e3b1f0d25839a0a68246",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs_4",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1741011961,
|
||||||
|
"narHash": "sha256-bssSxw3Z9CUNB9+f3EHAX/2urT15e12Jy6YU8tHyWkk=",
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "02862f5d52c30b476a5dca909a17aa4386d1fdc5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
59
flake.nix
Normal file
59
flake.nix
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
devenv.url = "github:cachix/devenv/main";
|
||||||
|
|
||||||
|
fenix.url = "github:nix-community/fenix";
|
||||||
|
fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
devenv,
|
||||||
|
systems,
|
||||||
|
...
|
||||||
|
}@inputs:
|
||||||
|
let
|
||||||
|
forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forEachSystem (system: {
|
||||||
|
devenv-up = self.devShells.${system}.default.config.procfileScript;
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells = forEachSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = devenv.lib.mkShell {
|
||||||
|
inherit inputs pkgs;
|
||||||
|
modules = [
|
||||||
|
(
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
dotenv.disableHint = true;
|
||||||
|
|
||||||
|
languages.rust = {
|
||||||
|
enable = true;
|
||||||
|
channel = "nightly";
|
||||||
|
components = [
|
||||||
|
"rustc"
|
||||||
|
"cargo"
|
||||||
|
"clippy"
|
||||||
|
"rustfmt"
|
||||||
|
"rust-analyzer"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
13
potato-protocol-derive/Cargo.toml
Normal file
13
potato-protocol-derive/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "potato-protocol-derive"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
darling = "0.20.10"
|
||||||
|
proc-macro2 = "1.0.94"
|
||||||
|
quote = "1.0.39"
|
||||||
|
syn = "2.0.99"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
216
potato-protocol-derive/src/lib.rs
Normal file
216
potato-protocol-derive/src/lib.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
#![recursion_limit = "128"]
|
||||||
|
|
||||||
|
use darling::{FromDeriveInput, FromVariant};
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::Path;
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
extern crate syn;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quote;
|
||||||
|
|
||||||
|
#[derive(FromDeriveInput)]
|
||||||
|
#[darling(attributes(packet))]
|
||||||
|
struct PacketOpts {
|
||||||
|
handshake_id: Option<Path>,
|
||||||
|
status_id: Option<Path>,
|
||||||
|
login_id: Option<Path>,
|
||||||
|
configuration_id: Option<Path>,
|
||||||
|
play_id: Option<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Packet, attributes(packet))]
|
||||||
|
pub fn packet_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let ast = syn::parse(input).unwrap();
|
||||||
|
let opts = PacketOpts::from_derive_input(&ast).expect("Wrong options");
|
||||||
|
let packet_encodable_impl = generate_packet_encodable_impl(&ast);
|
||||||
|
let packet_impl = generate_packet_impl(&ast, opts);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#packet_encodable_impl
|
||||||
|
#packet_impl
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(PacketEncodable, attributes(packet))]
|
||||||
|
pub fn packet_encodable_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let ast = syn::parse(input).unwrap();
|
||||||
|
let generated = generate_packet_encodable_impl(&ast);
|
||||||
|
|
||||||
|
generated.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(JsonString)]
|
||||||
|
pub fn json_string_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
let ident = &ast.ident;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl crate::packet_encodable::JsonString for #ident {}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_packet_impl(
|
||||||
|
ast: &syn::DeriveInput,
|
||||||
|
packet_opts: PacketOpts,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let ident = &ast.ident;
|
||||||
|
|
||||||
|
let handshake_id = packet_opts
|
||||||
|
.handshake_id
|
||||||
|
.map(ToTokens::into_token_stream)
|
||||||
|
.unwrap_or(quote! {-1});
|
||||||
|
|
||||||
|
let status_id = packet_opts
|
||||||
|
.status_id
|
||||||
|
.map(ToTokens::into_token_stream)
|
||||||
|
.unwrap_or(quote! {-1});
|
||||||
|
|
||||||
|
let login_id = packet_opts
|
||||||
|
.login_id
|
||||||
|
.map(ToTokens::into_token_stream)
|
||||||
|
.unwrap_or(quote! {-1});
|
||||||
|
|
||||||
|
let configuration_id = packet_opts
|
||||||
|
.configuration_id
|
||||||
|
.map(ToTokens::into_token_stream)
|
||||||
|
.unwrap_or(quote! {-1});
|
||||||
|
|
||||||
|
let play_id = packet_opts
|
||||||
|
.play_id
|
||||||
|
.map(ToTokens::into_token_stream)
|
||||||
|
.unwrap_or(quote! {-1});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl crate::packet::Packet for #ident {
|
||||||
|
const HANDSHAKE_ID: i32 = #handshake_id;
|
||||||
|
const STATUS_ID: i32 = #status_id;
|
||||||
|
const LOGIN_ID: i32 = #login_id;
|
||||||
|
const CONFIGURATION_ID: i32 = #configuration_id;
|
||||||
|
const PLAY_ID: i32 = #play_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_packet_encodable_impl(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
|
match ast.data {
|
||||||
|
syn::Data::Enum(syn::DataEnum { ref variants, .. }) => {
|
||||||
|
generate_packet_encodable_enum_impl(ast, variants)
|
||||||
|
}
|
||||||
|
syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
|
||||||
|
generate_packet_encodable_struct_impl(ast, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
syn::Data::Union(_) => panic!("PacketEncodable can not be implemented for unions"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_packet_encodable_enum_impl(
|
||||||
|
ast: &syn::DeriveInput,
|
||||||
|
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let ident = &ast.ident;
|
||||||
|
let encode_arms: Vec<_> = variants
|
||||||
|
.iter()
|
||||||
|
.map(generate_enum_variant_encode_packet_arms)
|
||||||
|
.collect();
|
||||||
|
let decode_arms: Vec<_> = variants
|
||||||
|
.iter()
|
||||||
|
.map(generate_enum_variant_decode_packet_arms)
|
||||||
|
.collect();
|
||||||
|
quote! {
|
||||||
|
impl crate::packet_encodable::PacketEncodable for #ident {
|
||||||
|
fn encode_packet(&self, cursor: &mut Vec<u8>) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
|
let value: crate::datatypes::var_int::VarInt = match self {
|
||||||
|
#(#encode_arms)*
|
||||||
|
}.into();
|
||||||
|
|
||||||
|
value.encode_packet(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
|
let value: i32 = crate::datatypes::var_int::VarInt::decode_packet(cursor)?.into();
|
||||||
|
match value {
|
||||||
|
#(#decode_arms)*
|
||||||
|
_ => Err(crate::packet_encodable::PacketDecodeError::UnkownEnumVariant {
|
||||||
|
name: stringify!(#ident),
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_packet_encodable_struct_impl(
|
||||||
|
ast: &syn::DeriveInput,
|
||||||
|
fields: &syn::Fields,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let ident = &ast.ident;
|
||||||
|
let fields_encode_packet_calls: Vec<_> = fields
|
||||||
|
.iter()
|
||||||
|
.map(generate_struct_field_encode_packet_calls)
|
||||||
|
.collect();
|
||||||
|
let fields_decode_packet_calls: Vec<_> = fields
|
||||||
|
.iter()
|
||||||
|
.map(generate_struct_field_decode_packet_calls)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl crate::packet_encodable::PacketEncodable for #ident {
|
||||||
|
fn encode_packet(&self, cursor: &mut Vec<u8>) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
|
#(#fields_encode_packet_calls)*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
|
Ok(Self {
|
||||||
|
#(#fields_decode_packet_calls)*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_struct_field_decode_packet_calls(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||||
|
let field_ident = &field.ident;
|
||||||
|
let field_ty = &field.ty;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#field_ident: <#field_ty>::decode_packet(cursor)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_struct_field_encode_packet_calls(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||||
|
let field_ident = &field.ident;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
self.#field_ident.encode_packet(cursor)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromVariant)]
|
||||||
|
#[darling(attributes(packet))]
|
||||||
|
struct EnumVariant {
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_enum_variant_decode_packet_arms(variant: &syn::Variant) -> proc_macro2::TokenStream {
|
||||||
|
let ident = &variant.ident;
|
||||||
|
let enum_id = EnumVariant::from_variant(variant).unwrap().id;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#enum_id => Ok(Self::#ident),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_enum_variant_encode_packet_arms(variant: &syn::Variant) -> proc_macro2::TokenStream {
|
||||||
|
let ident = &variant.ident;
|
||||||
|
let enum_id = EnumVariant::from_variant(variant).unwrap().id;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#ident => #enum_id,
|
||||||
|
}
|
||||||
|
}
|
21
potato-protocol/Cargo.toml
Normal file
21
potato-protocol/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "potato-protocol"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
potato-protocol-derive = { path = "../potato-protocol-derive" }
|
||||||
|
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
|
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
|
||||||
|
# Build from git, since there has not been a release in over a year
|
||||||
|
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
65
potato-protocol/build.rs
Normal file
65
potato-protocol/build.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const PACKETS_STR: &'static str = include_str!("packets.json");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo::rerun-if-changed=packets.json");
|
||||||
|
|
||||||
|
let out_dir = std::env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_path = std::path::Path::new(&out_dir).join("ids.rs");
|
||||||
|
|
||||||
|
let packets: Packets = serde_json::from_str(PACKETS_STR).unwrap();
|
||||||
|
|
||||||
|
let out_string = &mut String::new();
|
||||||
|
write_phase("handshake", &packets.handshake, out_string);
|
||||||
|
write_phase("status", &packets.status, out_string);
|
||||||
|
write_phase("login", &packets.login, out_string);
|
||||||
|
write_phase("configuration", &packets.configuration, out_string);
|
||||||
|
write_phase("play", &packets.play, out_string);
|
||||||
|
|
||||||
|
std::fs::write(&out_path, out_string).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_phase(name: &str, phase: &Phase, out: &mut String) {
|
||||||
|
out.push_str(&format!("pub mod {} {{\n", name));
|
||||||
|
if let Some(packets) = &phase.clientbound {
|
||||||
|
write_side("clientbound", packets, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(packets) = &phase.serverbound {
|
||||||
|
write_side("serverbound", packets, out);
|
||||||
|
}
|
||||||
|
out.push_str("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_side(side: &str, packets: &HashMap<String, Packet>, out: &mut String) {
|
||||||
|
out.push_str(&format!("pub mod {} {{\n", side.to_lowercase()));
|
||||||
|
for (name, packet) in packets {
|
||||||
|
out.push_str(&format!(
|
||||||
|
"pub const {}: i32 = 0x{:02X};\n",
|
||||||
|
name.replace("minecraft:", "").to_uppercase(),
|
||||||
|
packet.protocol_id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
out.push_str("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct Packets {
|
||||||
|
handshake: Phase,
|
||||||
|
status: Phase,
|
||||||
|
login: Phase,
|
||||||
|
configuration: Phase,
|
||||||
|
play: Phase,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct Phase {
|
||||||
|
clientbound: Option<HashMap<String, Packet>>,
|
||||||
|
serverbound: Option<HashMap<String, Packet>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct Packet {
|
||||||
|
protocol_id: i32,
|
||||||
|
}
|
732
potato-protocol/packets.json
Normal file
732
potato-protocol/packets.json
Normal file
|
@ -0,0 +1,732 @@
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"clientbound": {
|
||||||
|
"minecraft:cookie_request": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:custom_payload": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:custom_report_details": {
|
||||||
|
"protocol_id": 15
|
||||||
|
},
|
||||||
|
"minecraft:disconnect": {
|
||||||
|
"protocol_id": 2
|
||||||
|
},
|
||||||
|
"minecraft:finish_configuration": {
|
||||||
|
"protocol_id": 3
|
||||||
|
},
|
||||||
|
"minecraft:keep_alive": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:ping": {
|
||||||
|
"protocol_id": 5
|
||||||
|
},
|
||||||
|
"minecraft:registry_data": {
|
||||||
|
"protocol_id": 7
|
||||||
|
},
|
||||||
|
"minecraft:reset_chat": {
|
||||||
|
"protocol_id": 6
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack_pop": {
|
||||||
|
"protocol_id": 8
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack_push": {
|
||||||
|
"protocol_id": 9
|
||||||
|
},
|
||||||
|
"minecraft:select_known_packs": {
|
||||||
|
"protocol_id": 14
|
||||||
|
},
|
||||||
|
"minecraft:server_links": {
|
||||||
|
"protocol_id": 16
|
||||||
|
},
|
||||||
|
"minecraft:store_cookie": {
|
||||||
|
"protocol_id": 10
|
||||||
|
},
|
||||||
|
"minecraft:transfer": {
|
||||||
|
"protocol_id": 11
|
||||||
|
},
|
||||||
|
"minecraft:update_enabled_features": {
|
||||||
|
"protocol_id": 12
|
||||||
|
},
|
||||||
|
"minecraft:update_tags": {
|
||||||
|
"protocol_id": 13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverbound": {
|
||||||
|
"minecraft:client_information": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:cookie_response": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:custom_payload": {
|
||||||
|
"protocol_id": 2
|
||||||
|
},
|
||||||
|
"minecraft:finish_configuration": {
|
||||||
|
"protocol_id": 3
|
||||||
|
},
|
||||||
|
"minecraft:keep_alive": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:pong": {
|
||||||
|
"protocol_id": 5
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack": {
|
||||||
|
"protocol_id": 6
|
||||||
|
},
|
||||||
|
"minecraft:select_known_packs": {
|
||||||
|
"protocol_id": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"handshake": {
|
||||||
|
"serverbound": {
|
||||||
|
"minecraft:intention": {
|
||||||
|
"protocol_id": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"clientbound": {
|
||||||
|
"minecraft:cookie_request": {
|
||||||
|
"protocol_id": 5
|
||||||
|
},
|
||||||
|
"minecraft:custom_query": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:hello": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:login_compression": {
|
||||||
|
"protocol_id": 3
|
||||||
|
},
|
||||||
|
"minecraft:login_disconnect": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:login_finished": {
|
||||||
|
"protocol_id": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverbound": {
|
||||||
|
"minecraft:cookie_response": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:custom_query_answer": {
|
||||||
|
"protocol_id": 2
|
||||||
|
},
|
||||||
|
"minecraft:hello": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:key": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:login_acknowledged": {
|
||||||
|
"protocol_id": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"play": {
|
||||||
|
"clientbound": {
|
||||||
|
"minecraft:add_entity": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:add_experience_orb": {
|
||||||
|
"protocol_id": 2
|
||||||
|
},
|
||||||
|
"minecraft:animate": {
|
||||||
|
"protocol_id": 3
|
||||||
|
},
|
||||||
|
"minecraft:award_stats": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:block_changed_ack": {
|
||||||
|
"protocol_id": 5
|
||||||
|
},
|
||||||
|
"minecraft:block_destruction": {
|
||||||
|
"protocol_id": 6
|
||||||
|
},
|
||||||
|
"minecraft:block_entity_data": {
|
||||||
|
"protocol_id": 7
|
||||||
|
},
|
||||||
|
"minecraft:block_event": {
|
||||||
|
"protocol_id": 8
|
||||||
|
},
|
||||||
|
"minecraft:block_update": {
|
||||||
|
"protocol_id": 9
|
||||||
|
},
|
||||||
|
"minecraft:boss_event": {
|
||||||
|
"protocol_id": 10
|
||||||
|
},
|
||||||
|
"minecraft:bundle_delimiter": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:change_difficulty": {
|
||||||
|
"protocol_id": 11
|
||||||
|
},
|
||||||
|
"minecraft:chunk_batch_finished": {
|
||||||
|
"protocol_id": 12
|
||||||
|
},
|
||||||
|
"minecraft:chunk_batch_start": {
|
||||||
|
"protocol_id": 13
|
||||||
|
},
|
||||||
|
"minecraft:chunks_biomes": {
|
||||||
|
"protocol_id": 14
|
||||||
|
},
|
||||||
|
"minecraft:clear_titles": {
|
||||||
|
"protocol_id": 15
|
||||||
|
},
|
||||||
|
"minecraft:command_suggestions": {
|
||||||
|
"protocol_id": 16
|
||||||
|
},
|
||||||
|
"minecraft:commands": {
|
||||||
|
"protocol_id": 17
|
||||||
|
},
|
||||||
|
"minecraft:container_close": {
|
||||||
|
"protocol_id": 18
|
||||||
|
},
|
||||||
|
"minecraft:container_set_content": {
|
||||||
|
"protocol_id": 19
|
||||||
|
},
|
||||||
|
"minecraft:container_set_data": {
|
||||||
|
"protocol_id": 20
|
||||||
|
},
|
||||||
|
"minecraft:container_set_slot": {
|
||||||
|
"protocol_id": 21
|
||||||
|
},
|
||||||
|
"minecraft:cookie_request": {
|
||||||
|
"protocol_id": 22
|
||||||
|
},
|
||||||
|
"minecraft:cooldown": {
|
||||||
|
"protocol_id": 23
|
||||||
|
},
|
||||||
|
"minecraft:custom_chat_completions": {
|
||||||
|
"protocol_id": 24
|
||||||
|
},
|
||||||
|
"minecraft:custom_payload": {
|
||||||
|
"protocol_id": 25
|
||||||
|
},
|
||||||
|
"minecraft:custom_report_details": {
|
||||||
|
"protocol_id": 129
|
||||||
|
},
|
||||||
|
"minecraft:damage_event": {
|
||||||
|
"protocol_id": 26
|
||||||
|
},
|
||||||
|
"minecraft:debug_sample": {
|
||||||
|
"protocol_id": 27
|
||||||
|
},
|
||||||
|
"minecraft:delete_chat": {
|
||||||
|
"protocol_id": 28
|
||||||
|
},
|
||||||
|
"minecraft:disconnect": {
|
||||||
|
"protocol_id": 29
|
||||||
|
},
|
||||||
|
"minecraft:disguised_chat": {
|
||||||
|
"protocol_id": 30
|
||||||
|
},
|
||||||
|
"minecraft:entity_event": {
|
||||||
|
"protocol_id": 31
|
||||||
|
},
|
||||||
|
"minecraft:entity_position_sync": {
|
||||||
|
"protocol_id": 32
|
||||||
|
},
|
||||||
|
"minecraft:explode": {
|
||||||
|
"protocol_id": 33
|
||||||
|
},
|
||||||
|
"minecraft:forget_level_chunk": {
|
||||||
|
"protocol_id": 34
|
||||||
|
},
|
||||||
|
"minecraft:game_event": {
|
||||||
|
"protocol_id": 35
|
||||||
|
},
|
||||||
|
"minecraft:horse_screen_open": {
|
||||||
|
"protocol_id": 36
|
||||||
|
},
|
||||||
|
"minecraft:hurt_animation": {
|
||||||
|
"protocol_id": 37
|
||||||
|
},
|
||||||
|
"minecraft:initialize_border": {
|
||||||
|
"protocol_id": 38
|
||||||
|
},
|
||||||
|
"minecraft:keep_alive": {
|
||||||
|
"protocol_id": 39
|
||||||
|
},
|
||||||
|
"minecraft:level_chunk_with_light": {
|
||||||
|
"protocol_id": 40
|
||||||
|
},
|
||||||
|
"minecraft:level_event": {
|
||||||
|
"protocol_id": 41
|
||||||
|
},
|
||||||
|
"minecraft:level_particles": {
|
||||||
|
"protocol_id": 42
|
||||||
|
},
|
||||||
|
"minecraft:light_update": {
|
||||||
|
"protocol_id": 43
|
||||||
|
},
|
||||||
|
"minecraft:login": {
|
||||||
|
"protocol_id": 44
|
||||||
|
},
|
||||||
|
"minecraft:map_item_data": {
|
||||||
|
"protocol_id": 45
|
||||||
|
},
|
||||||
|
"minecraft:merchant_offers": {
|
||||||
|
"protocol_id": 46
|
||||||
|
},
|
||||||
|
"minecraft:move_entity_pos": {
|
||||||
|
"protocol_id": 47
|
||||||
|
},
|
||||||
|
"minecraft:move_entity_pos_rot": {
|
||||||
|
"protocol_id": 48
|
||||||
|
},
|
||||||
|
"minecraft:move_entity_rot": {
|
||||||
|
"protocol_id": 50
|
||||||
|
},
|
||||||
|
"minecraft:move_minecart_along_track": {
|
||||||
|
"protocol_id": 49
|
||||||
|
},
|
||||||
|
"minecraft:move_vehicle": {
|
||||||
|
"protocol_id": 51
|
||||||
|
},
|
||||||
|
"minecraft:open_book": {
|
||||||
|
"protocol_id": 52
|
||||||
|
},
|
||||||
|
"minecraft:open_screen": {
|
||||||
|
"protocol_id": 53
|
||||||
|
},
|
||||||
|
"minecraft:open_sign_editor": {
|
||||||
|
"protocol_id": 54
|
||||||
|
},
|
||||||
|
"minecraft:ping": {
|
||||||
|
"protocol_id": 55
|
||||||
|
},
|
||||||
|
"minecraft:place_ghost_recipe": {
|
||||||
|
"protocol_id": 57
|
||||||
|
},
|
||||||
|
"minecraft:player_abilities": {
|
||||||
|
"protocol_id": 58
|
||||||
|
},
|
||||||
|
"minecraft:player_chat": {
|
||||||
|
"protocol_id": 59
|
||||||
|
},
|
||||||
|
"minecraft:player_combat_end": {
|
||||||
|
"protocol_id": 60
|
||||||
|
},
|
||||||
|
"minecraft:player_combat_enter": {
|
||||||
|
"protocol_id": 61
|
||||||
|
},
|
||||||
|
"minecraft:player_combat_kill": {
|
||||||
|
"protocol_id": 62
|
||||||
|
},
|
||||||
|
"minecraft:player_info_remove": {
|
||||||
|
"protocol_id": 63
|
||||||
|
},
|
||||||
|
"minecraft:player_info_update": {
|
||||||
|
"protocol_id": 64
|
||||||
|
},
|
||||||
|
"minecraft:player_look_at": {
|
||||||
|
"protocol_id": 65
|
||||||
|
},
|
||||||
|
"minecraft:player_position": {
|
||||||
|
"protocol_id": 66
|
||||||
|
},
|
||||||
|
"minecraft:player_rotation": {
|
||||||
|
"protocol_id": 67
|
||||||
|
},
|
||||||
|
"minecraft:pong_response": {
|
||||||
|
"protocol_id": 56
|
||||||
|
},
|
||||||
|
"minecraft:projectile_power": {
|
||||||
|
"protocol_id": 128
|
||||||
|
},
|
||||||
|
"minecraft:recipe_book_add": {
|
||||||
|
"protocol_id": 68
|
||||||
|
},
|
||||||
|
"minecraft:recipe_book_remove": {
|
||||||
|
"protocol_id": 69
|
||||||
|
},
|
||||||
|
"minecraft:recipe_book_settings": {
|
||||||
|
"protocol_id": 70
|
||||||
|
},
|
||||||
|
"minecraft:remove_entities": {
|
||||||
|
"protocol_id": 71
|
||||||
|
},
|
||||||
|
"minecraft:remove_mob_effect": {
|
||||||
|
"protocol_id": 72
|
||||||
|
},
|
||||||
|
"minecraft:reset_score": {
|
||||||
|
"protocol_id": 73
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack_pop": {
|
||||||
|
"protocol_id": 74
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack_push": {
|
||||||
|
"protocol_id": 75
|
||||||
|
},
|
||||||
|
"minecraft:respawn": {
|
||||||
|
"protocol_id": 76
|
||||||
|
},
|
||||||
|
"minecraft:rotate_head": {
|
||||||
|
"protocol_id": 77
|
||||||
|
},
|
||||||
|
"minecraft:section_blocks_update": {
|
||||||
|
"protocol_id": 78
|
||||||
|
},
|
||||||
|
"minecraft:select_advancements_tab": {
|
||||||
|
"protocol_id": 79
|
||||||
|
},
|
||||||
|
"minecraft:server_data": {
|
||||||
|
"protocol_id": 80
|
||||||
|
},
|
||||||
|
"minecraft:server_links": {
|
||||||
|
"protocol_id": 130
|
||||||
|
},
|
||||||
|
"minecraft:set_action_bar_text": {
|
||||||
|
"protocol_id": 81
|
||||||
|
},
|
||||||
|
"minecraft:set_border_center": {
|
||||||
|
"protocol_id": 82
|
||||||
|
},
|
||||||
|
"minecraft:set_border_lerp_size": {
|
||||||
|
"protocol_id": 83
|
||||||
|
},
|
||||||
|
"minecraft:set_border_size": {
|
||||||
|
"protocol_id": 84
|
||||||
|
},
|
||||||
|
"minecraft:set_border_warning_delay": {
|
||||||
|
"protocol_id": 85
|
||||||
|
},
|
||||||
|
"minecraft:set_border_warning_distance": {
|
||||||
|
"protocol_id": 86
|
||||||
|
},
|
||||||
|
"minecraft:set_camera": {
|
||||||
|
"protocol_id": 87
|
||||||
|
},
|
||||||
|
"minecraft:set_chunk_cache_center": {
|
||||||
|
"protocol_id": 88
|
||||||
|
},
|
||||||
|
"minecraft:set_chunk_cache_radius": {
|
||||||
|
"protocol_id": 89
|
||||||
|
},
|
||||||
|
"minecraft:set_cursor_item": {
|
||||||
|
"protocol_id": 90
|
||||||
|
},
|
||||||
|
"minecraft:set_default_spawn_position": {
|
||||||
|
"protocol_id": 91
|
||||||
|
},
|
||||||
|
"minecraft:set_display_objective": {
|
||||||
|
"protocol_id": 92
|
||||||
|
},
|
||||||
|
"minecraft:set_entity_data": {
|
||||||
|
"protocol_id": 93
|
||||||
|
},
|
||||||
|
"minecraft:set_entity_link": {
|
||||||
|
"protocol_id": 94
|
||||||
|
},
|
||||||
|
"minecraft:set_entity_motion": {
|
||||||
|
"protocol_id": 95
|
||||||
|
},
|
||||||
|
"minecraft:set_equipment": {
|
||||||
|
"protocol_id": 96
|
||||||
|
},
|
||||||
|
"minecraft:set_experience": {
|
||||||
|
"protocol_id": 97
|
||||||
|
},
|
||||||
|
"minecraft:set_health": {
|
||||||
|
"protocol_id": 98
|
||||||
|
},
|
||||||
|
"minecraft:set_held_slot": {
|
||||||
|
"protocol_id": 99
|
||||||
|
},
|
||||||
|
"minecraft:set_objective": {
|
||||||
|
"protocol_id": 100
|
||||||
|
},
|
||||||
|
"minecraft:set_passengers": {
|
||||||
|
"protocol_id": 101
|
||||||
|
},
|
||||||
|
"minecraft:set_player_inventory": {
|
||||||
|
"protocol_id": 102
|
||||||
|
},
|
||||||
|
"minecraft:set_player_team": {
|
||||||
|
"protocol_id": 103
|
||||||
|
},
|
||||||
|
"minecraft:set_score": {
|
||||||
|
"protocol_id": 104
|
||||||
|
},
|
||||||
|
"minecraft:set_simulation_distance": {
|
||||||
|
"protocol_id": 105
|
||||||
|
},
|
||||||
|
"minecraft:set_subtitle_text": {
|
||||||
|
"protocol_id": 106
|
||||||
|
},
|
||||||
|
"minecraft:set_time": {
|
||||||
|
"protocol_id": 107
|
||||||
|
},
|
||||||
|
"minecraft:set_title_text": {
|
||||||
|
"protocol_id": 108
|
||||||
|
},
|
||||||
|
"minecraft:set_titles_animation": {
|
||||||
|
"protocol_id": 109
|
||||||
|
},
|
||||||
|
"minecraft:sound": {
|
||||||
|
"protocol_id": 111
|
||||||
|
},
|
||||||
|
"minecraft:sound_entity": {
|
||||||
|
"protocol_id": 110
|
||||||
|
},
|
||||||
|
"minecraft:start_configuration": {
|
||||||
|
"protocol_id": 112
|
||||||
|
},
|
||||||
|
"minecraft:stop_sound": {
|
||||||
|
"protocol_id": 113
|
||||||
|
},
|
||||||
|
"minecraft:store_cookie": {
|
||||||
|
"protocol_id": 114
|
||||||
|
},
|
||||||
|
"minecraft:system_chat": {
|
||||||
|
"protocol_id": 115
|
||||||
|
},
|
||||||
|
"minecraft:tab_list": {
|
||||||
|
"protocol_id": 116
|
||||||
|
},
|
||||||
|
"minecraft:tag_query": {
|
||||||
|
"protocol_id": 117
|
||||||
|
},
|
||||||
|
"minecraft:take_item_entity": {
|
||||||
|
"protocol_id": 118
|
||||||
|
},
|
||||||
|
"minecraft:teleport_entity": {
|
||||||
|
"protocol_id": 119
|
||||||
|
},
|
||||||
|
"minecraft:ticking_state": {
|
||||||
|
"protocol_id": 120
|
||||||
|
},
|
||||||
|
"minecraft:ticking_step": {
|
||||||
|
"protocol_id": 121
|
||||||
|
},
|
||||||
|
"minecraft:transfer": {
|
||||||
|
"protocol_id": 122
|
||||||
|
},
|
||||||
|
"minecraft:update_advancements": {
|
||||||
|
"protocol_id": 123
|
||||||
|
},
|
||||||
|
"minecraft:update_attributes": {
|
||||||
|
"protocol_id": 124
|
||||||
|
},
|
||||||
|
"minecraft:update_mob_effect": {
|
||||||
|
"protocol_id": 125
|
||||||
|
},
|
||||||
|
"minecraft:update_recipes": {
|
||||||
|
"protocol_id": 126
|
||||||
|
},
|
||||||
|
"minecraft:update_tags": {
|
||||||
|
"protocol_id": 127
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverbound": {
|
||||||
|
"minecraft:accept_teleportation": {
|
||||||
|
"protocol_id": 0
|
||||||
|
},
|
||||||
|
"minecraft:block_entity_tag_query": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:bundle_item_selected": {
|
||||||
|
"protocol_id": 2
|
||||||
|
},
|
||||||
|
"minecraft:change_difficulty": {
|
||||||
|
"protocol_id": 3
|
||||||
|
},
|
||||||
|
"minecraft:chat": {
|
||||||
|
"protocol_id": 7
|
||||||
|
},
|
||||||
|
"minecraft:chat_ack": {
|
||||||
|
"protocol_id": 4
|
||||||
|
},
|
||||||
|
"minecraft:chat_command": {
|
||||||
|
"protocol_id": 5
|
||||||
|
},
|
||||||
|
"minecraft:chat_command_signed": {
|
||||||
|
"protocol_id": 6
|
||||||
|
},
|
||||||
|
"minecraft:chat_session_update": {
|
||||||
|
"protocol_id": 8
|
||||||
|
},
|
||||||
|
"minecraft:chunk_batch_received": {
|
||||||
|
"protocol_id": 9
|
||||||
|
},
|
||||||
|
"minecraft:client_command": {
|
||||||
|
"protocol_id": 10
|
||||||
|
},
|
||||||
|
"minecraft:client_information": {
|
||||||
|
"protocol_id": 12
|
||||||
|
},
|
||||||
|
"minecraft:client_tick_end": {
|
||||||
|
"protocol_id": 11
|
||||||
|
},
|
||||||
|
"minecraft:command_suggestion": {
|
||||||
|
"protocol_id": 13
|
||||||
|
},
|
||||||
|
"minecraft:configuration_acknowledged": {
|
||||||
|
"protocol_id": 14
|
||||||
|
},
|
||||||
|
"minecraft:container_button_click": {
|
||||||
|
"protocol_id": 15
|
||||||
|
},
|
||||||
|
"minecraft:container_click": {
|
||||||
|
"protocol_id": 16
|
||||||
|
},
|
||||||
|
"minecraft:container_close": {
|
||||||
|
"protocol_id": 17
|
||||||
|
},
|
||||||
|
"minecraft:container_slot_state_changed": {
|
||||||
|
"protocol_id": 18
|
||||||
|
},
|
||||||
|
"minecraft:cookie_response": {
|
||||||
|
"protocol_id": 19
|
||||||
|
},
|
||||||
|
"minecraft:custom_payload": {
|
||||||
|
"protocol_id": 20
|
||||||
|
},
|
||||||
|
"minecraft:debug_sample_subscription": {
|
||||||
|
"protocol_id": 21
|
||||||
|
},
|
||||||
|
"minecraft:edit_book": {
|
||||||
|
"protocol_id": 22
|
||||||
|
},
|
||||||
|
"minecraft:entity_tag_query": {
|
||||||
|
"protocol_id": 23
|
||||||
|
},
|
||||||
|
"minecraft:interact": {
|
||||||
|
"protocol_id": 24
|
||||||
|
},
|
||||||
|
"minecraft:jigsaw_generate": {
|
||||||
|
"protocol_id": 25
|
||||||
|
},
|
||||||
|
"minecraft:keep_alive": {
|
||||||
|
"protocol_id": 26
|
||||||
|
},
|
||||||
|
"minecraft:lock_difficulty": {
|
||||||
|
"protocol_id": 27
|
||||||
|
},
|
||||||
|
"minecraft:move_player_pos": {
|
||||||
|
"protocol_id": 28
|
||||||
|
},
|
||||||
|
"minecraft:move_player_pos_rot": {
|
||||||
|
"protocol_id": 29
|
||||||
|
},
|
||||||
|
"minecraft:move_player_rot": {
|
||||||
|
"protocol_id": 30
|
||||||
|
},
|
||||||
|
"minecraft:move_player_status_only": {
|
||||||
|
"protocol_id": 31
|
||||||
|
},
|
||||||
|
"minecraft:move_vehicle": {
|
||||||
|
"protocol_id": 32
|
||||||
|
},
|
||||||
|
"minecraft:paddle_boat": {
|
||||||
|
"protocol_id": 33
|
||||||
|
},
|
||||||
|
"minecraft:pick_item_from_block": {
|
||||||
|
"protocol_id": 34
|
||||||
|
},
|
||||||
|
"minecraft:pick_item_from_entity": {
|
||||||
|
"protocol_id": 35
|
||||||
|
},
|
||||||
|
"minecraft:ping_request": {
|
||||||
|
"protocol_id": 36
|
||||||
|
},
|
||||||
|
"minecraft:place_recipe": {
|
||||||
|
"protocol_id": 37
|
||||||
|
},
|
||||||
|
"minecraft:player_abilities": {
|
||||||
|
"protocol_id": 38
|
||||||
|
},
|
||||||
|
"minecraft:player_action": {
|
||||||
|
"protocol_id": 39
|
||||||
|
},
|
||||||
|
"minecraft:player_command": {
|
||||||
|
"protocol_id": 40
|
||||||
|
},
|
||||||
|
"minecraft:player_input": {
|
||||||
|
"protocol_id": 41
|
||||||
|
},
|
||||||
|
"minecraft:player_loaded": {
|
||||||
|
"protocol_id": 42
|
||||||
|
},
|
||||||
|
"minecraft:pong": {
|
||||||
|
"protocol_id": 43
|
||||||
|
},
|
||||||
|
"minecraft:recipe_book_change_settings": {
|
||||||
|
"protocol_id": 44
|
||||||
|
},
|
||||||
|
"minecraft:recipe_book_seen_recipe": {
|
||||||
|
"protocol_id": 45
|
||||||
|
},
|
||||||
|
"minecraft:rename_item": {
|
||||||
|
"protocol_id": 46
|
||||||
|
},
|
||||||
|
"minecraft:resource_pack": {
|
||||||
|
"protocol_id": 47
|
||||||
|
},
|
||||||
|
"minecraft:seen_advancements": {
|
||||||
|
"protocol_id": 48
|
||||||
|
},
|
||||||
|
"minecraft:select_trade": {
|
||||||
|
"protocol_id": 49
|
||||||
|
},
|
||||||
|
"minecraft:set_beacon": {
|
||||||
|
"protocol_id": 50
|
||||||
|
},
|
||||||
|
"minecraft:set_carried_item": {
|
||||||
|
"protocol_id": 51
|
||||||
|
},
|
||||||
|
"minecraft:set_command_block": {
|
||||||
|
"protocol_id": 52
|
||||||
|
},
|
||||||
|
"minecraft:set_command_minecart": {
|
||||||
|
"protocol_id": 53
|
||||||
|
},
|
||||||
|
"minecraft:set_creative_mode_slot": {
|
||||||
|
"protocol_id": 54
|
||||||
|
},
|
||||||
|
"minecraft:set_jigsaw_block": {
|
||||||
|
"protocol_id": 55
|
||||||
|
},
|
||||||
|
"minecraft:set_structure_block": {
|
||||||
|
"protocol_id": 56
|
||||||
|
},
|
||||||
|
"minecraft:sign_update": {
|
||||||
|
"protocol_id": 57
|
||||||
|
},
|
||||||
|
"minecraft:swing": {
|
||||||
|
"protocol_id": 58
|
||||||
|
},
|
||||||
|
"minecraft:teleport_to_entity": {
|
||||||
|
"protocol_id": 59
|
||||||
|
},
|
||||||
|
"minecraft:use_item": {
|
||||||
|
"protocol_id": 61
|
||||||
|
},
|
||||||
|
"minecraft:use_item_on": {
|
||||||
|
"protocol_id": 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"clientbound": {
|
||||||
|
"minecraft:pong_response": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:status_response": {
|
||||||
|
"protocol_id": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverbound": {
|
||||||
|
"minecraft:ping_request": {
|
||||||
|
"protocol_id": 1
|
||||||
|
},
|
||||||
|
"minecraft:status_request": {
|
||||||
|
"protocol_id": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
potato-protocol/src/datatypes/byte_array.rs
Normal file
30
potato-protocol/src/datatypes/byte_array.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ByteArray(Vec<u8>);
|
||||||
|
|
||||||
|
impl PacketEncodable for ByteArray {
|
||||||
|
fn encode_packet(
|
||||||
|
&self,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
|
// Push all data to the buffer, length is not included.
|
||||||
|
buffer.extend_from_slice(&self.0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(
|
||||||
|
cursor: &mut std::io::Cursor<&[u8]>,
|
||||||
|
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
|
// Read all remaining data from the cursor. Length should be assumed to be the rest of the
|
||||||
|
// message.
|
||||||
|
let data = cursor
|
||||||
|
.get_ref()
|
||||||
|
.get(cursor.position() as usize..)
|
||||||
|
.ok_or(PacketDecodeError::UnexpectedEndOfPacket)?
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
Ok(ByteArray(data))
|
||||||
|
}
|
||||||
|
}
|
77
potato-protocol/src/datatypes/identifier.rs
Normal file
77
potato-protocol/src/datatypes/identifier.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
||||||
|
|
||||||
|
// The _phantom is used to stop construction of the struct outside of the new() function. Not for
|
||||||
|
// future api compatibility, but to avoid accidental instantiation of invalid identifiers.
|
||||||
|
#[allow(clippy::manual_non_exhaustive)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Identifier {
|
||||||
|
pub namespace: String,
|
||||||
|
pub path: String,
|
||||||
|
_phantom: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identifier {
|
||||||
|
pub fn new(namespace: String, path: String) -> Self {
|
||||||
|
// TODO: Validate namespace and path
|
||||||
|
Self {
|
||||||
|
namespace,
|
||||||
|
path,
|
||||||
|
_phantom: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn minecraft(path: String) -> Self {
|
||||||
|
Self::new("minecraft".to_string(), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_str(namespace: &str, path: &str) -> Self {
|
||||||
|
Self::new(namespace.to_string(), path.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn minecraft_str(path: &str) -> Self {
|
||||||
|
Self::minecraft(path.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_raw_str(raw: &str) -> Self {
|
||||||
|
let mut parts = raw.split(":");
|
||||||
|
Self::new(
|
||||||
|
parts.next().expect("Invalid identifier").to_string(),
|
||||||
|
parts.next().expect("Invalid identifier").to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for Identifier {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let val: String = self.into();
|
||||||
|
val.encode_packet(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let str = String::decode_packet(cursor)?;
|
||||||
|
let mut parts = str.split(":");
|
||||||
|
|
||||||
|
Ok(Self::new(
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
||||||
|
.to_string(),
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Identifier> for String {
|
||||||
|
fn from(value: Identifier) -> Self {
|
||||||
|
(&value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Identifier> for String {
|
||||||
|
fn from(value: &Identifier) -> Self {
|
||||||
|
format!("{}:{}", value.namespace, value.path)
|
||||||
|
}
|
||||||
|
}
|
5
potato-protocol/src/datatypes/mod.rs
Normal file
5
potato-protocol/src/datatypes/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod byte_array;
|
||||||
|
pub mod identifier;
|
||||||
|
pub mod pack;
|
||||||
|
pub mod position;
|
||||||
|
pub mod var_int;
|
8
potato-protocol/src/datatypes/pack.rs
Normal file
8
potato-protocol/src/datatypes/pack.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use potato_protocol_derive::PacketEncodable;
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub struct Pack {
|
||||||
|
pub namespace: String,
|
||||||
|
pub id: String,
|
||||||
|
pub version: String,
|
||||||
|
}
|
52
potato-protocol/src/datatypes/position.rs
Normal file
52
potato-protocol/src/datatypes/position.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use crate::packet_encodable::PacketEncodable;
|
||||||
|
|
||||||
|
/// Struct holding an integer position in the world. Values are bound as follows:
|
||||||
|
/// -33554432 <= x <= 33554431
|
||||||
|
/// -2048 <= y <= 2047
|
||||||
|
/// -33554432 <= z <= 33554431
|
||||||
|
// The _phantom is used to stop construction of the struct outside of the new() function. Not for
|
||||||
|
// future api compatibility, but to avoid accidental instantiation of invalid positions.
|
||||||
|
#[allow(clippy::manual_non_exhaustive)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Position {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i16,
|
||||||
|
pub z: i32,
|
||||||
|
_phantom: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn new(x: i32, y: i16, z: i32) -> Self {
|
||||||
|
// TODO: Validate x, y, z are in range
|
||||||
|
Self {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
_phantom: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for Position {
|
||||||
|
fn encode_packet(
|
||||||
|
&self,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
|
let stuffed: i64 = ((self.x as i64 & 0x3FFFFFF) << 38)
|
||||||
|
| ((self.z as i64 & 0x3FFFFFF) << 12)
|
||||||
|
| (self.y as i64 & 0xFFF);
|
||||||
|
stuffed.encode_packet(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(
|
||||||
|
cursor: &mut std::io::Cursor<&[u8]>,
|
||||||
|
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
|
let stuffed = i64::decode_packet(cursor)?;
|
||||||
|
Ok(Position {
|
||||||
|
x: (stuffed >> 38) as i32,
|
||||||
|
y: (stuffed << 52 >> 52) as i16,
|
||||||
|
z: (stuffed << 26 >> 38) as i32,
|
||||||
|
_phantom: (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
78
potato-protocol/src/datatypes/var_int.rs
Normal file
78
potato-protocol/src/datatypes/var_int.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable};
|
||||||
|
|
||||||
|
const SEGMENT_BITS: u8 = 0x7F;
|
||||||
|
const CONTINUE_BIT: u8 = 0x80;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VarInt(i32);
|
||||||
|
|
||||||
|
impl VarInt {
|
||||||
|
pub fn read(readable: &mut dyn Read) -> Result<VarInt, std::io::Error> {
|
||||||
|
let mut value: i32 = 0;
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut current_byte = vec![0u8; 1];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
readable.read_exact(&mut current_byte)?;
|
||||||
|
|
||||||
|
value |= ((current_byte[0] & SEGMENT_BITS) as i32) << pos;
|
||||||
|
if current_byte[0] & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VarInt(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for VarInt {
|
||||||
|
fn encode_packet(
|
||||||
|
&self,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
|
let mut value = self.0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if value & !(SEGMENT_BITS as i32) == 0 {
|
||||||
|
buffer.push(value as u8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.push(((value & (SEGMENT_BITS as i32)) | (CONTINUE_BIT as i32)) as u8);
|
||||||
|
value >>= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
VarInt::read(cursor).map_err(PacketDecodeError::IOError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarInt> for i32 {
|
||||||
|
fn from(val: VarInt) -> Self {
|
||||||
|
val.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarInt> for usize {
|
||||||
|
fn from(val: VarInt) -> Self {
|
||||||
|
val.0 as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for VarInt {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
VarInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for VarInt {
|
||||||
|
fn from(value: usize) -> Self {
|
||||||
|
VarInt(value as i32)
|
||||||
|
}
|
||||||
|
}
|
7
potato-protocol/src/lib.rs
Normal file
7
potato-protocol/src/lib.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
pub mod datatypes;
|
||||||
|
pub mod packet;
|
||||||
|
pub mod packet_encodable;
|
||||||
|
|
||||||
|
pub mod ids {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/ids.rs"));
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::clientbound::FINISH_CONFIGURATION)]
|
||||||
|
pub struct FinishConfigurationPacket;
|
8
potato-protocol/src/packet/clientbound/game_event.rs
Normal file
8
potato-protocol/src/packet/clientbound/game_event.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::clientbound::GAME_EVENT)]
|
||||||
|
pub struct GameEventPacket {
|
||||||
|
pub event: u8,
|
||||||
|
pub data: f32,
|
||||||
|
}
|
34
potato-protocol/src/packet/clientbound/login.rs
Normal file
34
potato-protocol/src/packet/clientbound/login.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
|
||||||
|
use crate::datatypes::{identifier::Identifier, position::Position, var_int::VarInt};
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::clientbound::LOGIN)]
|
||||||
|
pub struct LoginPacket {
|
||||||
|
pub entity_id: i32,
|
||||||
|
pub is_hardcore: bool,
|
||||||
|
pub dimension_names: Vec<Identifier>,
|
||||||
|
pub max_players: VarInt,
|
||||||
|
pub view_distance: VarInt,
|
||||||
|
pub simulation_distance: VarInt,
|
||||||
|
pub reduced_debug_info: bool,
|
||||||
|
pub enable_respawn_screen: bool,
|
||||||
|
pub do_limited_crafting: bool,
|
||||||
|
pub dimension_type: VarInt,
|
||||||
|
pub dimension_name: Identifier,
|
||||||
|
pub hashed_seed: i64,
|
||||||
|
pub game_mode: u8,
|
||||||
|
pub previous_game_mode: i8,
|
||||||
|
pub is_debug: bool,
|
||||||
|
pub is_flat: bool,
|
||||||
|
pub death_info: Option<DeathInfo>,
|
||||||
|
pub portal_cooldown: VarInt,
|
||||||
|
pub sea_level: VarInt,
|
||||||
|
pub enforces_secure_chat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub struct DeathInfo {
|
||||||
|
pub death_dimension: Identifier,
|
||||||
|
pub death_location: Position,
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(login_id = crate::ids::login::clientbound::LOGIN_DISCONNECT)]
|
||||||
|
pub struct LoginDisconnectPacket {
|
||||||
|
// TODO: This is a text component, maybe the type needs to reflect that
|
||||||
|
pub reason: String,
|
||||||
|
}
|
17
potato-protocol/src/packet/clientbound/login_finished.rs
Normal file
17
potato-protocol/src/packet/clientbound/login_finished.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(login_id = crate::ids::login::clientbound::LOGIN_FINISHED)]
|
||||||
|
pub struct LoginFinishedPacket {
|
||||||
|
pub uuid: Uuid,
|
||||||
|
pub username: String,
|
||||||
|
pub properties: Vec<Property>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PacketEncodable, Debug)]
|
||||||
|
pub struct Property {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
pub signature: Option<String>,
|
||||||
|
}
|
21
potato-protocol/src/packet/clientbound/mod.rs
Normal file
21
potato-protocol/src/packet/clientbound/mod.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pub mod finish_configuration;
|
||||||
|
pub mod game_event;
|
||||||
|
pub mod login;
|
||||||
|
pub mod login_disconnect;
|
||||||
|
pub mod login_finished;
|
||||||
|
pub mod pong_response;
|
||||||
|
pub mod registry_data;
|
||||||
|
pub mod select_known_packs;
|
||||||
|
pub mod set_chunk_cache_center;
|
||||||
|
pub mod status_response;
|
||||||
|
|
||||||
|
pub use finish_configuration::FinishConfigurationPacket;
|
||||||
|
pub use game_event::GameEventPacket;
|
||||||
|
pub use login::LoginPacket;
|
||||||
|
pub use login_disconnect::LoginDisconnectPacket;
|
||||||
|
pub use login_finished::LoginFinishedPacket;
|
||||||
|
pub use pong_response::PongResponsePacket;
|
||||||
|
pub use registry_data::RegistryDataPacket;
|
||||||
|
pub use select_known_packs::SelectKnownPacksPacket;
|
||||||
|
pub use set_chunk_cache_center::SetChunkCacheCenterPacket;
|
||||||
|
pub use status_response::StatusResponsePacket;
|
7
potato-protocol/src/packet/clientbound/pong_response.rs
Normal file
7
potato-protocol/src/packet/clientbound/pong_response.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(status_id = crate::ids::status::clientbound::PONG_RESPONSE)]
|
||||||
|
pub struct PongResponsePacket {
|
||||||
|
pub timestamp: i64,
|
||||||
|
}
|
129
potato-protocol/src/packet/clientbound/registry_data.rs
Normal file
129
potato-protocol/src/packet/clientbound/registry_data.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{datatypes::identifier::Identifier, packet_encodable::Nbt};
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::clientbound::REGISTRY_DATA)]
|
||||||
|
pub struct RegistryDataPacket {
|
||||||
|
pub registry_id: Identifier,
|
||||||
|
pub entries: Vec<RegistryDataEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub struct RegistryDataEntry {
|
||||||
|
pub id: Identifier,
|
||||||
|
pub data: Option<Nbt<RegistryData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum RegistryData {
|
||||||
|
DimensionType(DimensionType),
|
||||||
|
PaintingVariant(PaintingVariant),
|
||||||
|
WolfVariant(WolfVariant),
|
||||||
|
DamegeType(DamageType),
|
||||||
|
Biome(Biome),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct DimensionType {
|
||||||
|
pub fixed_time: Option<i64>,
|
||||||
|
pub has_skylight: i8,
|
||||||
|
pub has_ceiling: i8,
|
||||||
|
pub ultrawarm: i8,
|
||||||
|
pub natural: i8,
|
||||||
|
pub coordinate_scale: f64,
|
||||||
|
pub bed_works: i8,
|
||||||
|
pub respawn_anchor_works: i8,
|
||||||
|
pub min_y: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub logical_height: i32,
|
||||||
|
pub infiniburn: String, // TODO: This is a tag, create a type for these
|
||||||
|
pub effects: String, // TODO: This is an enum of 3 string variants:
|
||||||
|
// - mincecraft:overworld
|
||||||
|
// - minecraft:the_nether
|
||||||
|
// - minecraft:the_end
|
||||||
|
pub ambient_light: f32,
|
||||||
|
pub piglin_safe: i8,
|
||||||
|
pub has_raids: i8,
|
||||||
|
pub monster_spawn_light_level: i32, // TODO: This can also be a tag compound of some sort
|
||||||
|
pub monster_spawn_block_light_limit: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct PaintingVariant {
|
||||||
|
pub asset_id: String,
|
||||||
|
pub height: i32,
|
||||||
|
pub width: i32,
|
||||||
|
// TODO: Missing title and author fields
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WolfVariant {
|
||||||
|
pub wild_texture: String,
|
||||||
|
pub tame_texture: String,
|
||||||
|
pub angry_texture: String,
|
||||||
|
pub biomes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct DamageType {
|
||||||
|
pub message_id: String,
|
||||||
|
pub scaling: String,
|
||||||
|
pub exhaustion: f32,
|
||||||
|
pub effects: Option<String>,
|
||||||
|
pub death_message_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Biome {
|
||||||
|
pub has_precipitation: i8,
|
||||||
|
pub temperature: f32,
|
||||||
|
pub temperature_modifier: Option<String>,
|
||||||
|
pub downfall: f32,
|
||||||
|
pub effects: BiomeEffects,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeEffects {
|
||||||
|
pub fog_color: i32,
|
||||||
|
pub water_color: i32,
|
||||||
|
pub water_fog_color: i32,
|
||||||
|
pub sky_color: i32,
|
||||||
|
pub foliage_color: Option<i32>,
|
||||||
|
pub grass_color: Option<i32>,
|
||||||
|
pub grass_color_modifier: Option<String>,
|
||||||
|
pub particle: Option<BiomeParticle>,
|
||||||
|
pub ambient_sound: Option<String>, // TODO: Can also be a compound tag
|
||||||
|
pub mood_sound: Option<BiomeMoodSound>,
|
||||||
|
pub additions_sound: Option<BiomeAdditionsSound>,
|
||||||
|
pub music: Option<BiomeMusic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeParticle {
|
||||||
|
// TODO: Add fields
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeMoodSound {
|
||||||
|
pub sound: String,
|
||||||
|
pub tick_delay: i32,
|
||||||
|
pub block_search_extent: i32,
|
||||||
|
pub offset: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeAdditionsSound {
|
||||||
|
sound: String,
|
||||||
|
tick_chance: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeMusic {
|
||||||
|
sound: String,
|
||||||
|
min_delay: i32,
|
||||||
|
max_delay: i32,
|
||||||
|
replace_current_music: i8,
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
use crate::datatypes::pack::Pack;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::clientbound::SELECT_KNOWN_PACKS)]
|
||||||
|
pub struct SelectKnownPacksPacket {
|
||||||
|
pub packs: Vec<Pack>,
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
use crate::datatypes::var_int::VarInt;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::clientbound::SET_CHUNK_CACHE_CENTER)]
|
||||||
|
pub struct SetChunkCacheCenterPacket {
|
||||||
|
pub x: VarInt,
|
||||||
|
pub z: VarInt,
|
||||||
|
}
|
43
potato-protocol/src/packet/clientbound/status_response.rs
Normal file
43
potato-protocol/src/packet/clientbound/status_response.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::packet_encodable::Json;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(status_id = crate::ids::status::clientbound::STATUS_RESPONSE)]
|
||||||
|
pub struct StatusResponsePacket {
|
||||||
|
pub status: Json<StatusResponseData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct StatusResponseData {
|
||||||
|
pub version: Version,
|
||||||
|
pub players: Players,
|
||||||
|
pub description: Description,
|
||||||
|
pub favicon: Option<String>,
|
||||||
|
pub enforce_secure_chat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Version {
|
||||||
|
pub name: String,
|
||||||
|
pub protocol: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Players {
|
||||||
|
pub max: i32,
|
||||||
|
pub online: i32,
|
||||||
|
pub sample: Vec<Player>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Player {
|
||||||
|
pub name: String,
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Description {
|
||||||
|
pub text: String,
|
||||||
|
}
|
12
potato-protocol/src/packet/mod.rs
Normal file
12
potato-protocol/src/packet/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
pub mod clientbound;
|
||||||
|
pub mod serverbound;
|
||||||
|
|
||||||
|
use crate::packet_encodable::PacketEncodable;
|
||||||
|
|
||||||
|
pub trait Packet: PacketEncodable {
|
||||||
|
const HANDSHAKE_ID: i32;
|
||||||
|
const STATUS_ID: i32;
|
||||||
|
const LOGIN_ID: i32;
|
||||||
|
const CONFIGURATION_ID: i32;
|
||||||
|
const PLAY_ID: i32;
|
||||||
|
}
|
84
potato-protocol/src/packet/serverbound/client_information.rs
Normal file
84
potato-protocol/src/packet/serverbound/client_information.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
|
||||||
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::serverbound::CLIENT_INFORMATION)]
|
||||||
|
pub struct ClientInformationPacket {
|
||||||
|
locale: String,
|
||||||
|
view_distance: i8,
|
||||||
|
chat_mode: ChatMode,
|
||||||
|
chat_colors: bool,
|
||||||
|
displayed_skin_parts: DisplayedSkinParts,
|
||||||
|
main_hand: MainHand,
|
||||||
|
enable_text_filtering: bool,
|
||||||
|
allow_server_listings: bool,
|
||||||
|
particle_status: ParticleStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub enum ChatMode {
|
||||||
|
#[packet(id = 0)]
|
||||||
|
Enabled,
|
||||||
|
#[packet(id = 1)]
|
||||||
|
CommandsOnly,
|
||||||
|
#[packet(id = 2)]
|
||||||
|
Hidden,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub enum MainHand {
|
||||||
|
#[packet(id = 0)]
|
||||||
|
Left,
|
||||||
|
#[packet(id = 1)]
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PacketEncodable)]
|
||||||
|
pub enum ParticleStatus {
|
||||||
|
#[packet(id = 0)]
|
||||||
|
All,
|
||||||
|
#[packet(id = 1)]
|
||||||
|
Decreased,
|
||||||
|
#[packet(id = 2)]
|
||||||
|
Minimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DisplayedSkinParts {
|
||||||
|
pub cape: bool,
|
||||||
|
pub jacket: bool,
|
||||||
|
pub left_sleeve: bool,
|
||||||
|
pub right_sleeve: bool,
|
||||||
|
pub left_pants_leg: bool,
|
||||||
|
pub right_pants_leg: bool,
|
||||||
|
pub hat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for DisplayedSkinParts {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let value = (self.cape as u8)
|
||||||
|
| (self.jacket as u8) << 1
|
||||||
|
| (self.left_sleeve as u8) << 2
|
||||||
|
| (self.right_sleeve as u8) << 3
|
||||||
|
| (self.left_pants_leg as u8) << 4
|
||||||
|
| (self.right_pants_leg as u8) << 5
|
||||||
|
| (self.hat as u8) << 6;
|
||||||
|
|
||||||
|
value.encode_packet(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let value = u8::decode_packet(cursor)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
cape: value & 0x01 != 0,
|
||||||
|
jacket: value & 0x02 != 0,
|
||||||
|
left_sleeve: value & 0x04 != 0,
|
||||||
|
right_sleeve: value & 0x08 != 0,
|
||||||
|
left_pants_leg: value & 0x10 != 0,
|
||||||
|
right_pants_leg: value & 0x20 != 0,
|
||||||
|
hat: value & 0x40 != 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::serverbound::CLIENT_TICK_END)]
|
||||||
|
pub struct ClientTickEndPacket;
|
13
potato-protocol/src/packet/serverbound/custom_payload.rs
Normal file
13
potato-protocol/src/packet/serverbound/custom_payload.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
use crate::datatypes::byte_array::ByteArray;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(
|
||||||
|
configuration_id = crate::ids::configuration::serverbound::CUSTOM_PAYLOAD,
|
||||||
|
play_id = crate::ids::play::serverbound::CUSTOM_PAYLOAD
|
||||||
|
)]
|
||||||
|
pub struct CustomPayloadPacket {
|
||||||
|
pub channel: String,
|
||||||
|
pub data: ByteArray,
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::serverbound::FINISH_CONFIGURATION)]
|
||||||
|
pub struct FinishConfigurationPacket;
|
9
potato-protocol/src/packet/serverbound/hello.rs
Normal file
9
potato-protocol/src/packet/serverbound/hello.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(login_id = crate::ids::login::serverbound::HELLO)]
|
||||||
|
pub struct HelloPacket {
|
||||||
|
pub name: String,
|
||||||
|
pub uuid: Uuid,
|
||||||
|
}
|
12
potato-protocol/src/packet/serverbound/intention.rs
Normal file
12
potato-protocol/src/packet/serverbound/intention.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
use crate::datatypes::var_int::VarInt;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(handshake_id = crate::ids::handshake::serverbound::INTENTION)]
|
||||||
|
pub struct IntentionPacket {
|
||||||
|
pub protocol_version: VarInt,
|
||||||
|
pub server_address: String,
|
||||||
|
pub server_port: u16,
|
||||||
|
pub next_state: VarInt,
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(login_id = crate::ids::login::serverbound::LOGIN_ACKNOWLEDGED)]
|
||||||
|
pub struct LoginAcknowledgedPacket;
|
27
potato-protocol/src/packet/serverbound/mod.rs
Normal file
27
potato-protocol/src/packet/serverbound/mod.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
pub mod client_information;
|
||||||
|
pub mod client_tick_end;
|
||||||
|
pub mod custom_payload;
|
||||||
|
pub mod finish_configuration;
|
||||||
|
pub mod hello;
|
||||||
|
pub mod intention;
|
||||||
|
pub mod login_acknowledged;
|
||||||
|
pub mod move_player_pos;
|
||||||
|
pub mod move_player_pos_rot;
|
||||||
|
pub mod move_player_rot;
|
||||||
|
pub mod ping_request;
|
||||||
|
pub mod select_known_packs;
|
||||||
|
pub mod status_request;
|
||||||
|
|
||||||
|
pub use client_information::ClientInformationPacket;
|
||||||
|
pub use client_tick_end::ClientTickEndPacket;
|
||||||
|
pub use custom_payload::CustomPayloadPacket;
|
||||||
|
pub use finish_configuration::FinishConfigurationPacket;
|
||||||
|
pub use hello::HelloPacket;
|
||||||
|
pub use intention::IntentionPacket;
|
||||||
|
pub use login_acknowledged::LoginAcknowledgedPacket;
|
||||||
|
pub use move_player_pos::MovePlayerPosPacket;
|
||||||
|
pub use move_player_pos_rot::MovePlayerPosRotPacket;
|
||||||
|
pub use move_player_rot::MovePlayerRotPacket;
|
||||||
|
pub use ping_request::PingRequestPacket;
|
||||||
|
pub use select_known_packs::SelectKnownPacksPacket;
|
||||||
|
pub use status_request::StatusRequestPacket;
|
10
potato-protocol/src/packet/serverbound/move_player_pos.rs
Normal file
10
potato-protocol/src/packet/serverbound/move_player_pos.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::serverbound::MOVE_PLAYER_POS)]
|
||||||
|
pub struct MovePlayerPosPacket {
|
||||||
|
pub x: f64,
|
||||||
|
pub feet_y: f64,
|
||||||
|
pub z: f64,
|
||||||
|
pub flags: u8,
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::serverbound::MOVE_PLAYER_POS_ROT)]
|
||||||
|
pub struct MovePlayerPosRotPacket {
|
||||||
|
pub x: f64,
|
||||||
|
pub feet_y: f64,
|
||||||
|
pub z: f64,
|
||||||
|
pub yaw: f32,
|
||||||
|
pub pitch: f32,
|
||||||
|
pub flags: u8,
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(play_id = crate::ids::play::serverbound::MOVE_PLAYER_ROT)]
|
||||||
|
pub struct MovePlayerRotPacket {
|
||||||
|
pub yaw: f32,
|
||||||
|
pub pitch: f32,
|
||||||
|
pub flags: u8,
|
||||||
|
}
|
7
potato-protocol/src/packet/serverbound/ping_request.rs
Normal file
7
potato-protocol/src/packet/serverbound/ping_request.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(status_id = crate::ids::status::serverbound::PING_REQUEST)]
|
||||||
|
pub struct PingRequestPacket {
|
||||||
|
pub timestamp: i64,
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
use crate::datatypes::pack::Pack;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(configuration_id = crate::ids::configuration::serverbound::SELECT_KNOWN_PACKS)]
|
||||||
|
pub struct SelectKnownPacksPacket {
|
||||||
|
pub packs: Vec<Pack>,
|
||||||
|
}
|
5
potato-protocol/src/packet/serverbound/status_request.rs
Normal file
5
potato-protocol/src/packet/serverbound/status_request.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Packet, Debug)]
|
||||||
|
#[packet(status_id = crate::ids::status::serverbound::STATUS_REQUEST)]
|
||||||
|
pub struct StatusRequestPacket;
|
235
potato-protocol/src/packet_encodable/mod.rs
Normal file
235
potato-protocol/src/packet_encodable/mod.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
io::{Cursor, Read},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::datatypes::var_int::VarInt;
|
||||||
|
|
||||||
|
pub trait PacketEncodable: Sized + fmt::Debug {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError>;
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PacketDecodeError {
|
||||||
|
#[error("IO error while reading packet {0}")]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
|
#[error("Invalid UTF-8 sequence in string")]
|
||||||
|
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
||||||
|
#[error("Error while decoding JSON in packet {0}")]
|
||||||
|
JsonError(#[from] serde_json::Error),
|
||||||
|
#[error("Unexpected end of packet")]
|
||||||
|
UnexpectedEndOfPacket,
|
||||||
|
#[error("Unknown enum variant in enum {name} with value {value}")]
|
||||||
|
UnkownEnumVariant { name: &'static str, value: i32 },
|
||||||
|
#[error("Invalid identifier: {0}")]
|
||||||
|
InvalidIdentifier(String),
|
||||||
|
#[error("Error while decoding NBT in packet {0}")]
|
||||||
|
NbtError(#[from] fastnbt::error::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PacketEncodeError {
|
||||||
|
#[error("IO error while writing packet")]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
|
#[error("Error while encoding JSON in packet {0}")]
|
||||||
|
JsonError(#[from] serde_json::Error),
|
||||||
|
#[error("Invalid packet id: {0}")]
|
||||||
|
InvalidPacketId(i32),
|
||||||
|
#[error("Error while encoding NBT in packet {0}")]
|
||||||
|
NbtError(#[from] fastnbt::error::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for Uuid {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
buffer.write_u128::<BigEndian>(self.as_u128())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let data: u128 = cursor.read_u128::<BigEndian>()?;
|
||||||
|
Ok(Uuid::from_u128(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Json<T: Serialize + DeserializeOwned + fmt::Debug>(pub T);
|
||||||
|
|
||||||
|
impl<T> PacketEncodable for Json<T>
|
||||||
|
where
|
||||||
|
T: Serialize + DeserializeOwned + fmt::Debug,
|
||||||
|
{
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let json = serde_json::to_string(&self.0)?;
|
||||||
|
json.encode_packet(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let json = String::decode_packet(cursor)?;
|
||||||
|
Ok(Self(serde_json::from_str(&json)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Nbt<T>(pub T)
|
||||||
|
where
|
||||||
|
T: Serialize + DeserializeOwned + fmt::Debug;
|
||||||
|
|
||||||
|
impl<T> PacketEncodable for Nbt<T>
|
||||||
|
where
|
||||||
|
T: Serialize + DeserializeOwned + fmt::Debug,
|
||||||
|
{
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
fastnbt::to_writer_with_opts(buffer, &self.0, fastnbt::SerOpts::network_nbt())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let value = fastnbt::from_bytes(cursor.get_ref())?;
|
||||||
|
|
||||||
|
Ok(Nbt(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PacketEncodable for Option<T>
|
||||||
|
where
|
||||||
|
T: PacketEncodable,
|
||||||
|
{
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let present = self.is_some();
|
||||||
|
present.encode_packet(buffer)?;
|
||||||
|
if let Some(value) = self {
|
||||||
|
value.encode_packet(buffer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let present = bool::decode_packet(cursor)?;
|
||||||
|
|
||||||
|
if present {
|
||||||
|
Ok(Some(T::decode_packet(cursor)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PacketEncodable for Vec<T>
|
||||||
|
where
|
||||||
|
T: PacketEncodable,
|
||||||
|
{
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let length: VarInt = self.len().into();
|
||||||
|
length.encode_packet(buffer)?;
|
||||||
|
|
||||||
|
for item in self {
|
||||||
|
item.encode_packet(buffer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let length: usize = VarInt::decode_packet(cursor)?.into();
|
||||||
|
let mut vec = Vec::with_capacity(length);
|
||||||
|
for _ in 0..length {
|
||||||
|
vec.push(T::decode_packet(cursor)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for String {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
let lenght: VarInt = self.len().into();
|
||||||
|
lenght.encode_packet(buffer)?;
|
||||||
|
|
||||||
|
buffer.extend_from_slice(self.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let lenght: usize = VarInt::decode_packet(cursor)?.into();
|
||||||
|
let mut buffer = vec![0; lenght];
|
||||||
|
cursor.read_exact(&mut buffer)?;
|
||||||
|
let value = String::from_utf8(buffer)?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for bool {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
buffer.push(if *self { 1 } else { 0 });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let v = cursor.read_u8()?;
|
||||||
|
Ok(v != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for i8 {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
buffer.write_i8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let value = cursor.read_i8()?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for u8 {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
buffer.write_u8(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let value = cursor.read_u8()?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! number_impl {
|
||||||
|
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
impl PacketEncodable for $type {
|
||||||
|
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
||||||
|
buffer.$write_fn::<BigEndian>(*self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
||||||
|
let value = cursor.$read_fn::<BigEndian>()?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
number_impl! {
|
||||||
|
u16, read_u16, write_u16,
|
||||||
|
u32, read_u32, write_u32,
|
||||||
|
u64, read_u64, write_u64,
|
||||||
|
|
||||||
|
i16, read_i16, write_i16,
|
||||||
|
i32, read_i32, write_i32,
|
||||||
|
i64, read_i64, write_i64,
|
||||||
|
|
||||||
|
f32, read_f32, write_f32,
|
||||||
|
f64, read_f64, write_f64,
|
||||||
|
}
|
12
potato/Cargo.toml
Normal file
12
potato/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "potato"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
potato-protocol = { path = "../potato-protocol" }
|
||||||
|
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
uuid.workspace = true
|
544
potato/src/main.rs
Normal file
544
potato/src/main.rs
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Debug,
|
||||||
|
fs::read_to_string,
|
||||||
|
io::{Cursor, Read, Write},
|
||||||
|
net::{Shutdown, SocketAddr, TcpListener, TcpStream},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
use potato_protocol::{
|
||||||
|
datatypes::{identifier::Identifier, pack::Pack, var_int::VarInt},
|
||||||
|
packet::{
|
||||||
|
Packet,
|
||||||
|
clientbound::{
|
||||||
|
self, GameEventPacket, SetChunkCacheCenterPacket,
|
||||||
|
registry_data::{
|
||||||
|
Biome, BiomeEffects, DamageType, DimensionType, PaintingVariant, RegistryData,
|
||||||
|
RegistryDataEntry, WolfVariant,
|
||||||
|
},
|
||||||
|
status_response,
|
||||||
|
},
|
||||||
|
serverbound,
|
||||||
|
},
|
||||||
|
packet_encodable::{Json, Nbt, PacketDecodeError, PacketEncodable, PacketEncodeError},
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Looking for datapacks...");
|
||||||
|
let datapacks: Vec<PathBuf> = std::fs::read_dir("run/datapacks")
|
||||||
|
.expect("Failed to read datapacks directory")
|
||||||
|
.map(|f| f.unwrap().path())
|
||||||
|
.filter(|p| p.is_dir())
|
||||||
|
.collect();
|
||||||
|
println!("Found {} datapacks", datapacks.len());
|
||||||
|
println!("Loading datapacks...");
|
||||||
|
let mut damage_types = HashMap::new();
|
||||||
|
for datapack in datapacks {
|
||||||
|
let datapack_name = datapack.file_name().unwrap().to_str().unwrap();
|
||||||
|
println!("Loading datapack: {}", datapack_name);
|
||||||
|
let path = datapack.join("data/minecraft/damage_type");
|
||||||
|
if let Ok(files) = std::fs::read_dir(path) {
|
||||||
|
for file in files {
|
||||||
|
let file = file.unwrap().path();
|
||||||
|
let name = file.file_stem().unwrap().to_str().unwrap().to_owned();
|
||||||
|
let data: DamageType = serde_json::from_str(&read_to_string(file).unwrap())
|
||||||
|
.expect("Failed to parse damage type");
|
||||||
|
|
||||||
|
damage_types.insert(Identifier::minecraft(name), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Finished loading datapacks");
|
||||||
|
println!("Loaded {} damage types", damage_types.len());
|
||||||
|
|
||||||
|
let registries = Arc::new(Registries { damage_types });
|
||||||
|
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:25565").unwrap();
|
||||||
|
println!("listening started, ready to accept");
|
||||||
|
for stream in listener.incoming() {
|
||||||
|
let registries = registries.clone();
|
||||||
|
match stream {
|
||||||
|
Err(e) => println!("Accept ERROR: {}", e),
|
||||||
|
// Explicit drop because of the diverging branches
|
||||||
|
Ok(stream) => drop(thread::spawn(|| handle_client(stream, registries))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_client(stream: TcpStream, registries: Arc<Registries>) {
|
||||||
|
if let Ok(mut client) = ClientConnection::new(stream, registries) {
|
||||||
|
loop {
|
||||||
|
match client.read_packet() {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("[{}] Error while handling packets: {}", client.address, e);
|
||||||
|
// Make sure the connection is closed.
|
||||||
|
let _ = client.stream.shutdown(Shutdown::Both);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum ConnectionState {
|
||||||
|
Handshaking,
|
||||||
|
Status,
|
||||||
|
Login,
|
||||||
|
Configuration,
|
||||||
|
Play,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClientConnection {
|
||||||
|
stream: TcpStream,
|
||||||
|
address: SocketAddr,
|
||||||
|
state: ConnectionState,
|
||||||
|
recv_buffer: Vec<u8>,
|
||||||
|
|
||||||
|
registries: Arc<Registries>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Registries {
|
||||||
|
damage_types: HashMap<Identifier, DamageType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max packet size is 2MB in vanilla
|
||||||
|
const RECV_BUFFER_SIZE: usize = 1024 * 1024 * 2;
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
enum ConnectionError {
|
||||||
|
#[error("IO error while reading packet: {0}")]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
|
#[error("Error while decoding packet: {0}")]
|
||||||
|
DecodeError(#[from] PacketDecodeError),
|
||||||
|
#[error("Error while encoding packet: {0}")]
|
||||||
|
EncodeError(#[from] PacketEncodeError),
|
||||||
|
#[error("Client provided invalid next state")]
|
||||||
|
InvalidNextState,
|
||||||
|
#[error("Finished")]
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Need to start sending keep alive packets once we enter configuration phase
|
||||||
|
impl ClientConnection {
|
||||||
|
fn new(
|
||||||
|
stream: TcpStream,
|
||||||
|
registries: Arc<Registries>,
|
||||||
|
) -> Result<ClientConnection, std::io::Error> {
|
||||||
|
let address = stream.peer_addr()?;
|
||||||
|
|
||||||
|
Ok(ClientConnection {
|
||||||
|
state: ConnectionState::Handshaking,
|
||||||
|
address,
|
||||||
|
stream,
|
||||||
|
recv_buffer: vec![0; RECV_BUFFER_SIZE],
|
||||||
|
registries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_packet(&mut self) -> Result<(), ConnectionError> {
|
||||||
|
let packet_size: usize = VarInt::read(&mut self.stream)?.into();
|
||||||
|
|
||||||
|
self.stream
|
||||||
|
.read_exact(&mut self.recv_buffer[..packet_size])?;
|
||||||
|
|
||||||
|
self.handle_packet(packet_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_packet<T: Packet>(&mut self, packet: &T) -> Result<(), ConnectionError> {
|
||||||
|
println!("[{} ({:?})] -> {:?}", self.address, self.state, packet);
|
||||||
|
let buffer = &mut Vec::new();
|
||||||
|
// Encode ID
|
||||||
|
let packet_id: i32 = match self.state {
|
||||||
|
ConnectionState::Handshaking => T::HANDSHAKE_ID,
|
||||||
|
ConnectionState::Status => T::STATUS_ID,
|
||||||
|
ConnectionState::Login => T::LOGIN_ID,
|
||||||
|
ConnectionState::Configuration => T::CONFIGURATION_ID,
|
||||||
|
ConnectionState::Play => T::PLAY_ID,
|
||||||
|
};
|
||||||
|
if packet_id == -1 {
|
||||||
|
return Err(ConnectionError::EncodeError(
|
||||||
|
PacketEncodeError::InvalidPacketId(packet_id),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let packet_id: VarInt = packet_id.into();
|
||||||
|
|
||||||
|
packet_id.encode_packet(buffer)?;
|
||||||
|
// Encode packet
|
||||||
|
packet.encode_packet(buffer)?;
|
||||||
|
// Encode length
|
||||||
|
let length_buffer = &mut Vec::new();
|
||||||
|
let length: VarInt = buffer.len().into();
|
||||||
|
length.encode_packet(length_buffer)?;
|
||||||
|
|
||||||
|
// Print raw packet bytes
|
||||||
|
// println!(
|
||||||
|
// "{:X?} ({}) {:X?} ({})",
|
||||||
|
// length_buffer,
|
||||||
|
// length_buffer.len(),
|
||||||
|
// buffer,
|
||||||
|
// buffer.len()
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Send packet
|
||||||
|
self.stream.write_all(length_buffer)?;
|
||||||
|
self.stream.write_all(buffer)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_packet(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
match self.state {
|
||||||
|
ConnectionState::Handshaking => {
|
||||||
|
self.handle_handshaking(length)?;
|
||||||
|
}
|
||||||
|
ConnectionState::Status => {
|
||||||
|
self.handle_status(length)?;
|
||||||
|
}
|
||||||
|
ConnectionState::Login => {
|
||||||
|
self.handle_login(length)?;
|
||||||
|
}
|
||||||
|
ConnectionState::Configuration => {
|
||||||
|
self.handle_configuration(length)?;
|
||||||
|
}
|
||||||
|
ConnectionState::Play => {
|
||||||
|
self.handle_play(length)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_handshaking(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
let cursor = &mut Cursor::new(&self.recv_buffer[..length]);
|
||||||
|
let id = VarInt::decode_packet(cursor)?.into();
|
||||||
|
|
||||||
|
match id {
|
||||||
|
serverbound::IntentionPacket::HANDSHAKE_ID => {
|
||||||
|
let packet = serverbound::IntentionPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Handshaking)] <- {:?}", self.address, packet,);
|
||||||
|
self.change_state_num(packet.next_state.into())?
|
||||||
|
}
|
||||||
|
// TODO: Legacy server list ping
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Handshaking)] <- Unknown packet id: {} with length {} data: {:X?}",
|
||||||
|
self.address,
|
||||||
|
id,
|
||||||
|
length,
|
||||||
|
&self.recv_buffer[..length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_status(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
let cursor = &mut Cursor::new(&self.recv_buffer[..length]);
|
||||||
|
let id: i32 = VarInt::decode_packet(cursor)?.into();
|
||||||
|
|
||||||
|
match id {
|
||||||
|
serverbound::StatusRequestPacket::STATUS_ID => {
|
||||||
|
let packet = serverbound::StatusRequestPacket;
|
||||||
|
println!("[{} (Status)] <- {:?}", self.address, packet);
|
||||||
|
self.send_packet(&clientbound::StatusResponsePacket {
|
||||||
|
status: Json(status_response::StatusResponseData {
|
||||||
|
version: status_response::Version {
|
||||||
|
name: "Rust 1.21.4".to_owned(),
|
||||||
|
protocol: 769,
|
||||||
|
},
|
||||||
|
players: status_response::Players {
|
||||||
|
max: 500,
|
||||||
|
online: 0,
|
||||||
|
sample: vec![],
|
||||||
|
},
|
||||||
|
description: status_response::Description {
|
||||||
|
text: "A rust server!".to_owned(),
|
||||||
|
},
|
||||||
|
favicon: None,
|
||||||
|
enforce_secure_chat: false,
|
||||||
|
}),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
serverbound::PingRequestPacket::STATUS_ID => {
|
||||||
|
let packet = serverbound::PingRequestPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Status)] <- {:?}", self.address, packet);
|
||||||
|
self.send_packet(&clientbound::PongResponsePacket {
|
||||||
|
timestamp: packet.timestamp,
|
||||||
|
})?;
|
||||||
|
self.stream.shutdown(Shutdown::Both)?;
|
||||||
|
return Err(ConnectionError::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Status)] <- Unknown packet id: {} with length {} data: {:X?}",
|
||||||
|
self.address,
|
||||||
|
id,
|
||||||
|
length,
|
||||||
|
&self.recv_buffer[..length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_login(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
let cursor = &mut Cursor::new(&self.recv_buffer[..length]);
|
||||||
|
let id: i32 = VarInt::decode_packet(cursor)?.into();
|
||||||
|
|
||||||
|
match id {
|
||||||
|
serverbound::HelloPacket::LOGIN_ID => {
|
||||||
|
let packet = serverbound::HelloPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Login)] <- {:?}", self.address, packet);
|
||||||
|
self.send_packet(&clientbound::LoginFinishedPacket {
|
||||||
|
uuid: packet.uuid,
|
||||||
|
username: packet.name,
|
||||||
|
properties: vec![],
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
serverbound::LoginAcknowledgedPacket::LOGIN_ID => {
|
||||||
|
let packet = serverbound::LoginAcknowledgedPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Login)] <- {:?}", self.address, packet);
|
||||||
|
self.change_state(ConnectionState::Configuration)?;
|
||||||
|
self.send_packet(&clientbound::SelectKnownPacksPacket {
|
||||||
|
packs: vec![Pack {
|
||||||
|
namespace: "minecraft".to_owned(),
|
||||||
|
id: "core".to_owned(),
|
||||||
|
version: "1.21.4".to_owned(),
|
||||||
|
}],
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Login)] <- Unknown packet id: {} with length {} data: {:X?}",
|
||||||
|
self.address,
|
||||||
|
id,
|
||||||
|
length,
|
||||||
|
&self.recv_buffer[..length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_configuration(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
let cursor = &mut Cursor::new(&self.recv_buffer[..length]);
|
||||||
|
let id: i32 = VarInt::decode_packet(cursor)?.into();
|
||||||
|
|
||||||
|
match id {
|
||||||
|
serverbound::ClientInformationPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::ClientInformationPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::CustomPayloadPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::CustomPayloadPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::SelectKnownPacksPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::SelectKnownPacksPacket::decode_packet(cursor)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("dimension_type"),
|
||||||
|
entries: vec![RegistryDataEntry {
|
||||||
|
id: Identifier::minecraft_str("overworld"),
|
||||||
|
data: Some(Nbt(RegistryData::DimensionType(DimensionType {
|
||||||
|
fixed_time: None,
|
||||||
|
has_skylight: 1,
|
||||||
|
has_ceiling: 0,
|
||||||
|
ultrawarm: 0,
|
||||||
|
natural: 1,
|
||||||
|
coordinate_scale: 1.,
|
||||||
|
bed_works: 1,
|
||||||
|
respawn_anchor_works: 0,
|
||||||
|
min_y: 0,
|
||||||
|
height: 256,
|
||||||
|
logical_height: 256,
|
||||||
|
infiniburn: "#minecraft:infiniburn_overworld".to_owned(),
|
||||||
|
effects: "minecraft:overworld".to_owned(),
|
||||||
|
ambient_light: 0.,
|
||||||
|
piglin_safe: 1,
|
||||||
|
has_raids: 1,
|
||||||
|
monster_spawn_light_level: 0,
|
||||||
|
monster_spawn_block_light_limit: 0,
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("painting_variant"),
|
||||||
|
entries: vec![RegistryDataEntry {
|
||||||
|
id: Identifier::minecraft_str("backyard"),
|
||||||
|
data: Some(Nbt(RegistryData::PaintingVariant(PaintingVariant {
|
||||||
|
asset_id: "minecraft:backyard".to_owned(),
|
||||||
|
height: 2,
|
||||||
|
width: 2,
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("wolf_variant"),
|
||||||
|
entries: vec![RegistryDataEntry {
|
||||||
|
id: Identifier::minecraft_str("ashen"),
|
||||||
|
data: Some(Nbt(RegistryData::WolfVariant(WolfVariant {
|
||||||
|
wild_texture: "minecraft:entity/wolf/wolf_ashen".to_owned(),
|
||||||
|
tame_texture: "minecraft:entity/wolf/wolf_ashen_tame".to_owned(),
|
||||||
|
angry_texture: "minecraft:entity/wolf/wolf_ashen_angry".to_owned(),
|
||||||
|
biomes: vec![],
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let damage_type_registry_entries = self
|
||||||
|
.registries
|
||||||
|
.damage_types
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| RegistryDataEntry {
|
||||||
|
id: key.clone(),
|
||||||
|
data: Some(Nbt(RegistryData::DamegeType(value.clone()))),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("damage_type"),
|
||||||
|
entries: damage_type_registry_entries,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("worldgen/biome"),
|
||||||
|
entries: vec![RegistryDataEntry {
|
||||||
|
id: Identifier::minecraft_str("plains"),
|
||||||
|
data: Some(Nbt(RegistryData::Biome(Biome {
|
||||||
|
has_precipitation: 0,
|
||||||
|
temperature: 0.,
|
||||||
|
temperature_modifier: None,
|
||||||
|
downfall: 0.,
|
||||||
|
effects: BiomeEffects {
|
||||||
|
fog_color: 0,
|
||||||
|
water_color: 0,
|
||||||
|
water_fog_color: 0,
|
||||||
|
sky_color: 0,
|
||||||
|
foliage_color: None,
|
||||||
|
grass_color: None,
|
||||||
|
grass_color_modifier: None,
|
||||||
|
particle: None,
|
||||||
|
ambient_sound: None,
|
||||||
|
mood_sound: None,
|
||||||
|
additions_sound: None,
|
||||||
|
music: None,
|
||||||
|
},
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::FinishConfigurationPacket)?;
|
||||||
|
}
|
||||||
|
serverbound::FinishConfigurationPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::finish_configuration::FinishConfigurationPacket;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
self.change_state(ConnectionState::Play)?;
|
||||||
|
self.send_packet(&clientbound::LoginPacket {
|
||||||
|
entity_id: 0,
|
||||||
|
is_hardcore: false,
|
||||||
|
dimension_names: vec![],
|
||||||
|
max_players: 500.into(),
|
||||||
|
view_distance: 10.into(),
|
||||||
|
simulation_distance: 10.into(),
|
||||||
|
reduced_debug_info: false,
|
||||||
|
enable_respawn_screen: true,
|
||||||
|
do_limited_crafting: false,
|
||||||
|
dimension_type: 0.into(),
|
||||||
|
dimension_name: Identifier::minecraft("overworld".to_owned()),
|
||||||
|
hashed_seed: 0,
|
||||||
|
game_mode: 1,
|
||||||
|
previous_game_mode: -1,
|
||||||
|
is_debug: false,
|
||||||
|
is_flat: false,
|
||||||
|
death_info: None,
|
||||||
|
portal_cooldown: 0.into(),
|
||||||
|
sea_level: 63.into(),
|
||||||
|
enforces_secure_chat: false,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&GameEventPacket {
|
||||||
|
event: 13,
|
||||||
|
data: 0.,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.send_packet(&SetChunkCacheCenterPacket {
|
||||||
|
x: 0.into(),
|
||||||
|
z: 0.into(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Configuration)] <- Unknown packet id: {} with length {} data: {:X?}",
|
||||||
|
self.address,
|
||||||
|
id,
|
||||||
|
length,
|
||||||
|
&self.recv_buffer[..length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_play(&mut self, length: usize) -> Result<(), ConnectionError> {
|
||||||
|
let cursor = &mut Cursor::new(&self.recv_buffer[..length]);
|
||||||
|
let id: i32 = VarInt::decode_packet(cursor)?.into();
|
||||||
|
|
||||||
|
match id {
|
||||||
|
serverbound::ClientTickEndPacket::PLAY_ID => {
|
||||||
|
serverbound::ClientTickEndPacket::decode_packet(cursor)?;
|
||||||
|
}
|
||||||
|
serverbound::MovePlayerPosPacket::PLAY_ID => {
|
||||||
|
let _packet = serverbound::MovePlayerPosPacket::decode_packet(cursor)?;
|
||||||
|
// println!("[{} (Play)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::MovePlayerPosRotPacket::PLAY_ID => {
|
||||||
|
let _packet = serverbound::MovePlayerPosRotPacket::decode_packet(cursor)?;
|
||||||
|
// println!("[{} (Play)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::MovePlayerRotPacket::PLAY_ID => {
|
||||||
|
let _packet = serverbound::MovePlayerRotPacket::decode_packet(cursor)?;
|
||||||
|
// println!("[{} (Play)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Play)] <- Unknown packet id: {} with length {} data: {:X?}",
|
||||||
|
self.address,
|
||||||
|
id,
|
||||||
|
length,
|
||||||
|
&self.recv_buffer[..length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_state_num(&mut self, state: i32) -> Result<(), ConnectionError> {
|
||||||
|
let new_state = match state {
|
||||||
|
1 => ConnectionState::Status,
|
||||||
|
2 => ConnectionState::Login,
|
||||||
|
_ => return Err(ConnectionError::InvalidNextState),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.change_state(new_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_state(&mut self, new_state: ConnectionState) -> Result<(), ConnectionError> {
|
||||||
|
println!("[{}] {:?} -> {:?}", self.address, self.state, new_state);
|
||||||
|
self.state = new_state;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue