feat: add replaymod compatible recorder
This commit is contained in:
parent
1a2af8b7aa
commit
caec5fa7f8
218
Cargo.lock
generated
218
Cargo.lock
generated
@ -143,6 +143,15 @@ version = "1.0.96"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@ -952,6 +961,25 @@ version = "1.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.13+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "castaway"
|
name = "castaway"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@ -1178,6 +1206,12 @@ dependencies = [
|
|||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -1187,6 +1221,21 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "3.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||||
|
dependencies = [
|
||||||
|
"crc-catalog",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -1253,6 +1302,12 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate64"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.9"
|
version = "0.7.9"
|
||||||
@ -1264,6 +1319,26 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -1381,6 +1456,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"azalea",
|
"azalea",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"built",
|
"built",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1393,7 +1469,10 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"mlua",
|
"mlua",
|
||||||
"ncr",
|
"ncr",
|
||||||
|
"parking_lot",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2159,6 +2238,12 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lockfree-object-pool"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.26"
|
version = "0.4.26"
|
||||||
@ -2174,6 +2259,16 @@ dependencies = [
|
|||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lzma-rs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2381,6 +2476,12 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-format"
|
name = "num-format"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@ -2495,6 +2596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
|
"hmac",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2611,6 +2713,12 @@ dependencies = [
|
|||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@ -3100,6 +3208,12 @@ dependencies = [
|
|||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd_cesu8"
|
name = "simd_cesu8"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -3328,6 +3442,25 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
@ -4050,6 +4183,20 @@ name = "zeroize"
|
|||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerovec"
|
||||||
@ -4072,3 +4219,74 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "2.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b280484c454e74e5fff658bbf7df8fdbe7a07c6b2de4a53def232c15ef138f3a"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"arbitrary",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"deflate64",
|
||||||
|
"displaydoc",
|
||||||
|
"flate2",
|
||||||
|
"hmac",
|
||||||
|
"indexmap 2.7.1",
|
||||||
|
"lzma-rs",
|
||||||
|
"memchr",
|
||||||
|
"pbkdf2",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"thiserror 2.0.11",
|
||||||
|
"time",
|
||||||
|
"zeroize",
|
||||||
|
"zopfli",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"lockfree-object-pool",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.14+zstd.1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
@ -22,6 +22,7 @@ built = { version = "0", features = ["git2"] }
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
azalea = { git = "https://github.com/azalea-rs/azalea.git" }
|
azalea = { git = "https://github.com/azalea-rs/azalea.git" }
|
||||||
bevy_app = "0"
|
bevy_app = "0"
|
||||||
|
bevy_ecs = "0"
|
||||||
bevy_log = "0"
|
bevy_log = "0"
|
||||||
clap = { version = "4", features = ["derive", "string"] }
|
clap = { version = "4", features = ["derive", "string"] }
|
||||||
console-subscriber = { version = "0", optional = true }
|
console-subscriber = { version = "0", optional = true }
|
||||||
@ -33,7 +34,10 @@ hyper-util = "0"
|
|||||||
log = { version = "0" }
|
log = { version = "0" }
|
||||||
mlua = { version = "0", features = ["async", "luajit", "send"] }
|
mlua = { version = "0", features = ["async", "luajit", "send"] }
|
||||||
ncr = { version = "0", features = ["cfb8", "ecb", "gcm"] }
|
ncr = { version = "0", features = ["cfb8", "ecb", "gcm"] }
|
||||||
|
parking_lot = "0"
|
||||||
|
serde_json = "1"
|
||||||
tokio = { version = "1", features = ["macros"] }
|
tokio = { version = "1", features = ["macros"] }
|
||||||
|
zip = "2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
console-subscriber = ["dep:console-subscriber"]
|
console-subscriber = ["dep:console-subscriber"]
|
||||||
|
@ -12,6 +12,7 @@ A Minecraft bot with Lua scripting support, written in Rust with [azalea](https:
|
|||||||
- Pathfinding (from azalea)
|
- Pathfinding (from azalea)
|
||||||
- Entity and chest interaction
|
- Entity and chest interaction
|
||||||
- NoChatReports encryption
|
- NoChatReports encryption
|
||||||
|
- Saving ReplayMod recordings
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ use crate::{
|
|||||||
State,
|
State,
|
||||||
commands::CommandSource,
|
commands::CommandSource,
|
||||||
http::serve,
|
http::serve,
|
||||||
lua::{self, direction::Direction, player::Player, vec3::Vec3},
|
lua::{client, direction::Direction, player::Player, vec3::Vec3},
|
||||||
particle,
|
particle,
|
||||||
|
replay::Recorder,
|
||||||
};
|
};
|
||||||
|
use anyhow::Context;
|
||||||
use azalea::{
|
use azalea::{
|
||||||
brigadier::exceptions::BuiltInExceptions::DispatcherUnknownCommand, prelude::*,
|
brigadier::exceptions::BuiltInExceptions::DispatcherUnknownCommand, prelude::*,
|
||||||
protocol::packets::game::ClientboundGamePacket,
|
protocol::packets::game::ClientboundGamePacket,
|
||||||
@ -12,7 +14,7 @@ use azalea::{
|
|||||||
use hyper::{server::conn::http1, service::service_fn};
|
use hyper::{server::conn::http1, service::service_fn};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use log::{debug, error, info, trace};
|
use log::{debug, error, info, trace};
|
||||||
use mlua::{Function, IntoLuaMulti, Table};
|
use mlua::{Error, Function, IntoLuaMulti, Table};
|
||||||
use ncr::utils::trim_header;
|
use ncr::utils::trim_header;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
@ -167,9 +169,23 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
|
|||||||
Event::Init => {
|
Event::Init => {
|
||||||
debug!("received initialize event");
|
debug!("received initialize event");
|
||||||
|
|
||||||
state.lua.globals().set(
|
let globals = state.lua.globals();
|
||||||
|
let lua_ecs = client.ecs.clone();
|
||||||
|
globals.set(
|
||||||
|
"finish_replay_recording",
|
||||||
|
state.lua.create_function_mut(move |_, (): ()| {
|
||||||
|
lua_ecs
|
||||||
|
.lock()
|
||||||
|
.remove_resource::<Recorder>()
|
||||||
|
.context("recording not active")
|
||||||
|
.map_err(Error::external)?
|
||||||
|
.finish()
|
||||||
|
.map_err(Error::external)
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
globals.set(
|
||||||
"client",
|
"client",
|
||||||
lua::client::Client {
|
client::Client {
|
||||||
inner: Some(client),
|
inner: Some(client),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
26
src/main.rs
26
src/main.rs
@ -7,6 +7,7 @@ mod events;
|
|||||||
mod http;
|
mod http;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod particle;
|
mod particle;
|
||||||
|
mod replay;
|
||||||
|
|
||||||
use arguments::Arguments;
|
use arguments::Arguments;
|
||||||
use azalea::{
|
use azalea::{
|
||||||
@ -21,7 +22,8 @@ use clap::Parser;
|
|||||||
use commands::{CommandSource, register};
|
use commands::{CommandSource, register};
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use futures_locks::RwLock;
|
use futures_locks::RwLock;
|
||||||
use mlua::{Function, Lua};
|
use mlua::{Function, Lua, Table};
|
||||||
|
use replay::{Recorder, plugin::RecordPlugin};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env,
|
env,
|
||||||
@ -37,10 +39,10 @@ type ListenerMap = Arc<RwLock<HashMap<String, Vec<(String, Function)>>>>;
|
|||||||
|
|
||||||
#[derive(Default, Clone, Component)]
|
#[derive(Default, Clone, Component)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
http_address: Option<SocketAddr>,
|
||||||
lua: Arc<Lua>,
|
lua: Arc<Lua>,
|
||||||
event_listeners: ListenerMap,
|
event_listeners: ListenerMap,
|
||||||
commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
|
commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
|
||||||
http_address: Option<SocketAddr>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -91,15 +93,33 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
let record_plugin = RecordPlugin {
|
||||||
|
recorder: Arc::new(parking_lot::Mutex::new(
|
||||||
|
if let Ok(options) = globals.get::<Table>("ReplayRecordingOptions")
|
||||||
|
&& let Ok(path) = options.get::<String>("path")
|
||||||
|
{
|
||||||
|
Some(Recorder::new(
|
||||||
|
path,
|
||||||
|
server.clone(),
|
||||||
|
options
|
||||||
|
.get::<bool>("ignore_compression")
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
};
|
||||||
let Err(error) = ClientBuilder::new_without_plugins()
|
let Err(error) = ClientBuilder::new_without_plugins()
|
||||||
.add_plugins(default_plugins)
|
.add_plugins(default_plugins)
|
||||||
|
.add_plugins(record_plugin)
|
||||||
.add_plugins(DefaultBotPlugins)
|
.add_plugins(DefaultBotPlugins)
|
||||||
.set_handler(events::handle_event)
|
.set_handler(events::handle_event)
|
||||||
.set_state(State {
|
.set_state(State {
|
||||||
|
http_address: args.http_address,
|
||||||
lua: Arc::new(lua),
|
lua: Arc::new(lua),
|
||||||
event_listeners,
|
event_listeners,
|
||||||
commands: Arc::new(commands),
|
commands: Arc::new(commands),
|
||||||
http_address: args.http_address,
|
|
||||||
})
|
})
|
||||||
.start(
|
.start(
|
||||||
if username.contains('@') {
|
if username.contains('@') {
|
||||||
|
88
src/replay/mod.rs
Normal file
88
src/replay/mod.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
pub mod plugin;
|
||||||
|
|
||||||
|
use crate::build_info;
|
||||||
|
use anyhow::Result;
|
||||||
|
use azalea::{
|
||||||
|
buf::AzaleaWriteVar,
|
||||||
|
prelude::Resource,
|
||||||
|
protocol::packets::{PROTOCOL_VERSION, ProtocolPacket, VERSION_NAME},
|
||||||
|
};
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::Write,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
use zip::{ZipWriter, write::SimpleFileOptions};
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct Recorder {
|
||||||
|
zip_writer: ZipWriter<File>,
|
||||||
|
start_time: u128,
|
||||||
|
server: String,
|
||||||
|
ignore_compression: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Recorder {
|
||||||
|
pub fn new(path: String, server: String, ignore_compression: bool) -> Result<Self> {
|
||||||
|
let mut zip_writer = ZipWriter::new(
|
||||||
|
File::options()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(path)?,
|
||||||
|
);
|
||||||
|
zip_writer.start_file("recording.tmcpr", SimpleFileOptions::default())?;
|
||||||
|
Ok(Self {
|
||||||
|
zip_writer,
|
||||||
|
start_time: SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(),
|
||||||
|
server,
|
||||||
|
ignore_compression,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(mut self) -> Result<()> {
|
||||||
|
self.zip_writer
|
||||||
|
.start_file("metaData.json", SimpleFileOptions::default())?;
|
||||||
|
self.zip_writer.write_all(
|
||||||
|
json!({
|
||||||
|
"singleplayer": false,
|
||||||
|
"serverName": self.server,
|
||||||
|
"duration": SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() - self.start_time,
|
||||||
|
"date": self.start_time,
|
||||||
|
"mcversion": VERSION_NAME,
|
||||||
|
"fileFormat": "MCPR",
|
||||||
|
"fileFormatVersion": 14,
|
||||||
|
"protocol": PROTOCOL_VERSION,
|
||||||
|
"generator": build_info::version_formatted(),
|
||||||
|
})
|
||||||
|
.to_string()
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
self.zip_writer.finish()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_timestamp(&self) -> Result<[u8; 4]> {
|
||||||
|
Ok(TryInto::<u32>::try_into(
|
||||||
|
SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() - self.start_time,
|
||||||
|
)?
|
||||||
|
.to_be_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_raw_packet(&mut self, raw_packet: &[u8]) -> Result<()> {
|
||||||
|
let mut data = Vec::from(self.get_timestamp()?);
|
||||||
|
data.extend(TryInto::<u32>::try_into(raw_packet.len())?.to_be_bytes());
|
||||||
|
data.extend(raw_packet);
|
||||||
|
self.zip_writer.write_all(&data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_packet<T: ProtocolPacket>(&mut self, packet: &T) -> Result<()> {
|
||||||
|
let mut raw_packet = Vec::new();
|
||||||
|
packet.id().azalea_write_var(&mut raw_packet)?;
|
||||||
|
packet.write(&mut raw_packet)?;
|
||||||
|
self.save_raw_packet(&raw_packet)
|
||||||
|
}
|
||||||
|
}
|
76
src/replay/plugin.rs
Normal file
76
src/replay/plugin.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use super::Recorder;
|
||||||
|
use azalea::{
|
||||||
|
ecs::{event::EventReader, system::Query},
|
||||||
|
packet_handling::{
|
||||||
|
configuration::ConfigurationEvent,
|
||||||
|
game::send_packet_events,
|
||||||
|
login::{LoginPacketEvent, process_packet_events},
|
||||||
|
},
|
||||||
|
protocol::packets::login::ClientboundLoginPacket,
|
||||||
|
raw_connection::RawConnection,
|
||||||
|
};
|
||||||
|
use bevy_app::{First, Plugin};
|
||||||
|
use bevy_ecs::{schedule::IntoSystemConfigs, system::ResMut};
|
||||||
|
use log::error;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct RecordPlugin {
|
||||||
|
pub recorder: Arc<Mutex<Option<Recorder>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for RecordPlugin {
|
||||||
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
|
if let Some(recorder) = self.recorder.lock().take() {
|
||||||
|
app.insert_resource(recorder);
|
||||||
|
}
|
||||||
|
app.add_systems(First, record_login_packets.before(process_packet_events))
|
||||||
|
.add_systems(First, record_configuration_packets)
|
||||||
|
.add_systems(First, record_game_packets.before(send_packet_events));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_login_packets(
|
||||||
|
recorder: Option<ResMut<Recorder>>,
|
||||||
|
mut events: EventReader<LoginPacketEvent>,
|
||||||
|
) {
|
||||||
|
if let Some(mut recorder) = recorder {
|
||||||
|
for event in events.read() {
|
||||||
|
if recorder.ignore_compression
|
||||||
|
&& let ClientboundLoginPacket::LoginCompression(_) = *event.packet
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(error) = recorder.save_packet(event.packet.as_ref()) {
|
||||||
|
error!("failed to record login packet: {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_configuration_packets(
|
||||||
|
recorder: Option<ResMut<Recorder>>,
|
||||||
|
mut events: EventReader<ConfigurationEvent>,
|
||||||
|
) {
|
||||||
|
if let Some(mut recorder) = recorder {
|
||||||
|
for event in events.read() {
|
||||||
|
if let Err(error) = recorder.save_packet(&event.packet) {
|
||||||
|
error!("failed to record configuration packet: {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_game_packets(recorder: Option<ResMut<Recorder>>, query: Query<&RawConnection>) {
|
||||||
|
if let Some(mut recorder) = recorder
|
||||||
|
&& let Ok(raw_conn) = query.get_single()
|
||||||
|
{
|
||||||
|
let queue = raw_conn.incoming_packet_queue();
|
||||||
|
for raw_packet in queue.lock().iter() {
|
||||||
|
if let Err(error) = recorder.save_raw_packet(raw_packet) {
|
||||||
|
error!("failed to record game packet: {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user