more things
This commit is contained in:
parent
e1ccb6ec7d
commit
162d337769
12 changed files with 407 additions and 110 deletions
226
Cargo.lock
generated
226
Cargo.lock
generated
|
@ -17,6 +17,15 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -38,6 +47,12 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -62,6 +77,37 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"time",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan-reporting"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||||
|
dependencies = [
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -71,6 +117,50 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxx"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cxxbridge-flags",
|
||||||
|
"cxxbridge-macro",
|
||||||
|
"link-cplusplus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxx-build"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"codespan-reporting",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"scratch",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-flags"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-macro"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -157,6 +247,30 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||||
|
dependencies = [
|
||||||
|
"cxx",
|
||||||
|
"cxx-build",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -194,12 +308,30 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.138"
|
version = "0.2.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "link-cplusplus"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -262,6 +394,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -320,6 +462,7 @@ dependencies = [
|
||||||
name = "quectocraft"
|
name = "quectocraft"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"hematite-nbt",
|
"hematite-nbt",
|
||||||
"log",
|
"log",
|
||||||
|
@ -381,6 +524,12 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scratch"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.148"
|
version = "1.0.148"
|
||||||
|
@ -432,12 +581,29 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
|
@ -450,6 +616,66 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -13,3 +13,4 @@ mlua = { version = "0.8", features = ["lua54", "macros", "serialize"] }
|
||||||
uuid = "1.2"
|
uuid = "1.2"
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
|
chrono = "0.4"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local plugin = {
|
local plugin = {
|
||||||
id = "chat",
|
id = "mcchat",
|
||||||
name = "Chat",
|
name = "MCChat",
|
||||||
description = "Provides Minecraft-style chat. Messages sent by one client will be broadcasted to every client.",
|
description = "Provides Minecraft-style chat. Messages sent by one client will be broadcasted to every client.",
|
||||||
authors = { "trimill" },
|
authors = { "trimill" },
|
||||||
version = "0.1.0",
|
version = "0.1.0",
|
||||||
|
@ -10,7 +10,7 @@ local logger = nil
|
||||||
|
|
||||||
function plugin.init()
|
function plugin.init()
|
||||||
logger = server.initLogger(plugin)
|
logger = server.initLogger(plugin)
|
||||||
logger.info("Loaded chat")
|
logger.info("MCChat version " .. plugin.version)
|
||||||
end
|
end
|
||||||
|
|
||||||
function plugin.playerJoin(name)
|
function plugin.playerJoin(name)
|
33
src/main.rs
33
src/main.rs
|
@ -1,5 +1,8 @@
|
||||||
use std::time::{Duration, SystemTime};
|
use std::borrow::Cow;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use log::info;
|
use log::info;
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
|
@ -13,16 +16,34 @@ mod network;
|
||||||
pub const VERSION: &'static str = std::env!("CARGO_PKG_VERSION");
|
pub const VERSION: &'static str = std::env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
env_logger::Builder::from_env(
|
||||||
|
Env::default().default_filter_or("info")
|
||||||
|
).format(|buf, record| {
|
||||||
|
|
||||||
info!("quectocraft version {}", VERSION);
|
let now = Utc::now().format("%Y-%m-%d %H:%M:%S");
|
||||||
|
let mut target = Cow::Borrowed(record.target());
|
||||||
|
if target.starts_with("quectocraft") {
|
||||||
|
target = Cow::Owned(record.target().replacen("quectocraft", "qc", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = match record.level() {
|
||||||
|
log::Level::Error => "\x1b[31m",
|
||||||
|
log::Level::Warn => "\x1b[33m",
|
||||||
|
log::Level::Info => "\x1b[32m",
|
||||||
|
log::Level::Debug => "\x1b[37m",
|
||||||
|
log::Level::Trace => "\x1b[37m",
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(buf, "\x1b[90m[\x1b[37m{} {color}{}\x1b[37m {}\x1b[90m]\x1b[0m {}", now, record.level(), target, record.args())
|
||||||
|
}).init();
|
||||||
|
|
||||||
|
|
||||||
|
info!("Starting Quectocraft version {}", VERSION);
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let mut plugins = Plugins::new(&lua).expect("Error initializing lua environment");
|
let mut plugins = Plugins::new(&lua).expect("Error initializing lua environment");
|
||||||
std::fs::create_dir_all("plugins").expect("couldn't create the plugins directory");
|
std::fs::create_dir_all("plugins").expect("Couldn't create the plugins directory");
|
||||||
plugins.load_plugins();
|
plugins.load_plugins();
|
||||||
|
|
||||||
info!("{} plugins loaded", plugins.count());
|
|
||||||
|
|
||||||
let mut server = NetworkServer::new("127.0.0.1:25565".to_owned(), plugins);
|
let mut server = NetworkServer::new("127.0.0.1:25565".to_owned(), plugins);
|
||||||
let sleep_dur = Duration::from_millis(5);
|
let sleep_dur = Duration::from_millis(5);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{net::{TcpStream, Shutdown}, thread, io::Write, sync::mpsc::{Receiver, Sender, TryRecvError}, time::Duration};
|
use std::{net::{TcpStream, Shutdown}, thread, io::Write, sync::mpsc::{Receiver, Sender, TryRecvError}, time::Duration};
|
||||||
|
|
||||||
use log::info;
|
use log::debug;
|
||||||
|
|
||||||
use crate::{protocol::{data::{PacketDecoder}, serverbound::*, clientbound::*, NetworkState}};
|
use crate::{protocol::{data::{PacketDecoder}, serverbound::*, clientbound::*, NetworkState}};
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ impl NetworkClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
info!("closed connection id {}", self.id);
|
debug!("Closed connection id {}", self.id);
|
||||||
let _ = self.stream.shutdown(Shutdown::Both);
|
let _ = self.stream.shutdown(Shutdown::Both);
|
||||||
self.closed = true;
|
self.closed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{net::TcpListener, thread, sync::mpsc::{Receiver, Sender, channel}, collections::HashSet};
|
use std::{net::TcpListener, thread, sync::mpsc::{Receiver, Sender, channel}, collections::HashSet};
|
||||||
|
|
||||||
use log::{info, warn, trace, debug, error};
|
use log::{info, warn, debug};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{protocol::{data::PacketEncoder, serverbound::*, clientbound::*}, plugins::{Plugins, Response}, VERSION};
|
use crate::{protocol::{data::PacketEncoder, serverbound::*, clientbound::*}, plugins::{Plugins, Response}, VERSION};
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ pub struct NetworkServer<'lua> {
|
||||||
impl <'lua> NetworkServer<'lua> {
|
impl <'lua> NetworkServer<'lua> {
|
||||||
pub fn new(addr: String, plugins: Plugins<'lua>) -> Self {
|
pub fn new(addr: String, plugins: Plugins<'lua>) -> Self {
|
||||||
let (send, recv) = channel();
|
let (send, recv) = channel();
|
||||||
info!("initializing plugins");
|
info!("Initializing plugins");
|
||||||
plugins.init();
|
plugins.init();
|
||||||
thread::spawn(move || Self::listen(&addr, send));
|
thread::spawn(move || Self::listen(&addr, send));
|
||||||
Self {
|
Self {
|
||||||
|
@ -26,11 +27,11 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listen(addr: &str, send_clients: Sender<NetworkClient>) {
|
fn listen(addr: &str, send_clients: Sender<NetworkClient>) {
|
||||||
info!("listening on {}", addr);
|
info!("Listening on {}", addr);
|
||||||
let listener = TcpListener::bind(addr).unwrap();
|
let listener = TcpListener::bind(addr).unwrap();
|
||||||
for (id, stream) in listener.incoming().enumerate() {
|
for (id, stream) in listener.incoming().enumerate() {
|
||||||
let stream = stream.unwrap();
|
let stream = stream.unwrap();
|
||||||
info!("connection from {} (id {})", stream.peer_addr().unwrap(), id);
|
debug!("Connection from {} (id {})", stream.peer_addr().unwrap(), id);
|
||||||
let stream_2 = stream.try_clone().unwrap();
|
let stream_2 = stream.try_clone().unwrap();
|
||||||
let (send, recv) = channel();
|
let (send, recv) = channel();
|
||||||
thread::spawn(|| NetworkClient::listen(stream_2, send));
|
thread::spawn(|| NetworkClient::listen(stream_2, send));
|
||||||
|
@ -97,16 +98,6 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
|
|
||||||
fn handle_plugin_response(&mut self, response: Response) -> std::io::Result<()> {
|
fn handle_plugin_response(&mut self, response: Response) -> std::io::Result<()> {
|
||||||
match response {
|
match response {
|
||||||
Response::Log { level, origin, message } => {
|
|
||||||
match level {
|
|
||||||
0 => trace!(target: &origin, "{}", message),
|
|
||||||
1 => debug!(target: &origin, "{}", message),
|
|
||||||
2 => info!(target: &origin, "{}", message),
|
|
||||||
3 => warn!(target: &origin, "{}", message),
|
|
||||||
4 => error!(target: &origin, "{}", message),
|
|
||||||
_ => warn!("unknown log level: {}", level)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Response::Message { player, message } => {
|
Response::Message { player, message } => {
|
||||||
for client in self.clients.iter_mut() {
|
for client in self.clients.iter_mut() {
|
||||||
if let Some(p) = &client.player {
|
if let Some(p) = &client.player {
|
||||||
|
@ -124,6 +115,15 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Response::Disconnect { player, reason } => {
|
||||||
|
for client in self.clients.iter_mut() {
|
||||||
|
if let Some(pl) = &client.player {
|
||||||
|
if pl.name == player || pl.uuid.to_string() == player {
|
||||||
|
client.send_packet(ClientBoundPacket::Disconnect(reason.clone()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
fn handle_packet(&mut self, client: &mut NetworkClient, packet: ServerBoundPacket) -> std::io::Result<()> {
|
fn handle_packet(&mut self, client: &mut NetworkClient, packet: ServerBoundPacket) -> std::io::Result<()> {
|
||||||
match packet {
|
match packet {
|
||||||
ServerBoundPacket::Ignored(_) => (),
|
ServerBoundPacket::Ignored(_) => (),
|
||||||
ServerBoundPacket::Unknown(id) => warn!("unknown: {}", id),
|
ServerBoundPacket::Unknown(id) => warn!("Unknown packet: {}", id),
|
||||||
ServerBoundPacket::Handshake(_) => (),
|
ServerBoundPacket::Handshake(_) => (),
|
||||||
ServerBoundPacket::StatusRequest()
|
ServerBoundPacket::StatusRequest()
|
||||||
=> client.send_packet(ClientBoundPacket::StatusResponse(
|
=> client.send_packet(ClientBoundPacket::StatusResponse(
|
||||||
|
@ -142,17 +142,22 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
ServerBoundPacket::LoginStart(login_start) => {
|
ServerBoundPacket::LoginStart(login_start) => {
|
||||||
client.player = Some(Player {
|
if self.clients.iter().filter_map(|x| x.player.as_ref()).any(|x| x.uuid == login_start.uuid) {
|
||||||
name: login_start.name.clone(),
|
client.send_packet(ClientBoundPacket::LoginDisconnect(json!({"translate": "multiplayer.disconnect.duplicate_login"})))?;
|
||||||
uuid: login_start.uuid.unwrap(),
|
client.close();
|
||||||
});
|
} else {
|
||||||
client.play = true;
|
client.player = Some(Player {
|
||||||
client.send_packet(ClientBoundPacket::LoginSuccess(LoginSuccess {
|
name: login_start.name.clone(),
|
||||||
name: login_start.name,
|
uuid: login_start.uuid,
|
||||||
uuid: login_start.uuid.unwrap(),
|
});
|
||||||
}))?;
|
client.play = true;
|
||||||
self.plugins.player_join(client.player.as_ref().unwrap());
|
client.send_packet(ClientBoundPacket::LoginSuccess(LoginSuccess {
|
||||||
self.post_login(client)?;
|
name: login_start.name,
|
||||||
|
uuid: login_start.uuid,
|
||||||
|
}))?;
|
||||||
|
self.plugins.player_join(client.player.as_ref().unwrap());
|
||||||
|
self.post_login(client)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ServerBoundPacket::ChatMessage(msg) => {
|
ServerBoundPacket::ChatMessage(msg) => {
|
||||||
self.plugins.chat_message(client.player.as_ref().unwrap(), &msg.message);
|
self.plugins.chat_message(client.player.as_ref().unwrap(), &msg.message);
|
||||||
|
@ -187,11 +192,10 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
channel: "minecraft:brand".to_owned(),
|
channel: "minecraft:brand".to_owned(),
|
||||||
data: {
|
data: {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
data.write_string(32767, &format!("quectocraft {}", VERSION));
|
data.write_string(32767, &format!("Quectocraft {}", VERSION));
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}))?;
|
}))?;
|
||||||
client.send_packet(ClientBoundPacket::PlayerAbilities(0x0d, 0.05, 0.1))?;
|
|
||||||
let mut chunk_data: Vec<u8> = Vec::new();
|
let mut chunk_data: Vec<u8> = Vec::new();
|
||||||
for _ in 0..(384 / 16) {
|
for _ in 0..(384 / 16) {
|
||||||
// number of non-air blocks
|
// number of non-air blocks
|
||||||
|
@ -224,6 +228,8 @@ impl <'lua> NetworkServer<'lua> {
|
||||||
teleport_id: 0,
|
teleport_id: 0,
|
||||||
dismount: false
|
dismount: false
|
||||||
}))?;
|
}))?;
|
||||||
|
// TODO why doesn't this work with quilt?
|
||||||
|
// client.send_packet(ClientBoundPacket::PlayerAbilities(0x0f, 0.05, 0.1))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
70
src/plugins/init_lua.rs
Normal file
70
src/plugins/init_lua.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use log::{info, warn, trace, error, debug};
|
||||||
|
use mlua::{Lua, chunk};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn init<'lua>(lua: &'lua Lua) -> Result<(), mlua::Error> {
|
||||||
|
macro_rules! log_any {
|
||||||
|
($level:tt) => {
|
||||||
|
lua.create_function(|_, args: (String, String)| {
|
||||||
|
$level!(target: &args.0, "{}", args.1);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let log_trace = log_any!(trace)?;
|
||||||
|
let log_debug = log_any!(debug)?;
|
||||||
|
let log_info = log_any!(info)?;
|
||||||
|
let log_warn = log_any!(warn)?;
|
||||||
|
let log_error = log_any!(error)?;
|
||||||
|
lua.load(chunk!{
|
||||||
|
server = { players = {} }
|
||||||
|
_qc = { responses = {} }
|
||||||
|
function server.initLogger(plugin)
|
||||||
|
local id = "pl::" .. assert(plugin["id"])
|
||||||
|
return {
|
||||||
|
trace = function(msg) $log_trace(id, msg) end,
|
||||||
|
debug = function(msg) $log_debug(id, msg) end,
|
||||||
|
info = function(msg) $log_info(id, msg) end,
|
||||||
|
warn = function(msg) $log_warn(id, msg) end,
|
||||||
|
error = function(msg) $log_error(id, msg) end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function to_chat(message, default)
|
||||||
|
if message == nil then
|
||||||
|
if default ~= nil then
|
||||||
|
return default
|
||||||
|
else
|
||||||
|
error("message must be a string or table")
|
||||||
|
end
|
||||||
|
elseif type(message) == "table" then
|
||||||
|
return message
|
||||||
|
elseif type(message) == "string" then
|
||||||
|
return { text = message }
|
||||||
|
elseif default == nil then
|
||||||
|
error("message must be a string or table")
|
||||||
|
else
|
||||||
|
error("message must be a string, table, or nil for the default message")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.sendMessage(player, message)
|
||||||
|
if type(player) ~= "string" then
|
||||||
|
error("player must be a string")
|
||||||
|
end
|
||||||
|
local message = assert(to_chat(message))
|
||||||
|
table.insert(_qc.responses, {type = "message", player = player, message = message})
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.broadcast(message)
|
||||||
|
local message = assert(to_chat(message))
|
||||||
|
table.insert(_qc.responses, { type = "broadcast", message = message })
|
||||||
|
end
|
||||||
|
|
||||||
|
function server.disconnect(player, reason)
|
||||||
|
local reason = assert(to_chat(reason, { translate = "multiplayer.disconnect.generic" }))
|
||||||
|
table.insert(_qc.responses, { type = "disconnect", player = player, reason = reason })
|
||||||
|
end
|
||||||
|
}).exec()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{path::Path, fs::{self, read_dir}};
|
use std::fs::read_dir;
|
||||||
|
|
||||||
use log::warn;
|
use log::{warn, info};
|
||||||
use mlua::{Lua, Table, chunk, LuaSerdeExt};
|
use mlua::{Lua, Table, LuaSerdeExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -9,8 +9,21 @@ use crate::network::Player;
|
||||||
|
|
||||||
use self::plugin::Plugin;
|
use self::plugin::Plugin;
|
||||||
|
|
||||||
|
mod init_lua;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(tag="type")]
|
||||||
|
pub enum Response {
|
||||||
|
#[serde(rename = "message")]
|
||||||
|
Message { player: String, message: serde_json::Value },
|
||||||
|
#[serde(rename = "broadcast")]
|
||||||
|
Broadcast { message: serde_json::Value },
|
||||||
|
#[serde(rename = "disconnect")]
|
||||||
|
Disconnect { player: String, reason: serde_json::Value },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Plugins<'lua> {
|
pub struct Plugins<'lua> {
|
||||||
lua: &'lua Lua,
|
lua: &'lua Lua,
|
||||||
plugins: Vec<Plugin<'lua>>
|
plugins: Vec<Plugin<'lua>>
|
||||||
|
@ -18,47 +31,7 @@ pub struct Plugins<'lua> {
|
||||||
|
|
||||||
impl <'lua> Plugins<'lua> {
|
impl <'lua> Plugins<'lua> {
|
||||||
pub fn new(lua: &'lua Lua) -> Result<Self, mlua::Error> {
|
pub fn new(lua: &'lua Lua) -> Result<Self, mlua::Error> {
|
||||||
lua.load(chunk!{
|
init_lua::init(lua)?;
|
||||||
server = { players = {} }
|
|
||||||
_qc = { responses = {} }
|
|
||||||
function server.sendMessage(player, message)
|
|
||||||
if type(player) ~= "string" then
|
|
||||||
error("player must be a string")
|
|
||||||
end
|
|
||||||
if type(message) == "table" then
|
|
||||||
table.insert(_qc.responses, {type = "message", player = player, message = message})
|
|
||||||
elseif type(message) == "string" then
|
|
||||||
table.insert(_qc.responses, {type = "message", player = player, message = { text = message}})
|
|
||||||
else
|
|
||||||
error("message must be a string or table")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function server.broadcast(message)
|
|
||||||
if type(message) == "table" then
|
|
||||||
table.insert(_qc.responses, { type = "broadcast", message = message })
|
|
||||||
elseif type(message) == "string" then
|
|
||||||
table.insert(_qc.responses, { type = "broadcast", message = { text = message } })
|
|
||||||
else
|
|
||||||
error("message must be a string or table")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function server.initLogger(plugin)
|
|
||||||
local function log_for(level)
|
|
||||||
return function(message)
|
|
||||||
table.insert(_qc.responses, {
|
|
||||||
type = "log", origin = plugin.id, message = message, level = level
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
trace = log_for(0),
|
|
||||||
debug = log_for(1),
|
|
||||||
info = log_for(2),
|
|
||||||
warn = log_for(3),
|
|
||||||
error = log_for(4),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
}).exec()?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
lua,
|
lua,
|
||||||
plugins: Vec::new(),
|
plugins: Vec::new(),
|
||||||
|
@ -78,18 +51,15 @@ impl <'lua> Plugins<'lua> {
|
||||||
};
|
};
|
||||||
let pl = Plugin::load(&path, &self.lua).expect("error loading plugin");
|
let pl = Plugin::load(&path, &self.lua).expect("error loading plugin");
|
||||||
self.plugins.push(pl);
|
self.plugins.push(pl);
|
||||||
|
info!("Loaded plugin '{}'", file.file_name().to_string_lossy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.plugins.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_responses(&self) -> Vec<Response> {
|
pub fn get_responses(&self) -> Vec<Response> {
|
||||||
match self.get_responses_inner() {
|
match self.get_responses_inner() {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("error getting responses: {}", e);
|
warn!("Error getting responses: {}", e);
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,14 +134,3 @@ impl <'lua> Plugins<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(tag="type")]
|
|
||||||
pub enum Response {
|
|
||||||
#[serde(rename = "log")]
|
|
||||||
Log { level: i32, origin: String, message: String },
|
|
||||||
#[serde(rename = "message")]
|
|
||||||
Message { player: String, message: serde_json::Value },
|
|
||||||
#[serde(rename = "broadcast")]
|
|
||||||
Broadcast { message: serde_json::Value },
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{path::{PathBuf, Path}, str::FromStr};
|
use std::path::Path;
|
||||||
|
|
||||||
use mlua::{Function, Table, Lua};
|
use mlua::{Function, Table, Lua};
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,7 @@ impl ChunkData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ClientBoundPacket {
|
pub enum ClientBoundPacket {
|
||||||
// status
|
// status
|
||||||
|
@ -142,6 +143,7 @@ pub enum ClientBoundPacket {
|
||||||
PingResponse(i64),
|
PingResponse(i64),
|
||||||
// login
|
// login
|
||||||
LoginSuccess(LoginSuccess),
|
LoginSuccess(LoginSuccess),
|
||||||
|
LoginDisconnect(serde_json::Value),
|
||||||
// play
|
// play
|
||||||
LoginPlay(LoginPlay),
|
LoginPlay(LoginPlay),
|
||||||
PluginMessage(PluginMessage),
|
PluginMessage(PluginMessage),
|
||||||
|
@ -149,6 +151,7 @@ pub enum ClientBoundPacket {
|
||||||
ChunkData(ChunkData),
|
ChunkData(ChunkData),
|
||||||
KeepAlive(i64),
|
KeepAlive(i64),
|
||||||
PlayerAbilities(i8, f32, f32),
|
PlayerAbilities(i8, f32, f32),
|
||||||
|
Disconnect(serde_json::Value),
|
||||||
SystemChatMessage(serde_json::Value, bool),
|
SystemChatMessage(serde_json::Value, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +159,7 @@ impl ClientBoundPacket {
|
||||||
pub fn encode(self) -> Vec<u8> {
|
pub fn encode(self) -> Vec<u8> {
|
||||||
let mut packet = Vec::new();
|
let mut packet = Vec::new();
|
||||||
match self {
|
match self {
|
||||||
|
// Status
|
||||||
Self::StatusResponse(status) => {
|
Self::StatusResponse(status) => {
|
||||||
packet.write_string(32767, &status);
|
packet.write_string(32767, &status);
|
||||||
finalize_packet(packet, 0)
|
finalize_packet(packet, 0)
|
||||||
|
@ -164,10 +168,24 @@ impl ClientBoundPacket {
|
||||||
packet.write_long(n);
|
packet.write_long(n);
|
||||||
finalize_packet(packet, 1)
|
finalize_packet(packet, 1)
|
||||||
},
|
},
|
||||||
|
// Login
|
||||||
|
Self::LoginDisconnect(message) => {
|
||||||
|
packet.write_string(262144, &message.to_string());
|
||||||
|
finalize_packet(packet, 0)
|
||||||
|
}
|
||||||
Self::LoginSuccess(login_success) => {
|
Self::LoginSuccess(login_success) => {
|
||||||
login_success.encode(&mut packet);
|
login_success.encode(&mut packet);
|
||||||
finalize_packet(packet, 2)
|
finalize_packet(packet, 2)
|
||||||
}
|
}
|
||||||
|
// Play
|
||||||
|
Self::Disconnect(message) => {
|
||||||
|
packet.write_string(262144, &message.to_string());
|
||||||
|
finalize_packet(packet, 25)
|
||||||
|
}
|
||||||
|
Self::LoginPlay(login_play) => {
|
||||||
|
login_play.encode(&mut packet);
|
||||||
|
finalize_packet(packet, 37)
|
||||||
|
}
|
||||||
Self::PluginMessage(plugin_message) => {
|
Self::PluginMessage(plugin_message) => {
|
||||||
plugin_message.encode(&mut packet);
|
plugin_message.encode(&mut packet);
|
||||||
finalize_packet(packet, 22)
|
finalize_packet(packet, 22)
|
||||||
|
@ -176,10 +194,6 @@ impl ClientBoundPacket {
|
||||||
sync_player_position.encode(&mut packet);
|
sync_player_position.encode(&mut packet);
|
||||||
finalize_packet(packet, 57)
|
finalize_packet(packet, 57)
|
||||||
}
|
}
|
||||||
Self::LoginPlay(login_play) => {
|
|
||||||
login_play.encode(&mut packet);
|
|
||||||
finalize_packet(packet, 37)
|
|
||||||
}
|
|
||||||
Self::ChunkData(chunk_data) => {
|
Self::ChunkData(chunk_data) => {
|
||||||
chunk_data.encode(&mut packet);
|
chunk_data.encode(&mut packet);
|
||||||
finalize_packet(packet, 33)
|
finalize_packet(packet, 33)
|
||||||
|
|
|
@ -136,6 +136,7 @@ pub struct PacketDecoder {
|
||||||
packet_id: i32,
|
packet_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
impl PacketDecoder {
|
impl PacketDecoder {
|
||||||
pub fn decode(read: &mut impl Read) -> Result<PacketDecoder, std::io::Error> {
|
pub fn decode(read: &mut impl Read) -> Result<PacketDecoder, std::io::Error> {
|
||||||
let size = read_varint(read)? as usize;
|
let size = read_varint(read)? as usize;
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct SigData {
|
||||||
pub struct LoginStart {
|
pub struct LoginStart {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub sig_data: Option<SigData>,
|
pub sig_data: Option<SigData>,
|
||||||
pub uuid: Option<Uuid>,
|
pub uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoginStart {
|
impl LoginStart {
|
||||||
|
@ -49,11 +49,10 @@ impl LoginStart {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let has_uuid = decoder.read_bool();
|
let has_uuid = decoder.read_bool();
|
||||||
let uuid = if has_uuid {
|
if !has_uuid {
|
||||||
Some(decoder.read_uuid())
|
panic!("Client didn't supply UUID");
|
||||||
} else {
|
}
|
||||||
None
|
let uuid = decoder.read_uuid();
|
||||||
};
|
|
||||||
Self { name, sig_data, uuid }
|
Self { name, sig_data, uuid }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue