Use async
This commit is contained in:
parent
e06c4882a0
commit
d133126f7f
23 changed files with 1309 additions and 661 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
/target/
|
/target/
|
||||||
.direnv/
|
.direnv/
|
||||||
.devenv/
|
.devenv/
|
||||||
|
/run/
|
||||||
|
|
292
Cargo.lock
generated
292
Cargo.lock
generated
|
@ -2,18 +2,72 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.74"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cesu8"
|
name = "cesu8"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.10"
|
version = "0.20.10"
|
||||||
|
@ -66,6 +120,12 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.31.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -78,20 +138,96 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.170"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.36.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potato"
|
name = "potato"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
"potato-protocol",
|
"potato-protocol",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -100,11 +236,13 @@ name = "potato-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
"fastnbt",
|
"fastnbt",
|
||||||
"potato-protocol-derive",
|
"potato-protocol-derive",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -136,12 +274,33 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.218"
|
version = "1.0.218"
|
||||||
|
@ -183,6 +342,31 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -220,6 +404,35 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.43.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
@ -231,3 +444,82 @@ name = "uuid"
|
||||||
version = "1.15.1"
|
version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
|
@ -11,3 +11,5 @@ serde = { version = "1.0.218", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
uuid = "1.15.1"
|
uuid = "1.15.1"
|
||||||
|
bytes = "1"
|
||||||
|
tokio = { version = "1.43.0", features = ["full"] }
|
||||||
|
|
|
@ -122,16 +122,16 @@ fn generate_packet_encodable_enum_impl(
|
||||||
.collect();
|
.collect();
|
||||||
quote! {
|
quote! {
|
||||||
impl crate::packet_encodable::PacketEncodable for #ident {
|
impl crate::packet_encodable::PacketEncodable for #ident {
|
||||||
fn encode_packet(&self, cursor: &mut Vec<u8>) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl bytes::BufMut) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
let value: crate::datatypes::var_int::VarInt = match self {
|
let value: crate::datatypes::var_int::VarInt = match self {
|
||||||
#(#encode_arms)*
|
#(#encode_arms)*
|
||||||
}.into();
|
}.into();
|
||||||
|
|
||||||
value.encode_packet(cursor)
|
value.encode_packet(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
fn decode_packet(buf: &mut impl bytes::Buf) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
let value: i32 = crate::datatypes::var_int::VarInt::decode_packet(cursor)?.into();
|
let value: i32 = crate::datatypes::var_int::VarInt::decode_packet(buf)?.into();
|
||||||
match value {
|
match value {
|
||||||
#(#decode_arms)*
|
#(#decode_arms)*
|
||||||
_ => Err(crate::packet_encodable::PacketDecodeError::UnkownEnumVariant {
|
_ => Err(crate::packet_encodable::PacketDecodeError::UnkownEnumVariant {
|
||||||
|
@ -160,12 +160,12 @@ fn generate_packet_encodable_struct_impl(
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl crate::packet_encodable::PacketEncodable for #ident {
|
impl crate::packet_encodable::PacketEncodable for #ident {
|
||||||
fn encode_packet(&self, cursor: &mut Vec<u8>) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl bytes::BufMut) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
#(#fields_encode_packet_calls)*
|
#(#fields_encode_packet_calls)*
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
fn decode_packet(buf: &mut impl bytes::Buf) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
#(#fields_decode_packet_calls)*
|
#(#fields_decode_packet_calls)*
|
||||||
})
|
})
|
||||||
|
@ -179,7 +179,7 @@ fn generate_struct_field_decode_packet_calls(field: &syn::Field) -> proc_macro2:
|
||||||
let field_ty = &field.ty;
|
let field_ty = &field.ty;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#field_ident: <#field_ty>::decode_packet(cursor)?,
|
#field_ident: <#field_ty>::decode_packet(buf)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ fn generate_struct_field_encode_packet_calls(field: &syn::Field) -> proc_macro2:
|
||||||
let field_ident = &field.ident;
|
let field_ident = &field.ident;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
self.#field_ident.encode_packet(cursor)?;
|
self.#field_ident.encode_packet(buf)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
bytes.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::packet_encodable::{PacketDecodeError, PacketEncodable};
|
use bytes::{Buf, BufMut};
|
||||||
|
|
||||||
|
use crate::packet_encodable::PacketEncodable;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ByteArray(Vec<u8>);
|
pub struct ByteArray(Vec<u8>);
|
||||||
|
@ -6,25 +8,22 @@ pub struct ByteArray(Vec<u8>);
|
||||||
impl PacketEncodable for ByteArray {
|
impl PacketEncodable for ByteArray {
|
||||||
fn encode_packet(
|
fn encode_packet(
|
||||||
&self,
|
&self,
|
||||||
buffer: &mut Vec<u8>,
|
buf: &mut impl BufMut,
|
||||||
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
// Push all data to the buffer, length is not included.
|
// Push all data to the buffer, length is not included.
|
||||||
buffer.extend_from_slice(&self.0);
|
buf.put_slice(&self.0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(
|
fn decode_packet(
|
||||||
cursor: &mut std::io::Cursor<&[u8]>,
|
buf: &mut impl Buf,
|
||||||
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
// Read all remaining data from the cursor. Length should be assumed to be the rest of the
|
// Read all remaining data from the cursor. Length should be assumed to be the rest of the
|
||||||
// message.
|
// message.
|
||||||
let data = cursor
|
let mut dst = Vec::with_capacity(buf.remaining());
|
||||||
.get_ref()
|
buf.copy_to_slice(&mut dst);
|
||||||
.get(cursor.position() as usize..)
|
|
||||||
.ok_or(PacketDecodeError::UnexpectedEndOfPacket)?
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
Ok(ByteArray(data))
|
Ok(ByteArray(dst))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use bytes::{Buf, BufMut};
|
||||||
|
|
||||||
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
||||||
|
|
||||||
// The _phantom is used to stop construction of the struct outside of the new() function. Not for
|
// The _phantom is used to stop construction of the struct outside of the new() function. Not for
|
||||||
|
@ -42,13 +44,13 @@ impl Identifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for Identifier {
|
impl PacketEncodable for Identifier {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let val: String = self.into();
|
let val: String = self.into();
|
||||||
val.encode_packet(buffer)
|
val.encode_packet(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let str = String::decode_packet(cursor)?;
|
let str = String::decode_packet(buf)?;
|
||||||
let mut parts = str.split(":");
|
let mut parts = str.split(":");
|
||||||
|
|
||||||
Ok(Self::new(
|
Ok(Self::new(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use bytes::{Buf, BufMut};
|
||||||
|
|
||||||
use crate::packet_encodable::PacketEncodable;
|
use crate::packet_encodable::PacketEncodable;
|
||||||
|
|
||||||
/// Struct holding an integer position in the world. Values are bound as follows:
|
/// Struct holding an integer position in the world. Values are bound as follows:
|
||||||
|
@ -30,18 +32,18 @@ impl Position {
|
||||||
impl PacketEncodable for Position {
|
impl PacketEncodable for Position {
|
||||||
fn encode_packet(
|
fn encode_packet(
|
||||||
&self,
|
&self,
|
||||||
buffer: &mut Vec<u8>,
|
buf: &mut impl BufMut,
|
||||||
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
let stuffed: i64 = ((self.x as i64 & 0x3FFFFFF) << 38)
|
let stuffed: i64 = ((self.x as i64 & 0x3FFFFFF) << 38)
|
||||||
| ((self.z as i64 & 0x3FFFFFF) << 12)
|
| ((self.z as i64 & 0x3FFFFFF) << 12)
|
||||||
| (self.y as i64 & 0xFFF);
|
| (self.y as i64 & 0xFFF);
|
||||||
stuffed.encode_packet(buffer)
|
stuffed.encode_packet(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(
|
fn decode_packet(
|
||||||
cursor: &mut std::io::Cursor<&[u8]>,
|
buf: &mut impl Buf,
|
||||||
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
) -> Result<Self, crate::packet_encodable::PacketDecodeError> {
|
||||||
let stuffed = i64::decode_packet(cursor)?;
|
let stuffed = i64::decode_packet(buf)?;
|
||||||
Ok(Position {
|
Ok(Position {
|
||||||
x: (stuffed >> 38) as i32,
|
x: (stuffed >> 38) as i32,
|
||||||
y: (stuffed << 52 >> 52) as i16,
|
y: (stuffed << 52 >> 52) as i16,
|
||||||
|
|
|
@ -1,55 +1,86 @@
|
||||||
use std::io::Read;
|
use bytes::{Buf, BufMut};
|
||||||
|
use tokio::{io::AsyncWriteExt, net::tcp::OwnedWriteHalf};
|
||||||
|
|
||||||
use crate::packet_encodable::{PacketDecodeError, PacketEncodable};
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable};
|
||||||
|
|
||||||
const SEGMENT_BITS: u8 = 0x7F;
|
const SEGMENT_BITS: u8 = 0x7F;
|
||||||
const CONTINUE_BIT: u8 = 0x80;
|
const CONTINUE_BIT: u8 = 0x80;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct VarInt(i32);
|
pub struct VarInt(i32);
|
||||||
|
|
||||||
impl VarInt {
|
impl VarInt {
|
||||||
pub fn read(readable: &mut dyn Read) -> Result<VarInt, std::io::Error> {
|
pub fn encoded_len(&self) -> usize {
|
||||||
let mut value: i32 = 0;
|
match self.0 {
|
||||||
let mut pos = 0;
|
0 => 1,
|
||||||
let mut current_byte = vec![0u8; 1];
|
n => (31 - n.leading_zeros() as usize) / 7 + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_to_stream(&self, stream: &mut OwnedWriteHalf) -> Result<(), std::io::Error> {
|
||||||
|
let mut value = self.0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
readable.read_exact(&mut current_byte)?;
|
if value & !(SEGMENT_BITS as i32) == 0 {
|
||||||
|
stream.write_u8(value as u8).await?;
|
||||||
value |= ((current_byte[0] & SEGMENT_BITS) as i32) << pos;
|
|
||||||
if current_byte[0] & 0x80 == 0 {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos += 7;
|
|
||||||
|
stream
|
||||||
|
.write_u8(((value & (SEGMENT_BITS as i32)) | (CONTINUE_BIT as i32)) as u8)
|
||||||
|
.await?;
|
||||||
|
value >>= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(VarInt(value))
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for VarInt {
|
impl PacketEncodable for VarInt {
|
||||||
fn encode_packet(
|
fn encode_packet(
|
||||||
&self,
|
&self,
|
||||||
buffer: &mut Vec<u8>,
|
buf: &mut impl BufMut,
|
||||||
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
) -> Result<(), crate::packet_encodable::PacketEncodeError> {
|
||||||
let mut value = self.0;
|
let mut value = self.0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if value & !(SEGMENT_BITS as i32) == 0 {
|
if value & !(SEGMENT_BITS as i32) == 0 {
|
||||||
buffer.push(value as u8);
|
buf.put_u8(value as u8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.push(((value & (SEGMENT_BITS as i32)) | (CONTINUE_BIT as i32)) as u8);
|
buf.put_u8(((value & (SEGMENT_BITS as i32)) | (CONTINUE_BIT as i32)) as u8);
|
||||||
value >>= 7;
|
value >>= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
VarInt::read(cursor).map_err(PacketDecodeError::IOError)
|
let mut value: i32 = 0;
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if !buf.has_remaining() {
|
||||||
|
// The data is incomplete
|
||||||
|
return Err(PacketDecodeError::Incomplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_byte = buf.get_u8();
|
||||||
|
|
||||||
|
value |= ((current_byte & SEGMENT_BITS) as i32) << pos;
|
||||||
|
if current_byte & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 7;
|
||||||
|
|
||||||
|
if pos > 32 {
|
||||||
|
// The value is too large
|
||||||
|
return Err(PacketDecodeError::InvalidVarInt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VarInt(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
potato-protocol/src/packet/clientbound/keep_alive.rs
Normal file
10
potato-protocol/src/packet/clientbound/keep_alive.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(
|
||||||
|
configuration_id = crate::ids::configuration::clientbound::KEEP_ALIVE,
|
||||||
|
play_id = crate::ids::play::clientbound::KEEP_ALIVE,
|
||||||
|
)]
|
||||||
|
pub struct KeepAlivePacket {
|
||||||
|
pub id: i64,
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod finish_configuration;
|
pub mod finish_configuration;
|
||||||
pub mod game_event;
|
pub mod game_event;
|
||||||
|
pub mod keep_alive;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod login_disconnect;
|
pub mod login_disconnect;
|
||||||
pub mod login_finished;
|
pub mod login_finished;
|
||||||
|
@ -11,6 +12,7 @@ pub mod status_response;
|
||||||
|
|
||||||
pub use finish_configuration::FinishConfigurationPacket;
|
pub use finish_configuration::FinishConfigurationPacket;
|
||||||
pub use game_event::GameEventPacket;
|
pub use game_event::GameEventPacket;
|
||||||
|
pub use keep_alive::KeepAlivePacket;
|
||||||
pub use login::LoginPacket;
|
pub use login::LoginPacket;
|
||||||
pub use login_disconnect::LoginDisconnectPacket;
|
pub use login_disconnect::LoginDisconnectPacket;
|
||||||
pub use login_finished::LoginFinishedPacket;
|
pub use login_finished::LoginFinishedPacket;
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
pub mod clientbound;
|
pub mod clientbound;
|
||||||
pub mod serverbound;
|
pub mod serverbound;
|
||||||
|
|
||||||
use crate::packet_encodable::PacketEncodable;
|
use std::fmt;
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use tokio::{io::AsyncWriteExt, net::tcp::OwnedWriteHalf};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
datatypes::var_int::VarInt,
|
||||||
|
packet_encodable::{PacketDecodeError, PacketEncodable},
|
||||||
|
};
|
||||||
|
|
||||||
pub trait Packet: PacketEncodable {
|
pub trait Packet: PacketEncodable {
|
||||||
const HANDSHAKE_ID: i32;
|
const HANDSHAKE_ID: i32;
|
||||||
|
@ -9,4 +17,30 @@ pub trait Packet: PacketEncodable {
|
||||||
const LOGIN_ID: i32;
|
const LOGIN_ID: i32;
|
||||||
const CONFIGURATION_ID: i32;
|
const CONFIGURATION_ID: i32;
|
||||||
const PLAY_ID: i32;
|
const PLAY_ID: i32;
|
||||||
|
|
||||||
|
fn from_raw(mut raw: RawPacket) -> Result<Self, PacketDecodeError> {
|
||||||
|
Self::decode_packet(&mut raw.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RawPacket {
|
||||||
|
pub id: VarInt,
|
||||||
|
pub data: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawPacket {
|
||||||
|
pub async fn write_to_stream(&self, stream: &mut OwnedWriteHalf) -> Result<(), std::io::Error> {
|
||||||
|
let length: VarInt = (self.id.encoded_len() + self.data.len()).into();
|
||||||
|
length.write_to_stream(stream).await?;
|
||||||
|
self.id.write_to_stream(stream).await?;
|
||||||
|
stream.write_all(&self.data).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RawPacket {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let id: i32 = self.id.into();
|
||||||
|
write!(f, "{{ id: {}, data: {:X?} }}", id, self.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use bytes::{Buf, BufMut};
|
||||||
use potato_protocol_derive::{Packet, PacketEncodable};
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
|
||||||
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
||||||
|
@ -56,7 +57,7 @@ pub struct DisplayedSkinParts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for DisplayedSkinParts {
|
impl PacketEncodable for DisplayedSkinParts {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let value = (self.cape as u8)
|
let value = (self.cape as u8)
|
||||||
| (self.jacket as u8) << 1
|
| (self.jacket as u8) << 1
|
||||||
| (self.left_sleeve as u8) << 2
|
| (self.left_sleeve as u8) << 2
|
||||||
|
@ -65,11 +66,11 @@ impl PacketEncodable for DisplayedSkinParts {
|
||||||
| (self.right_pants_leg as u8) << 5
|
| (self.right_pants_leg as u8) << 5
|
||||||
| (self.hat as u8) << 6;
|
| (self.hat as u8) << 6;
|
||||||
|
|
||||||
value.encode_packet(buffer)
|
value.encode_packet(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut std::io::Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let value = u8::decode_packet(cursor)?;
|
let value = u8::decode_packet(buf)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cape: value & 0x01 != 0,
|
cape: value & 0x01 != 0,
|
||||||
|
|
10
potato-protocol/src/packet/serverbound/keep_alive.rs
Normal file
10
potato-protocol/src/packet/serverbound/keep_alive.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use potato_protocol_derive::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug, Packet)]
|
||||||
|
#[packet(
|
||||||
|
configuration_id = crate::ids::configuration::serverbound::KEEP_ALIVE,
|
||||||
|
play_id = crate::ids::play::serverbound::KEEP_ALIVE,
|
||||||
|
)]
|
||||||
|
pub struct KeepAlivePacket {
|
||||||
|
pub id: i64,
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ pub mod custom_payload;
|
||||||
pub mod finish_configuration;
|
pub mod finish_configuration;
|
||||||
pub mod hello;
|
pub mod hello;
|
||||||
pub mod intention;
|
pub mod intention;
|
||||||
|
pub mod keep_alive;
|
||||||
pub mod login_acknowledged;
|
pub mod login_acknowledged;
|
||||||
pub mod move_player_pos;
|
pub mod move_player_pos;
|
||||||
pub mod move_player_pos_rot;
|
pub mod move_player_pos_rot;
|
||||||
|
@ -18,6 +19,7 @@ pub use custom_payload::CustomPayloadPacket;
|
||||||
pub use finish_configuration::FinishConfigurationPacket;
|
pub use finish_configuration::FinishConfigurationPacket;
|
||||||
pub use hello::HelloPacket;
|
pub use hello::HelloPacket;
|
||||||
pub use intention::IntentionPacket;
|
pub use intention::IntentionPacket;
|
||||||
|
pub use keep_alive::KeepAlivePacket;
|
||||||
pub use login_acknowledged::LoginAcknowledgedPacket;
|
pub use login_acknowledged::LoginAcknowledgedPacket;
|
||||||
pub use move_player_pos::MovePlayerPosPacket;
|
pub use move_player_pos::MovePlayerPosPacket;
|
||||||
pub use move_player_pos_rot::MovePlayerPosRotPacket;
|
pub use move_player_pos_rot::MovePlayerPosRotPacket;
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use bytes::{Buf, BufMut};
|
||||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::{
|
use std::fmt;
|
||||||
fmt,
|
|
||||||
io::{Cursor, Read},
|
|
||||||
marker::PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -12,8 +8,8 @@ use uuid::Uuid;
|
||||||
use crate::datatypes::var_int::VarInt;
|
use crate::datatypes::var_int::VarInt;
|
||||||
|
|
||||||
pub trait PacketEncodable: Sized + fmt::Debug {
|
pub trait PacketEncodable: Sized + fmt::Debug {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError>;
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError>;
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError>;
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -32,6 +28,10 @@ pub enum PacketDecodeError {
|
||||||
InvalidIdentifier(String),
|
InvalidIdentifier(String),
|
||||||
#[error("Error while decoding NBT in packet {0}")]
|
#[error("Error while decoding NBT in packet {0}")]
|
||||||
NbtError(#[from] fastnbt::error::Error),
|
NbtError(#[from] fastnbt::error::Error),
|
||||||
|
#[error("Not enough data to decode the packet yet")]
|
||||||
|
Incomplete,
|
||||||
|
#[error("Invalid VarInt")]
|
||||||
|
InvalidVarInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -47,13 +47,13 @@ pub enum PacketEncodeError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for Uuid {
|
impl PacketEncodable for Uuid {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
buffer.write_u128::<BigEndian>(self.as_u128())?;
|
buf.put_u128(self.as_u128());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let data: u128 = cursor.read_u128::<BigEndian>()?;
|
let data = buf.get_u128();
|
||||||
Ok(Uuid::from_u128(data))
|
Ok(Uuid::from_u128(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,13 @@ impl<T> PacketEncodable for Json<T>
|
||||||
where
|
where
|
||||||
T: Serialize + DeserializeOwned + fmt::Debug,
|
T: Serialize + DeserializeOwned + fmt::Debug,
|
||||||
{
|
{
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let json = serde_json::to_string(&self.0)?;
|
let json = serde_json::to_string(&self.0)?;
|
||||||
json.encode_packet(buffer)
|
json.encode_packet(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let json = String::decode_packet(cursor)?;
|
let json = String::decode_packet(buf)?;
|
||||||
Ok(Self(serde_json::from_str(&json)?))
|
Ok(Self(serde_json::from_str(&json)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,15 +85,16 @@ impl<T> PacketEncodable for Nbt<T>
|
||||||
where
|
where
|
||||||
T: Serialize + DeserializeOwned + fmt::Debug,
|
T: Serialize + DeserializeOwned + fmt::Debug,
|
||||||
{
|
{
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
fastnbt::to_writer_with_opts(buffer, &self.0, fastnbt::SerOpts::network_nbt())?;
|
fastnbt::to_writer_with_opts(buf.writer(), &self.0, fastnbt::SerOpts::network_nbt())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let value = fastnbt::from_bytes(cursor.get_ref())?;
|
todo!("Decode NBT in packets")
|
||||||
|
// let value = fastnbt::from_bytes(buf)?;
|
||||||
Ok(Nbt(value))
|
//
|
||||||
|
// Ok(Nbt(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,21 +102,21 @@ impl<T> PacketEncodable for Option<T>
|
||||||
where
|
where
|
||||||
T: PacketEncodable,
|
T: PacketEncodable,
|
||||||
{
|
{
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let present = self.is_some();
|
let present = self.is_some();
|
||||||
present.encode_packet(buffer)?;
|
present.encode_packet(buf)?;
|
||||||
if let Some(value) = self {
|
if let Some(value) = self {
|
||||||
value.encode_packet(buffer)?;
|
value.encode_packet(buf)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let present = bool::decode_packet(cursor)?;
|
let present = bool::decode_packet(buf)?;
|
||||||
|
|
||||||
if present {
|
if present {
|
||||||
Ok(Some(T::decode_packet(cursor)?))
|
Ok(Some(T::decode_packet(buf)?))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -126,22 +127,22 @@ impl<T> PacketEncodable for Vec<T>
|
||||||
where
|
where
|
||||||
T: PacketEncodable,
|
T: PacketEncodable,
|
||||||
{
|
{
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let length: VarInt = self.len().into();
|
let length: VarInt = self.len().into();
|
||||||
length.encode_packet(buffer)?;
|
length.encode_packet(buf)?;
|
||||||
|
|
||||||
for item in self {
|
for item in self {
|
||||||
item.encode_packet(buffer)?;
|
item.encode_packet(buf)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let length: usize = VarInt::decode_packet(cursor)?.into();
|
let length: usize = VarInt::decode_packet(buf)?.into();
|
||||||
let mut vec = Vec::with_capacity(length);
|
let mut vec = Vec::with_capacity(length);
|
||||||
for _ in 0..length {
|
for _ in 0..length {
|
||||||
vec.push(T::decode_packet(cursor)?);
|
vec.push(T::decode_packet(buf)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
|
@ -149,18 +150,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for String {
|
impl PacketEncodable for String {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
let lenght: VarInt = self.len().into();
|
let lenght: VarInt = self.len().into();
|
||||||
lenght.encode_packet(buffer)?;
|
lenght.encode_packet(buf)?;
|
||||||
|
|
||||||
buffer.extend_from_slice(self.as_bytes());
|
buf.put_slice(self.as_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let lenght: usize = VarInt::decode_packet(cursor)?.into();
|
let lenght: usize = VarInt::decode_packet(buf)?.into();
|
||||||
let mut buffer = vec![0; lenght];
|
let mut buffer = vec![0; lenght];
|
||||||
cursor.read_exact(&mut buffer)?;
|
buf.copy_to_slice(&mut buffer);
|
||||||
let value = String::from_utf8(buffer)?;
|
let value = String::from_utf8(buffer)?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
|
@ -168,52 +169,28 @@ impl PacketEncodable for String {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketEncodable for bool {
|
impl PacketEncodable for bool {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
buffer.push(if *self { 1 } else { 0 });
|
buf.put_u8(if *self { 1 } else { 0 });
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let v = cursor.read_u8()?;
|
let v = buf.get_u8();
|
||||||
Ok(v != 0)
|
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 {
|
macro_rules! number_impl {
|
||||||
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
|
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
|
||||||
$(
|
$(
|
||||||
impl PacketEncodable for $type {
|
impl PacketEncodable for $type {
|
||||||
fn encode_packet(&self, buffer: &mut Vec<u8>) -> Result<(), PacketEncodeError> {
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
buffer.$write_fn::<BigEndian>(*self)?;
|
buf.$write_fn(*self);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_packet(cursor: &mut Cursor<&[u8]>) -> Result<Self, PacketDecodeError> {
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
let value = cursor.$read_fn::<BigEndian>()?;
|
let value = buf.$read_fn();
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,14 +199,16 @@ macro_rules! number_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
number_impl! {
|
number_impl! {
|
||||||
u16, read_u16, write_u16,
|
u8, get_u8, put_u8,
|
||||||
u32, read_u32, write_u32,
|
u16, get_u16, put_u16,
|
||||||
u64, read_u64, write_u64,
|
u32, get_u32, put_u32,
|
||||||
|
u64, get_u64, put_u64,
|
||||||
|
|
||||||
i16, read_i16, write_i16,
|
i8, get_i8, put_i8,
|
||||||
i32, read_i32, write_i32,
|
i16, get_i16, put_i16,
|
||||||
i64, read_i64, write_i64,
|
i32, get_i32, put_i32,
|
||||||
|
i64, get_i64, put_i64,
|
||||||
|
|
||||||
f32, read_f32, write_f32,
|
f32, get_f32, put_f32,
|
||||||
f64, read_f64, write_f64,
|
f64, get_f64, put_f64,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,6 @@ potato-protocol = { path = "../potato-protocol" }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
bytes.workspace = true
|
||||||
|
|
528
potato/src/connection.rs
Normal file
528
potato/src/connection.rs
Normal file
|
@ -0,0 +1,528 @@
|
||||||
|
use std::{collections::VecDeque, net::SocketAddr, sync::Arc};
|
||||||
|
|
||||||
|
use bytes::{Buf, BytesMut};
|
||||||
|
use potato_protocol::{
|
||||||
|
datatypes::{identifier::Identifier, pack::Pack, var_int::VarInt},
|
||||||
|
packet::{
|
||||||
|
Packet, RawPacket,
|
||||||
|
clientbound::{
|
||||||
|
self, GameEventPacket, SetChunkCacheCenterPacket,
|
||||||
|
registry_data::{
|
||||||
|
Biome, BiomeEffects, DimensionType, PaintingVariant, RegistryData,
|
||||||
|
RegistryDataEntry, WolfVariant,
|
||||||
|
},
|
||||||
|
status_response,
|
||||||
|
},
|
||||||
|
serverbound,
|
||||||
|
},
|
||||||
|
packet_encodable::{Json, Nbt, PacketDecodeError, PacketEncodable, PacketEncodeError},
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::{
|
||||||
|
io::AsyncReadExt,
|
||||||
|
net::tcp::OwnedReadHalf,
|
||||||
|
sync::{Mutex, RwLock, mpsc::Sender},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::server::Registries;
|
||||||
|
|
||||||
|
// Max packet size is 2MB in vanilla
|
||||||
|
const RECV_BUFFER_SIZE: usize = 1024 * 1024 * 2;
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
address: SocketAddr,
|
||||||
|
packet_sender: Sender<RawPacket>,
|
||||||
|
readable_stream: Mutex<OwnedReadHalf>,
|
||||||
|
state: RwLock<ConnectionState>,
|
||||||
|
recv_buffer: Mutex<BytesMut>,
|
||||||
|
packet_queue: Mutex<VecDeque<RawPacket>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum ConnectionState {
|
||||||
|
Handshaking,
|
||||||
|
Status,
|
||||||
|
Login,
|
||||||
|
Configuration,
|
||||||
|
Play,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(
|
||||||
|
packet_sender: Sender<RawPacket>,
|
||||||
|
readable_stream: OwnedReadHalf,
|
||||||
|
) -> Result<Client, std::io::Error> {
|
||||||
|
Ok(Client {
|
||||||
|
address: readable_stream.peer_addr()?,
|
||||||
|
packet_sender,
|
||||||
|
readable_stream: Mutex::new(readable_stream),
|
||||||
|
state: RwLock::new(ConnectionState::Handshaking),
|
||||||
|
recv_buffer: Mutex::new(BytesMut::new()),
|
||||||
|
packet_queue: Mutex::new(VecDeque::new()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn needs_keep_alive(&self) -> bool {
|
||||||
|
let state = self.state.read().await;
|
||||||
|
*state == ConnectionState::Configuration || *state == ConnectionState::Play
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_packets(&self) -> Result<(), ConnectionError> {
|
||||||
|
{
|
||||||
|
// NOTE: Deadlock waiting to happen, maybe there is a way to only lock once both are
|
||||||
|
// available.
|
||||||
|
let mut stream = self.readable_stream.lock().await;
|
||||||
|
let mut recv_buffer = self.recv_buffer.lock().await;
|
||||||
|
recv_buffer.reserve(RECV_BUFFER_SIZE);
|
||||||
|
// TODO: Deal with errors
|
||||||
|
let _ = stream.read_buf(&mut *recv_buffer).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read packets until we encounter an incomplete packet
|
||||||
|
loop {
|
||||||
|
match self.try_read_packet().await {
|
||||||
|
Ok(raw_packet) => {
|
||||||
|
let mut packet_queue = self.packet_queue.lock().await;
|
||||||
|
packet_queue.push_back(raw_packet);
|
||||||
|
}
|
||||||
|
Err(ConnectionError::DecodeError(PacketDecodeError::Incomplete)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_read_packet(&self) -> Result<RawPacket, ConnectionError> {
|
||||||
|
let mut recv_buffer = self.recv_buffer.lock().await;
|
||||||
|
let length: usize = VarInt::decode_packet(&mut *recv_buffer)?.into();
|
||||||
|
|
||||||
|
if recv_buffer.remaining() < length {
|
||||||
|
return Err(PacketDecodeError::Incomplete.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = VarInt::decode_packet(&mut *recv_buffer)?;
|
||||||
|
let data = recv_buffer.split_to(length - id.encoded_len()).freeze();
|
||||||
|
|
||||||
|
Ok(RawPacket { id, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_packet<T: Packet>(&self, packet: &T) -> Result<(), ConnectionError> {
|
||||||
|
let state = { *self.state.read().await };
|
||||||
|
println!("[{} ({:?})] -> {:?}", self.address, state, packet);
|
||||||
|
let mut buffer = BytesMut::new();
|
||||||
|
// Encode ID
|
||||||
|
let packet_id: i32 = match 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,
|
||||||
|
};
|
||||||
|
assert!(packet_id != -1, "Packet ID can not be -1");
|
||||||
|
|
||||||
|
let packet_id: VarInt = packet_id.into();
|
||||||
|
|
||||||
|
packet.encode_packet(&mut buffer)?;
|
||||||
|
|
||||||
|
match self
|
||||||
|
.packet_sender
|
||||||
|
.send(RawPacket {
|
||||||
|
id: packet_id,
|
||||||
|
data: buffer.freeze(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("[{}] Error while sending packet: {}", self.address, e);
|
||||||
|
return Err(ConnectionError::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_packets(
|
||||||
|
&self,
|
||||||
|
registries: &Arc<Registries>,
|
||||||
|
) -> Result<(), ConnectionError> {
|
||||||
|
while let Some(raw_packet) = {
|
||||||
|
let mut packet_queue = self.packet_queue.lock().await;
|
||||||
|
packet_queue.pop_front()
|
||||||
|
} {
|
||||||
|
// TODO: Need to check that this gets dropped immediately
|
||||||
|
let state = { *self.state.read().await };
|
||||||
|
match state {
|
||||||
|
ConnectionState::Handshaking => {
|
||||||
|
self.handle_handshaking(raw_packet).await?;
|
||||||
|
}
|
||||||
|
ConnectionState::Status => {
|
||||||
|
self.handle_status(raw_packet).await?;
|
||||||
|
}
|
||||||
|
ConnectionState::Login => {
|
||||||
|
self.handle_login(raw_packet).await?;
|
||||||
|
}
|
||||||
|
ConnectionState::Configuration => {
|
||||||
|
self.handle_configuration(raw_packet, registries).await?;
|
||||||
|
}
|
||||||
|
ConnectionState::Play => {
|
||||||
|
// TODO: Might not be able to handle this in the client, might need a full
|
||||||
|
// player for that.
|
||||||
|
// println!("Play packet on client. Ignoring for now")
|
||||||
|
// self.handle_play(raw_packet)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_handshaking(&self, raw_packet: RawPacket) -> Result<(), ConnectionError> {
|
||||||
|
match raw_packet.id.into() {
|
||||||
|
serverbound::IntentionPacket::HANDSHAKE_ID => {
|
||||||
|
let packet = serverbound::IntentionPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Handshaking)] <- {:?}", self.address, packet,);
|
||||||
|
self.change_state_num(packet.next_state.into()).await?
|
||||||
|
}
|
||||||
|
// TODO: Legacy server list ping
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Handshaking)] <- Unknown packet: {}",
|
||||||
|
self.address, raw_packet
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_status(&self, raw_packet: RawPacket) -> Result<(), ConnectionError> {
|
||||||
|
match raw_packet.id.into() {
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
serverbound::PingRequestPacket::STATUS_ID => {
|
||||||
|
let packet = serverbound::PingRequestPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Status)] <- {:?}", self.address, packet);
|
||||||
|
self.send_packet(&clientbound::PongResponsePacket {
|
||||||
|
timestamp: packet.timestamp,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
// TODO: Signal to close connection
|
||||||
|
// self.stream.shutdown(Shutdown::Both)?;
|
||||||
|
return Err(ConnectionError::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Status)] <- Unknown packet: {}",
|
||||||
|
self.address, raw_packet
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_login(&self, raw_packet: RawPacket) -> Result<(), ConnectionError> {
|
||||||
|
match raw_packet.id.into() {
|
||||||
|
serverbound::HelloPacket::LOGIN_ID => {
|
||||||
|
let packet = serverbound::HelloPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Login)] <- {:?}", self.address, packet);
|
||||||
|
self.send_packet(&clientbound::LoginFinishedPacket {
|
||||||
|
uuid: packet.uuid,
|
||||||
|
username: packet.name,
|
||||||
|
properties: vec![],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
serverbound::LoginAcknowledgedPacket::LOGIN_ID => {
|
||||||
|
let packet = serverbound::LoginAcknowledgedPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Login)] <- {:?}", self.address, packet);
|
||||||
|
self.change_state(ConnectionState::Configuration).await?;
|
||||||
|
self.send_packet(&clientbound::SelectKnownPacksPacket {
|
||||||
|
packs: vec![Pack {
|
||||||
|
namespace: "minecraft".to_owned(),
|
||||||
|
id: "core".to_owned(),
|
||||||
|
version: "1.21.4".to_owned(),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Login)] <- Unknown packet: {}",
|
||||||
|
self.address, raw_packet
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_configuration(
|
||||||
|
&self,
|
||||||
|
raw_packet: RawPacket,
|
||||||
|
registries: &Arc<Registries>,
|
||||||
|
) -> Result<(), ConnectionError> {
|
||||||
|
match raw_packet.id.into() {
|
||||||
|
serverbound::ClientInformationPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::ClientInformationPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::CustomPayloadPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::CustomPayloadPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
}
|
||||||
|
serverbound::SelectKnownPacksPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::SelectKnownPacksPacket::from_raw(raw_packet)?;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
|
||||||
|
self.sync_registries(registries).await?;
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::FinishConfigurationPacket)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
serverbound::FinishConfigurationPacket::CONFIGURATION_ID => {
|
||||||
|
let packet = serverbound::finish_configuration::FinishConfigurationPacket;
|
||||||
|
println!("[{} (Configuration)] <- {:?}", self.address, packet);
|
||||||
|
self.change_state(ConnectionState::Play).await?;
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.send_packet(&GameEventPacket {
|
||||||
|
event: 13,
|
||||||
|
data: 0.,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.send_packet(&SetChunkCacheCenterPacket {
|
||||||
|
x: 0.into(),
|
||||||
|
z: 0.into(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
serverbound::KeepAlivePacket::CONFIGURATION_ID => {
|
||||||
|
let _ = serverbound::KeepAlivePacket::from_raw(raw_packet)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"[{} (Configuration)] <- Unknown packet: {}",
|
||||||
|
self.address, raw_packet
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
async fn sync_registries(&self, registries: &Arc<Registries>) -> Result<(), ConnectionError> {
|
||||||
|
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,
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
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,
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
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![],
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let damage_type_registry_entries = registries
|
||||||
|
.damage_types
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| RegistryDataEntry {
|
||||||
|
id: key.clone(),
|
||||||
|
data: Some(Nbt(RegistryData::DamegeType(value.clone().into()))),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: Identifier::minecraft_str("damage_type"),
|
||||||
|
entries: damage_type_registry_entries,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}))),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn change_state_num(&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).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn change_state(&self, new_state: ConnectionState) -> Result<(), ConnectionError> {
|
||||||
|
{
|
||||||
|
let state = self.state.read().await;
|
||||||
|
println!("[{}] {:?} -> {:?}", self.address, state, new_state);
|
||||||
|
}
|
||||||
|
*self.state.write().await = new_state;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
86
potato/src/datapack/damage_type.rs
Normal file
86
potato/src/datapack/damage_type.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use potato_protocol::packet::clientbound::registry_data;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct DamageType {
|
||||||
|
message_id: String,
|
||||||
|
scaling: DamageTypeScaling,
|
||||||
|
exhaustion: f32,
|
||||||
|
effects: Option<DamageTypeEffect>,
|
||||||
|
death_message_type: Option<DamageTypeDeathMessageType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: It is stupid to have two identical types
|
||||||
|
impl From<DamageType> for registry_data::DamageType {
|
||||||
|
fn from(value: DamageType) -> Self {
|
||||||
|
registry_data::DamageType {
|
||||||
|
message_id: value.message_id,
|
||||||
|
scaling: value.scaling.into(),
|
||||||
|
exhaustion: value.exhaustion,
|
||||||
|
effects: value.effects.map(Into::<String>::into),
|
||||||
|
death_message_type: value.death_message_type.map(Into::<String>::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeScaling {
|
||||||
|
Never,
|
||||||
|
WhenCausedByLivingNonPlayer,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DamageTypeScaling> for String {
|
||||||
|
fn from(value: DamageTypeScaling) -> Self {
|
||||||
|
match value {
|
||||||
|
DamageTypeScaling::Never => "never".into(),
|
||||||
|
DamageTypeScaling::WhenCausedByLivingNonPlayer => {
|
||||||
|
"when_caused_by_living_non_player".into()
|
||||||
|
}
|
||||||
|
DamageTypeScaling::Always => "always".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeEffect {
|
||||||
|
Hurt,
|
||||||
|
Thorns,
|
||||||
|
Drowning,
|
||||||
|
Burning,
|
||||||
|
Poking,
|
||||||
|
Freezing,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DamageTypeEffect> for String {
|
||||||
|
fn from(value: DamageTypeEffect) -> Self {
|
||||||
|
match value {
|
||||||
|
DamageTypeEffect::Hurt => "hurt".into(),
|
||||||
|
DamageTypeEffect::Thorns => "thorns".into(),
|
||||||
|
DamageTypeEffect::Drowning => "drowning".into(),
|
||||||
|
DamageTypeEffect::Burning => "burning".into(),
|
||||||
|
DamageTypeEffect::Poking => "poking".into(),
|
||||||
|
DamageTypeEffect::Freezing => "freezing".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeDeathMessageType {
|
||||||
|
Default,
|
||||||
|
FallVariants,
|
||||||
|
IntentionalGameDesign,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DamageTypeDeathMessageType> for String {
|
||||||
|
fn from(value: DamageTypeDeathMessageType) -> Self {
|
||||||
|
match value {
|
||||||
|
DamageTypeDeathMessageType::Default => "default".into(),
|
||||||
|
DamageTypeDeathMessageType::FallVariants => "fall_variants".into(),
|
||||||
|
DamageTypeDeathMessageType::IntentionalGameDesign => "intentional_game_design".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
potato/src/datapack/mod.rs
Normal file
56
potato/src/datapack/mod.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
pub mod damage_type;
|
||||||
|
|
||||||
|
use std::{fs::read_to_string, path::PathBuf};
|
||||||
|
|
||||||
|
use damage_type::DamageType;
|
||||||
|
use potato_protocol::datatypes::identifier::Identifier;
|
||||||
|
|
||||||
|
use crate::server::{Registries, Registry};
|
||||||
|
|
||||||
|
pub struct Datapack {
|
||||||
|
pub name: String,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Datapack {
|
||||||
|
pub fn new(path: PathBuf) -> Self {
|
||||||
|
// TODO: Load name (and other info) from pack.mcmeta
|
||||||
|
let name = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||||
|
|
||||||
|
Self { name, path }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self, registries: &mut Registries) {
|
||||||
|
println!("Loading datapack: {}", self.name);
|
||||||
|
self.load_damage_types(&mut registries.damage_types);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove the unwraps and deal with errors gracefully
|
||||||
|
fn load_damage_types(&self, registry: &mut Registry<DamageType>) {
|
||||||
|
let namespaces: Vec<_> = self
|
||||||
|
.path
|
||||||
|
.join("data")
|
||||||
|
.read_dir()
|
||||||
|
.unwrap()
|
||||||
|
.map(|f| f.unwrap().path())
|
||||||
|
.filter(|p| p.is_dir())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for namespace in namespaces {
|
||||||
|
let namespace_str = namespace.file_name().unwrap().to_str().unwrap();
|
||||||
|
let path = namespace.join("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();
|
||||||
|
let identfiier = Identifier::new_str(namespace_str, name);
|
||||||
|
|
||||||
|
let data: DamageType = serde_json::from_str(&read_to_string(file).unwrap())
|
||||||
|
.expect("Failed to parse damage type");
|
||||||
|
|
||||||
|
registry.insert(identfiier, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,544 +1,102 @@
|
||||||
use std::{
|
pub mod connection;
|
||||||
collections::HashMap,
|
pub mod datapack;
|
||||||
fmt::Debug,
|
pub mod player;
|
||||||
fs::read_to_string,
|
pub mod server;
|
||||||
io::{Cursor, Read, Write},
|
|
||||||
net::{Shutdown, SocketAddr, TcpListener, TcpStream},
|
|
||||||
path::PathBuf,
|
|
||||||
sync::Arc,
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
use potato_protocol::{
|
use std::{sync::Arc, time::Duration};
|
||||||
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() {
|
use connection::Client;
|
||||||
println!("Looking for datapacks...");
|
use potato_protocol::packet::clientbound;
|
||||||
let datapacks: Vec<PathBuf> = std::fs::read_dir("run/datapacks")
|
use server::Server;
|
||||||
.expect("Failed to read datapacks directory")
|
use tokio::{net::TcpListener, time::interval};
|
||||||
.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);
|
#[tokio::main]
|
||||||
}
|
async fn main() {
|
||||||
}
|
let server = PotatoServer::new();
|
||||||
}
|
|
||||||
|
|
||||||
println!("Finished loading datapacks");
|
server.listen_for_connections().await;
|
||||||
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>) {
|
pub struct PotatoServer {
|
||||||
if let Ok(mut client) = ClientConnection::new(stream, registries) {
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PotatoServer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PotatoServer {
|
||||||
|
server: Server::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn listen_for_connections(&self) {
|
||||||
|
let listener = TcpListener::bind("0.0.0.0:25565")
|
||||||
|
.await
|
||||||
|
.expect("Failed to start listening on port 25565");
|
||||||
|
println!("listening started, ready to accept");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match client.read_packet() {
|
if let Ok((stream, _)) = listener.accept().await {
|
||||||
Ok(_) => (),
|
let (stream_read, mut stream_send) = stream.into_split();
|
||||||
Err(e) => {
|
let (tx, mut rx) = tokio::sync::mpsc::channel(64);
|
||||||
println!("[{}] Error while handling packets: {}", client.address, e);
|
let Ok(client) = Client::new(tx, stream_read) else {
|
||||||
// Make sure the connection is closed.
|
println!("Failed to create client");
|
||||||
let _ = client.stream.shutdown(Shutdown::Both);
|
continue;
|
||||||
break;
|
};
|
||||||
|
let client = Arc::new(client);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(packet) = rx.recv().await {
|
||||||
|
if let Err(e) = packet.write_to_stream(&mut stream_send).await {
|
||||||
|
println!("Error while sending packet: {}", e);
|
||||||
|
// TODO: Close connection with client here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keep alive task
|
||||||
|
{
|
||||||
|
let client = client.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut interval = interval(Duration::from_secs(10));
|
||||||
|
// TODO: Terminate this when the client connection is closed.
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
if client.needs_keep_alive().await {
|
||||||
|
// TODO: Deal with this error by closing the connection.
|
||||||
|
// TODO: Use a unique id for each keep alive packet (or at least a
|
||||||
|
// timestamp)
|
||||||
|
match client
|
||||||
|
.send_packet(&clientbound::KeepAlivePacket { id: 0 })
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error while sending keep alive packet: {}", e)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let registries = self.server.registries.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
// TODO: Deal with errors
|
||||||
|
loop {
|
||||||
|
client
|
||||||
|
.read_packets()
|
||||||
|
.await
|
||||||
|
.expect("Error while reading packets");
|
||||||
|
client
|
||||||
|
.handle_packets(®istries)
|
||||||
|
.await
|
||||||
|
.expect("Error while handling packets");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
2
potato/src/player.rs
Normal file
2
potato/src/player.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
|
47
potato/src/server.rs
Normal file
47
potato/src/server.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use potato_protocol::datatypes::identifier::Identifier;
|
||||||
|
|
||||||
|
use crate::datapack::{Datapack, damage_type::DamageType};
|
||||||
|
|
||||||
|
pub struct Server {
|
||||||
|
pub registries: Arc<Registries>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Server {
|
||||||
|
println!("Looking for datapacks...");
|
||||||
|
let datapacks: Vec<_> = std::fs::read_dir("datapacks")
|
||||||
|
.expect("Failed to read datapacks directory")
|
||||||
|
.map(|f| f.unwrap().path())
|
||||||
|
.filter(|p| p.is_dir())
|
||||||
|
.map(Datapack::new)
|
||||||
|
.collect();
|
||||||
|
println!("Found {} datapacks", datapacks.len());
|
||||||
|
println!("Loading datapacks...");
|
||||||
|
let mut registries = Registries::empty();
|
||||||
|
datapacks.iter().for_each(|pack| pack.load(&mut registries));
|
||||||
|
|
||||||
|
println!("Finished loading datapacks");
|
||||||
|
println!("Loaded {} damage types", registries.damage_types.len());
|
||||||
|
|
||||||
|
Server {
|
||||||
|
registries: Arc::new(registries),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Registry<T> = HashMap<Identifier, T>;
|
||||||
|
|
||||||
|
pub struct Registries {
|
||||||
|
pub damage_types: Registry<DamageType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registries {
|
||||||
|
fn empty() -> Registries {
|
||||||
|
Registries {
|
||||||
|
damage_types: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue