feat: random head rotation & swing

This commit is contained in:
Alexander Medvedev 2026-01-31 15:42:48 +01:00
parent 3e956643a3
commit e4fa37f2c9
5 changed files with 580 additions and 118 deletions

313
Cargo.lock generated
View File

@ -75,6 +75,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.37" version = "0.4.37"
@ -137,7 +143,7 @@ dependencies = [
[[package]] [[package]]
name = "botmark" name = "botmark"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"clap", "clap",
"crossbeam", "crossbeam",
@ -186,13 +192,13 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "chacha20" name = "chacha20"
version = "0.10.0-rc.8" version = "0.10.0-rc.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31cd65b2ca03198c223cd9a8fa1152c4ec251cd79049f6dc584152ad3fb5ba9d" checksum = "c81d916c6ae06736ec667b51f95ee5ff660a75f4ea6ce1bd932c942365c0ea43"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"rand_core 0.10.0-rc-5", "rand_core 0.10.0-rc-6",
] ]
[[package]] [[package]]
@ -207,9 +213,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.54" version = "4.5.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -217,9 +223,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.54" version = "4.5.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -229,9 +235,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.49" version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -374,15 +380,14 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]] [[package]]
name = "crossfire" name = "crossfire"
version = "2.1.10" version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd901251b9b46c1752c85edfee0aee718c03a85a065f4126d32e5d6d419edf48" checksum = "cf9c756fd351d7bc1f95698a33a90ce39c8f7458335cc7e95132d3d36124902a"
dependencies = [ dependencies = [
"crossbeam-queue",
"crossbeam-utils", "crossbeam-utils",
"enum_dispatch",
"futures-core", "futures-core",
"parking_lot", "parking_lot",
"smallvec",
] ]
[[package]] [[package]]
@ -409,9 +414,9 @@ dependencies = [
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.2.0-rc.12" version = "0.2.0-rc.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6dcdb44f2c3ee25689ca12a4c19e664fd09f97aeae0bc5043b2dbab6389e308" checksum = "c7722afd27468475c9b6063dc03a57ef2ca833816981619f8ebe64d38d207eef"
dependencies = [ dependencies = [
"hybrid-array", "hybrid-array",
] ]
@ -464,13 +469,13 @@ dependencies = [
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.11.0-rc.7" version = "0.11.0-rc.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca14c221bd9052fd2da7c34a2eeb5ae54732db28be47c35937be71793d675422" checksum = "bff8de092798697546237a3a701e4174fe021579faec9b854379af9bf1e31962"
dependencies = [ dependencies = [
"block-buffer 0.11.0", "block-buffer 0.11.0",
"const-oid 0.10.2", "const-oid 0.10.2",
"crypto-common 0.2.0-rc.12", "crypto-common 0.2.0-rc.13",
] ]
[[package]] [[package]]
@ -568,6 +573,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.2.0" version = "0.2.0"
@ -699,15 +710,16 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.4.0-rc.0" version = "0.4.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b99f0d993a2b9b97b9a201193aa8ad21305cde06a3be9a7e1f8f4201e5cc27e" checksum = "74f70a332ddf75e5e5e43284304179ba02f391f82f692f030b08a8378adf3c99"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"rand_core 0.10.0-rc-5", "rand_core 0.10.0-rc-6",
"wasip2", "wasip2",
"wasip3",
] ]
[[package]] [[package]]
@ -727,6 +739,15 @@ version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"
@ -735,7 +756,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
"foldhash", "foldhash 0.2.0",
] ]
[[package]] [[package]]
@ -770,13 +791,19 @@ dependencies = [
[[package]] [[package]]
name = "hybrid-array" name = "hybrid-array"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" checksum = "b41fb3dc24fe72c2e3a4685eed55917c2fb228851257f4a8f2d985da9443c3e5"
dependencies = [ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.0"
@ -829,6 +856,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.180" version = "0.2.180"
@ -920,9 +953,9 @@ dependencies = [
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]] [[package]]
name = "num-derive" name = "num-derive"
@ -1133,6 +1166,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]] [[package]]
name = "primeorder" name = "primeorder"
version = "0.13.6" version = "0.13.6"
@ -1176,7 +1219,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-config" name = "pumpkin-config"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"log", "log",
"pumpkin-util", "pumpkin-util",
@ -1188,7 +1231,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-data" name = "pumpkin-data"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"crc-fast", "crc-fast",
"heck", "heck",
@ -1208,10 +1251,11 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-macros" name = "pumpkin-macros"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"pumpkin-data",
"quote", "quote",
"syn", "syn",
] ]
@ -1219,7 +1263,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-nbt" name = "pumpkin-nbt"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"bytes", "bytes",
"cesu8", "cesu8",
@ -1231,7 +1275,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-protocol" name = "pumpkin-protocol"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"aes", "aes",
"async-compression", "async-compression",
@ -1253,7 +1297,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-util" name = "pumpkin-util"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -1278,7 +1322,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-world" name = "pumpkin-world"
version = "0.1.0-dev+1.21.11" version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin#91813d4cac41f3e6ab76950a20927dda62f832ea" source = "git+https://github.com/Pumpkin-MC/Pumpkin#452d8c413b0e54a1b29eeda73fd0fdbddddebaf2"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bytes", "bytes",
@ -1304,7 +1348,7 @@ dependencies = [
"ruzstd", "ruzstd",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.11.0-rc.3", "sha2 0.11.0-rc.4",
"slotmap", "slotmap",
"thiserror", "thiserror",
"thread_local", "thread_local",
@ -1315,9 +1359,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.43" version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1330,12 +1374,12 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.10.0-rc.7" version = "0.10.0-rc.8"
source = "git+https://github.com/rust-random/rand#6e80534233d4bb09ed0abc086edeacaae6f54dc0" source = "git+https://github.com/rust-random/rand#a4c9a8e565fa310de423e3ba89593b647b721e8c"
dependencies = [ dependencies = [
"chacha20", "chacha20",
"getrandom 0.4.0-rc.0", "getrandom 0.4.0-rc.1",
"rand_core 0.10.0-rc-5", "rand_core 0.10.0-rc-6",
] ]
[[package]] [[package]]
@ -1349,9 +1393,9 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.10.0-rc-5" version = "0.10.0-rc-6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05a06e03bd1f2ae861ab9e7498b6c64ed3dadb9ce175c0464a2522a5f23c0045" checksum = "70765ff7112b0fb2d272d24d9a2f907fc206211304328fe58b2db15a5649ef28"
[[package]] [[package]]
name = "rayon" name = "rayon"
@ -1433,6 +1477,12 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@ -1509,13 +1559,13 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.11.0-rc.3" version = "0.11.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d43dc0354d88b791216bb5c1bfbb60c0814460cc653ae0ebd71f286d0bd927" checksum = "7535f94fa3339fe9e5e9be6260a909e62af97f6e14b32345ccf79b92b8b81233"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest 0.11.0-rc.7", "digest 0.11.0-rc.9",
] ]
[[package]] [[package]]
@ -1558,15 +1608,15 @@ dependencies = [
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.11" version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "slotmap" name = "slotmap"
@ -1585,9 +1635,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.6.1" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.60.2",
@ -1675,9 +1725,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.45" version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
@ -1692,15 +1742,15 @@ dependencies = [
[[package]] [[package]]
name = "time-core" name = "time-core"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.25" version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4"
dependencies = [ dependencies = [
"num-conv", "num-conv",
"time-core", "time-core",
@ -1820,6 +1870,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -1828,9 +1884,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.19.0" version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
dependencies = [ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
"js-sys", "js-sys",
@ -1857,7 +1913,16 @@ version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen 0.51.0",
]
[[package]]
name = "wasip3"
version = "0.3.1+wasi-0.3.0-rc-2025-09-16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba4be47b1d11244670d11857eee0758a8f2c39aea64d80b78c1ce29b4642cd"
dependencies = [
"wit-bindgen 0.48.1",
] ]
[[package]] [[package]]
@ -1905,6 +1970,40 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "wasm-encoder"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
@ -2000,12 +2099,100 @@ version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "wit-bindgen"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f8c2adb5f74ac9395bc3121c99a1254bf9310482c27b13f97167aedb5887138"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.51.0" version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "wit-bindgen-core"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b881a098cae03686d7a0587f8f306f8a58102ad8da8b5599100fbe0e7f5800b"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69667efa439a453e1d50dac939c6cab6d2c3ac724a9d232b6631dad2472a5b70"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae2e22cceb5d105d52326c07e3e67603a861cc7add70fc467f7cc7ec5265017"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.8.2" version = "1.8.2"
@ -2014,6 +2201,6 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.16" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" checksum = "1966f8ac2c1f76987d69a74d0e0f929241c10e78136434e3be70ff7f58f64214"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "botmark" name = "botmark"
version = "0.1.0" version = "0.1.1"
edition = "2024" edition = "2024"
[profile.release] [profile.release]
@ -12,7 +12,7 @@ codegen-units = 1
log = "0.4" log = "0.4"
simple_logger = "5.1.0" simple_logger = "5.1.0"
# CLI # CLI
clap = { version = "4.5.54", features = ["derive"] } clap = { version = "4.5.56", features = ["derive"] }
# Networking # Networking
tokio = { version = "1.49.0", features = ["net", "io-util"] } tokio = { version = "1.49.0", features = ["net", "io-util"] }
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-protocol", default-features = false, features = ["serverbound", "clientbound"]} pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-protocol", default-features = false, features = ["serverbound", "clientbound"]}
@ -20,5 +20,5 @@ pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpk
pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-data"} pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-data"}
crossbeam = "0.8.4" crossbeam = "0.8.4"
uuid = "1.19.0" uuid = "1.20.0"
rand = { git = "https://github.com/rust-random/rand"} rand = { git = "https://github.com/rust-random/rand"}

View File

@ -39,10 +39,22 @@ Here's a detailed breakdown of all available command-line arguments:
- The timeout (in milliseconds) for each bot connection. - The timeout (in milliseconds) for each bot connection.
- Example: `--delay 500` (500 milliseconds delay) - Example: `--delay 500` (500 milliseconds delay)
- **`--spam_message "<message>"` (Optional):** - **`--spam_message "<message>"` (Optional, Default: `Please do not spam!`):**
- Will send a Chat message with the specified `spam_message_delay`. - Will send a Chat message with the specified `spam_message_delay`.
- Example: `--spam_message "Hello, I'm a Robot"` - Example: `--spam_message "Hello, I'm a Robot"`
- **`--spam_message_delay <delay>` (Optional, Default: `210`):** - **`--spam_message_delay_min <delay>` (Optional, Default: `150`):**
- The delay (in milliseconds) between spam message. - The minimal delay (in milliseconds) between spam message.
- Example: `--spam_message_delay 100` (100 milliseconds delay) - Example: `--spam_message_delay_min 100` (100 milliseconds delay)
- **`--spam_message_delay_max <delay>` (Optional, Default: `350`):**
- The maximal delay (in milliseconds) between spam message.
- Example: `--spam_message_delay_max 100` (100 milliseconds delay)
- **`--spam_message_delay_max <delay>` (Optional, Default: `350`):**
- The maximal delay (in milliseconds) between spam message.
- Example: `--spam_message_delay_max 100` (100 milliseconds delay)
- **`--enable_rotation <false/true>` (Optional, Default: `true`):**
- Enable random head rotations.
- Example: `--enable_rotation true`
- **`--enable_swing <false/true>` (Optional, Default: `true`):**
- Enable random arm swings.
- Example: `--enable_swing true`

View File

@ -5,24 +5,28 @@ use pumpkin_protocol::java::client::config::{CConfigDisconnect, CFinishConfig};
use pumpkin_protocol::java::client::login::{ use pumpkin_protocol::java::client::login::{
CEncryptionRequest, CLoginDisconnect, CLoginSuccess, CSetCompression, CEncryptionRequest, CLoginDisconnect, CLoginSuccess, CSetCompression,
}; };
use pumpkin_protocol::java::client::play::{CKeepAlive, CPlayDisconnect, CPlayerPosition}; use pumpkin_protocol::java::client::play::{CKeepAlive, CLogin, CPlayDisconnect, CPlayerPosition};
use pumpkin_protocol::java::packet_decoder::TCPNetworkDecoder; use pumpkin_protocol::java::packet_decoder::TCPNetworkDecoder;
use pumpkin_protocol::java::packet_encoder::TCPNetworkEncoder; use pumpkin_protocol::java::packet_encoder::TCPNetworkEncoder;
use pumpkin_protocol::java::server::config::{SAcknowledgeFinishConfig, SKnownPacks}; use pumpkin_protocol::java::server::config::{SAcknowledgeFinishConfig, SKnownPacks};
use pumpkin_protocol::java::server::handshake::SHandShake; use pumpkin_protocol::java::server::handshake::SHandShake;
use pumpkin_protocol::java::server::login::{SLoginAcknowledged, SLoginStart}; use pumpkin_protocol::java::server::login::{SLoginAcknowledged, SLoginStart};
use pumpkin_protocol::java::server::play::{SChatMessage, SConfirmTeleport, SKeepAlive}; use pumpkin_protocol::java::server::play::{
use pumpkin_protocol::packet::Packet; SChatMessage, SConfirmTeleport, SKeepAlive, SPlayerLoaded, SPlayerPosition, SPlayerRotation,
SSwingArm,
};
use pumpkin_protocol::packet::MultiVersionJavaPacket;
use pumpkin_protocol::ser::NetworkWriteExt; use pumpkin_protocol::ser::NetworkWriteExt;
use pumpkin_protocol::ser::{ReadingError, WritingError}; use pumpkin_protocol::ser::{ReadingError, WritingError};
use pumpkin_protocol::{ use pumpkin_protocol::{
ClientPacket, CompressionLevel, CompressionThreshold, ConnectionState, PacketDecodeError, ClientPacket, CompressionLevel, CompressionThreshold, ConnectionState, PacketDecodeError,
RawPacket, ServerPacket, RawPacket, ServerPacket,
}; };
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::version::MinecraftVersion; use pumpkin_util::version::MinecraftVersion;
use std::io::Write; use std::io::Write;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
use std::sync::{Arc, atomic::AtomicBool}; use std::sync::{Arc, atomic::AtomicBool};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use tokio::io::{BufReader, BufWriter}; use tokio::io::{BufReader, BufWriter};
@ -36,7 +40,10 @@ use tokio::{
}; };
use uuid::Uuid; use uuid::Uuid;
use crate::Args;
/// Everything which makes a Connection with our Server is a `Client`. /// Everything which makes a Connection with our Server is a `Client`.
#[expect(dead_code)]
pub struct Client { pub struct Client {
/// The current connection state of the client (e.g., Handshaking, Status, Play). /// The current connection state of the client (e.g., Handshaking, Status, Play).
pub connection_state: AtomicCell<ConnectionState>, pub connection_state: AtomicCell<ConnectionState>,
@ -48,7 +55,41 @@ pub struct Client {
pub network_reader: Arc<Mutex<TCPNetworkDecoder<BufReader<OwnedReadHalf>>>>, pub network_reader: Arc<Mutex<TCPNetworkDecoder<BufReader<OwnedReadHalf>>>>,
close_interrupt: Arc<Notify>, close_interrupt: Arc<Notify>,
entity_id: AtomicI32,
message_spam_cooldown: AtomicU32, message_spam_cooldown: AtomicU32,
message_count: AtomicU32,
is_loaded: AtomicBool,
swing_cooldown: AtomicU32,
// Position
current_x: AtomicCell<f64>,
current_y: AtomicCell<f64>,
current_z: AtomicCell<f64>,
velocity_x: AtomicCell<f64>,
velocity_y: AtomicCell<f64>,
velocity_z: AtomicCell<f64>,
start_x: AtomicCell<f64>,
start_z: AtomicCell<f64>,
target_x: AtomicCell<f64>,
target_z: AtomicCell<f64>,
move_progress: AtomicCell<f32>,
move_cooldown: AtomicU32,
// Rotation
current_yaw: AtomicCell<f32>,
current_pitch: AtomicCell<f32>,
// Starting point of the movement
start_yaw: AtomicCell<f32>,
start_pitch: AtomicCell<f32>,
// Goal point
target_yaw: AtomicCell<f32>,
target_pitch: AtomicCell<f32>,
// Progress: 0.0 (start) to 1.0 (end)
rotation_progress: AtomicCell<f32>,
rotation_cooldown: AtomicU32,
} }
impl Client { impl Client {
@ -62,9 +103,35 @@ impl Client {
network_reader: Arc::new(Mutex::new(TCPNetworkDecoder::new(BufReader::new( network_reader: Arc::new(Mutex::new(TCPNetworkDecoder::new(BufReader::new(
connection_reader, connection_reader,
)))), )))),
entity_id: AtomicI32::new(0),
velocity_x: AtomicCell::new(0.0),
velocity_y: AtomicCell::new(0.0),
velocity_z: AtomicCell::new(0.0),
closed: AtomicBool::new(false), closed: AtomicBool::new(false),
swing_cooldown: AtomicU32::new(0),
close_interrupt: Arc::new(Notify::new()), close_interrupt: Arc::new(Notify::new()),
message_spam_cooldown: AtomicU32::new(1), message_spam_cooldown: AtomicU32::new(1),
message_count: AtomicU32::new(0),
is_loaded: AtomicBool::new(false),
rotation_cooldown: AtomicU32::new(0),
rotation_progress: AtomicCell::new(1.0),
current_yaw: AtomicCell::new(0.0),
current_pitch: AtomicCell::new(0.0),
start_yaw: AtomicCell::new(0.0),
start_pitch: AtomicCell::new(0.0),
target_yaw: AtomicCell::new(0.0),
target_pitch: AtomicCell::new(0.0),
current_x: AtomicCell::new(0.0),
current_y: AtomicCell::new(0.0),
current_z: AtomicCell::new(0.0),
start_x: AtomicCell::new(0.0),
start_z: AtomicCell::new(0.0),
target_x: AtomicCell::new(0.0),
target_z: AtomicCell::new(0.0),
move_progress: AtomicCell::new(1.0),
move_cooldown: AtomicU32::new(0),
} }
} }
@ -108,7 +175,7 @@ impl Client {
Ok(packet) => Some(packet), Ok(packet) => Some(packet),
Err(err) => { Err(err) => {
if !matches!(err, PacketDecodeError::ConnectionClosed) { if !matches!(err, PacketDecodeError::ConnectionClosed) {
log::warn!("Failed to decode packet from client: {err}"); log::warn!("Failed to decode packet from server: {err}");
self.close().await; self.close().await;
} }
None None
@ -136,23 +203,169 @@ impl Client {
} }
// TODO: make this less ugly ig // TODO: make this less ugly ig
pub async fn tick(&self, spam_message: Arc<Option<String>>, spam_message_delay: u32) { pub async fn tick(&self, args: &Args) {
if self.connection_state.load() != ConnectionState::Play { if self.connection_state.load() != ConnectionState::Play {
return; return;
} }
if let Some(spam_message) = spam_message.as_ref() if !self.is_loaded.load(Ordering::Relaxed) {
&& self return;
.message_spam_cooldown }
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed)
== 0 if let Some(spam_message) = &args.spam_message {
{ let result = self.message_spam_cooldown.fetch_update(
self.message_spam_cooldown Ordering::SeqCst,
.store(spam_message_delay, std::sync::atomic::Ordering::Relaxed); Ordering::Relaxed,
self.send_message(spam_message.clone()).await |curr| {
if curr == 0 {
let delay = rand::random_range(
args.spam_message_delay_min..args.spam_message_delay_max,
);
Some(delay)
} else {
Some(curr - 1)
}
},
);
if let Ok(0) = result {
self.send_message(spam_message.clone()).await;
}
}
if args.enable_rotation {
self.tick_rotation().await;
}
if args.enable_swing {
self.tick_swing().await;
}
//self.tick_movement().await
}
async fn tick_rotation(&self) {
let progress = self.rotation_progress.load();
let cooldown = self.rotation_cooldown.load(Ordering::Relaxed);
if progress >= 1.0 {
// We have finished the movement. Wait for the cooldown.
if cooldown == 0 {
// Pick a new target and reset
let current_y = self.current_yaw.load();
let current_p = self.current_pitch.load();
self.start_yaw.store(current_y);
self.start_pitch.store(current_p);
self.target_yaw.store(rand::random_range(-180.0..180.0));
self.target_pitch.store(rand::random_range(-90.0..90.0));
self.rotation_progress.store(0.0);
// Wait for 2-5 seconds (40-100 ticks) before moving again
self.rotation_cooldown
.store(rand::random_range(40..100), Ordering::Relaxed);
} else {
self.rotation_cooldown.fetch_sub(1, Ordering::Relaxed);
}
} else {
// Increment progress (e.g., 0.02 means it takes 50 ticks/2.5s to complete turn)
let new_progress = (progress + 0.02).min(1.0);
self.rotation_progress.store(new_progress);
// Calculate the "Smooth" T value using the S-Curve formula
// t = 3p^2 - 2p^3
let t = 3.0 * new_progress.powi(2) - 2.0 * new_progress.powi(3);
// Interpolate: start + (target - start) * t
let start_y = self.start_yaw.load();
let target_y = self.target_yaw.load();
let interpolated_yaw = start_y + (target_y - start_y) * t;
let start_p = self.start_pitch.load();
let target_p = self.target_pitch.load();
let interpolated_pitch = start_p + (target_p - start_p) * t;
self.current_yaw.store(interpolated_yaw);
self.current_pitch.store(interpolated_pitch);
// Send rotation packet
self.send_packet(&SPlayerRotation {
yaw: interpolated_yaw,
pitch: interpolated_pitch,
ground: true,
})
.await;
}
}
#[expect(dead_code)]
async fn tick_movement(&self) {
let progress = self.move_progress.load();
let cooldown = self.move_cooldown.load(Ordering::Relaxed);
if progress >= 1.0 {
if cooldown == 0 {
// Start a new walk
let cur_x = self.current_x.load();
let cur_z = self.current_z.load();
self.start_x.store(cur_x);
self.start_z.store(cur_z);
// Small random walk (0.5 to 1.5 blocks)
let offset_x = rand::random_range(-1.5..1.5);
let offset_z = rand::random_range(-1.5..1.5);
self.target_x.store(cur_x + offset_x);
self.target_z.store(cur_z + offset_z);
self.move_progress.store(0.0);
// Wait 2-5 seconds between moves
self.move_cooldown
.store(rand::random_range(40..100), Ordering::Relaxed);
} else {
self.move_cooldown.fetch_sub(1, Ordering::Relaxed);
}
} else {
// Increase this to 0.05 for a normal human walking pace (20 ticks to finish)
let new_progress = (progress + 0.05).min(1.0);
self.move_progress.store(new_progress);
// Cubic easing for a natural start/stop
let t = 3.0 * new_progress.powi(2) - 2.0 * new_progress.powi(3);
let start_x = self.start_x.load();
let target_x = self.target_x.load();
let start_z = self.start_z.load();
let target_z = self.target_z.load();
let interp_x = start_x + (target_x - start_x) * t as f64;
let interp_z = start_z + (target_z - start_z) * t as f64;
self.current_x.store(interp_x);
self.current_z.store(interp_z);
self.send_packet(&SPlayerPosition {
position: Vector3::new(interp_x, self.current_y.load(), interp_z),
collision: 1, // on_ground
})
.await;
}
}
async fn tick_swing(&self) {
let cooldown = self.swing_cooldown.load(Ordering::Relaxed);
if cooldown == 0 {
if rand::random_bool(0.01) {
self.send_packet(&SSwingArm { hand: VarInt(0) }).await;
self.swing_cooldown
.store(rand::random_range(20..40), Ordering::Relaxed);
}
} else {
self.swing_cooldown.fetch_sub(1, Ordering::Relaxed);
} }
} }
pub async fn send_message(&self, message: String) { pub async fn send_message(&self, message: String) {
let count = self.message_count.fetch_add(1, Ordering::SeqCst);
let start = SystemTime::now(); let start = SystemTime::now();
let since_the_epoch = start let since_the_epoch = start
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -162,7 +375,7 @@ impl Client {
timestamp: since_the_epoch.as_millis() as i64, timestamp: since_the_epoch.as_millis() as i64,
salt: rand::random(), salt: rand::random(),
signature: None, signature: None,
message_count: VarInt(1), message_count: VarInt(count as i32),
acknowledged: vec![0; 20].into_boxed_slice(), acknowledged: vec![0; 20].into_boxed_slice(),
checksum: 0, checksum: 0,
}) })
@ -174,7 +387,7 @@ impl Client {
write: impl Write, write: impl Write,
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
let mut write = write; let mut write = write;
write.write_var_int(&VarInt(P::PACKET_ID))?; write.write_var_int(&VarInt(P::PACKET_ID.latest_id))?;
packet.write_packet_data(write, &MinecraftVersion::V_1_21_11) packet.write_packet_data(write, &MinecraftVersion::V_1_21_11)
} }
@ -205,7 +418,6 @@ impl Client {
} }
pub async fn join_server(&self, address: SocketAddr, name: String) { pub async fn join_server(&self, address: SocketAddr, name: String) {
dbg!(address.ip().to_string());
self.send_packet(&SHandShake { self.send_packet(&SHandShake {
protocol_version: VarInt(CURRENT_MC_PROTOCOL as i32), protocol_version: VarInt(CURRENT_MC_PROTOCOL as i32),
server_address: address.ip().to_string(), server_address: address.ip().to_string(),
@ -236,20 +448,20 @@ impl Client {
async fn handle_login_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> { async fn handle_login_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> {
let bytebuf = &packet.payload[..]; let bytebuf = &packet.payload[..];
match packet.id { match packet.id {
CEncryptionRequest::PACKET_ID => { id if id == CEncryptionRequest::PACKET_ID => {
log::debug!("Got Encryption Request") todo!("Encryption is currently not implemented, please disable it at server level")
} }
CSetCompression::PACKET_ID => { id if id == CSetCompression::PACKET_ID => {
log::trace!("Set Compression"); log::trace!("Set Compression");
let packet = CSetCompression::read(bytebuf)?; let packet = CSetCompression::read(bytebuf)?;
self.set_compression(Some((packet.threshold.0 as usize, 6))) self.set_compression(Some((packet.threshold.0 as usize, 6)))
.await .await
} }
CLoginDisconnect::PACKET_ID => { id if id == CLoginDisconnect::PACKET_ID => {
log::error!("Kicking in Login State"); log::error!("Kicking in Login State");
self.close().await; self.close().await;
} }
CLoginSuccess::PACKET_ID => { id if id == CLoginSuccess::PACKET_ID => {
log::trace!("Login -> Config"); log::trace!("Login -> Config");
self.send_packet(&SLoginAcknowledged).await; self.send_packet(&SLoginAcknowledged).await;
self.connection_state.store(ConnectionState::Config); self.connection_state.store(ConnectionState::Config);
@ -266,11 +478,11 @@ impl Client {
async fn handle_config_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> { async fn handle_config_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> {
match packet.id { match packet.id {
CConfigDisconnect::PACKET_ID => { id if id == CConfigDisconnect::PACKET_ID => {
log::error!("Kicking in Config State"); log::error!("Kicking in Config State");
self.close().await; self.close().await;
} }
CFinishConfig::PACKET_ID => { id if id == CFinishConfig::PACKET_ID => {
log::trace!("Config -> Play"); log::trace!("Config -> Play");
self.send_packet(&SAcknowledgeFinishConfig).await; self.send_packet(&SAcknowledgeFinishConfig).await;
self.connection_state.store(ConnectionState::Play); self.connection_state.store(ConnectionState::Play);
@ -283,21 +495,58 @@ impl Client {
async fn handle_play_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> { async fn handle_play_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> {
let bytebuf = &packet.payload[..]; let bytebuf = &packet.payload[..];
match packet.id { match packet.id {
CKeepAlive::PACKET_ID => { id if id == CKeepAlive::PACKET_ID => {
let packet = CKeepAlive::read(bytebuf)?; let packet = CKeepAlive::read(bytebuf)?;
self.send_packet(&SKeepAlive { self.send_packet(&SKeepAlive {
keep_alive_id: packet.keep_alive_id, keep_alive_id: packet.keep_alive_id,
}) })
.await; .await;
} }
CPlayerPosition::PACKET_ID => { // TODO
// id if id == CEntityVelocity::PACKET_ID => {
// let packet = CEntityVelocity::read(bytebuf)?;
// if packet.entity_id.0 == self.entity_id.load(Ordering::Relaxed) as i32 {
// self.velocity_x.store(packet.velocity.0.x as f64 / 8000.0);
// self.velocity_y.store(packet.velocity.0.y as f64 / 8000.0);
// self.velocity_z.store(packet.velocity.0.z as f64 / 8000.0);
// }
// }
id if id == CPlayerPosition::PACKET_ID => {
let packet = CPlayerPosition::read(bytebuf)?; let packet = CPlayerPosition::read(bytebuf)?;
self.current_yaw.store(packet.yaw);
self.current_pitch.store(packet.pitch);
let x = packet.position.x;
let y = packet.position.y;
let z = packet.position.z;
self.current_x.store(x);
self.current_y.store(y);
self.current_z.store(z);
self.start_x.store(x);
self.start_z.store(z);
self.target_x.store(x);
self.target_z.store(z);
self.move_progress.store(1.0);
self.move_cooldown
.store(rand::random_range(40..100), Ordering::Relaxed);
self.send_packet(&SConfirmTeleport { self.send_packet(&SConfirmTeleport {
teleport_id: packet.teleport_id, teleport_id: packet.teleport_id,
}) })
.await; .await;
self.is_loaded.store(true, Ordering::Relaxed);
} }
CPlayDisconnect::PACKET_ID => { id if id == CLogin::PACKET_ID => {
// TODO
// let packet = CLogin::read(bytebuf)?;
// self.entity_id.store(packet.entity_id, Ordering::Relaxed);
self.send_packet(&SPlayerLoaded).await;
}
id if id == CPlayDisconnect::PACKET_ID => {
log::error!("Kicking in Play State"); log::error!("Kicking in Play State");
self.close().await; self.close().await;
} }

View File

@ -1,4 +1,4 @@
use std::{net::SocketAddr, sync::Arc, thread::sleep, time::Duration}; use std::{net::SocketAddr, sync::Arc, time::Duration};
use clap::Parser; use clap::Parser;
use client::Client; use client::Client;
@ -17,31 +17,39 @@ pub struct Args {
pub delay: u64, pub delay: u64,
#[arg(short, long, default_value_t = 5000)] #[arg(short, long, default_value_t = 5000)]
pub timeout: u64, pub timeout: u64,
#[arg(long)]
#[arg(long, default_value = "Please do not spam!")]
pub spam_message: Option<String>, pub spam_message: Option<String>,
#[arg(long, default_value_t = 210)] #[arg(long, default_value_t = 150)]
pub spam_message_delay: u32, pub spam_message_delay_min: u32,
#[arg(long, default_value_t = 250)]
pub spam_message_delay_max: u32,
#[arg(long, default_value_t = true)]
pub enable_rotation: bool,
#[arg(long, default_value_t = true)]
pub enable_swing: bool,
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
simple_logger::init_with_level(log::Level::Info).unwrap(); simple_logger::init_with_level(log::Level::Info).unwrap();
let args = Args::parse(); let args = Arc::new(Args::parse());
let address = args.ip.parse::<SocketAddr>().unwrap(); let address = args.ip.parse::<SocketAddr>().unwrap();
let bot_count = args.count;
log::info!( log::info!(
"{} Bots will Join {} with a delay of {}ms", "{} Bots will Join {} with a delay of {}ms",
args.count, bot_count,
address, address,
args.delay args.delay
); );
let mut join_handles = Vec::with_capacity(args.count as usize); let timeout_dur = Duration::from_millis(args.timeout);
for i in 0..args.count { let timeout_delay = Duration::from_millis(args.delay);
sleep(Duration::from_millis(args.delay));
let stream_result = timeout( let mut join_handles = Vec::with_capacity(bot_count as usize);
Duration::from_millis(args.timeout), for i in 0..bot_count {
TcpStream::connect(address), tokio::time::sleep(timeout_delay).await;
) let stream_result = timeout(timeout_dur, TcpStream::connect(address)).await;
.await;
let stream = match stream_result { let stream = match stream_result {
Ok(Ok(s)) => s, Ok(Ok(s)) => s,
@ -54,25 +62,31 @@ async fn main() {
"Bot {} connection to {} timed out after {}ms", "Bot {} connection to {} timed out after {}ms",
i + 1, i + 1,
address, address,
args.timeout bot_count
); );
continue; continue;
} }
}; };
let client = Arc::new(Client::new(stream)); let client = Arc::new(Client::new(stream));
let message = Arc::new(args.spam_message.clone());
client.join_server(address, format!("BOT_{i}")).await; client.join_server(address, format!("BOT_{i}")).await;
let cloned_args = args.clone();
let join_handle = tokio::spawn(async move { let join_handle = tokio::spawn(async move {
let mut tick_interval = tokio::time::interval(Duration::from_millis(5));
tick_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop { loop {
if !client.process_packets().await { tokio::select! {
break; res = client.process_packets() => {
if !res { break; }
}
_ = tick_interval.tick() => {
client.tick(&cloned_args).await;
}
} }
client.tick(message.clone(), args.spam_message_delay).await;
} }
}); });
join_handles.push(join_handle); join_handles.push(join_handle);
log::info!("{}/{} Bots Joined", i + 1, args.count); log::info!("{}/{} Bots Joined", i + 1, bot_count);
} }
// Graceful shutdown on Ctrl+C // Graceful shutdown on Ctrl+C
tokio::signal::ctrl_c().await.unwrap(); tokio::signal::ctrl_c().await.unwrap();