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