From d98e56d413cf41e87f2e30165731961968aa27aa Mon Sep 17 00:00:00 2001 From: illegitimate-egg Date: Sat, 1 Mar 2025 22:21:19 +0000 Subject: [PATCH] my computer is dying --- Cargo.lock | 240 +++++++++++++++++++++++++----- Cargo.toml | 8 +- src/command.rs | 82 ++++++++++ src/error.rs | 54 +++++++ src/extensions.rs | 271 ++++++++++++++++++++++++++++++++++ src/extensions/jimmy.rhai | 11 ++ src/extensions/ping-pong.rhai | 10 ++ src/main.rs | 18 ++- src/network.rs | 66 ++------- src/utils.rs | 26 ++-- src/world.rs | 7 +- 11 files changed, 685 insertions(+), 108 deletions(-) create mode 100644 src/command.rs create mode 100644 src/extensions.rs create mode 100644 src/extensions/jimmy.rhai create mode 100644 src/extensions/ping-pong.rhai diff --git a/Cargo.lock b/Cargo.lock index e724485..c26924a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,18 +8,41 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "const-random", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cfg-if" version = "1.0.0" @@ -42,6 +65,26 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -51,6 +94,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "ctrlc" version = "3.4.5" @@ -91,6 +140,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "itoa" version = "1.0.14" @@ -119,15 +177,20 @@ checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" name = "mcrizzledizzle" version = "0.1.0" dependencies = [ - "colored", "ctrlc", "flate2", - "lazy_static", "log", - "rand", + "regex", + "rhai", "simple_logger", ] +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "miniz_oxide" version = "0.8.5" @@ -149,12 +212,30 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +dependencies = [ + "spin", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -164,21 +245,27 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.93" @@ -198,33 +285,61 @@ dependencies = [ ] [[package]] -name = "rand" +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rhai" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce4d759a4729a655ddfdbb3ff6e77fb9eadd902dae12319455557796e435d2a6" dependencies = [ - "libc", - "rand_chacha", - "rand_core", + "ahash", + "bitflags", + "instant", + "no-std-compat", + "num-traits", + "once_cell", + "rhai_codegen", + "smallvec", + "smartstring", + "thin-vec", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rhai_codegen" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -259,6 +374,35 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "2.0.98" @@ -270,6 +414,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thin-vec" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" + [[package]] name = "time" version = "0.3.37" @@ -303,12 +453,27 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "unicode-ident" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -460,7 +625,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] diff --git a/Cargo.toml b/Cargo.toml index 0002865..46ca842 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,13 +4,15 @@ version = "0.1.0" edition = "2021" [dependencies] -colored = "2.1.0" ctrlc = "3.4.5" flate2 = "1.0.30" -lazy_static = "1.4.0" log = "0.4.26" -rand = "0.8.5" +regex = "1.11.1" [dependencies.simple_logger] version = "5.0.0" features = ["threads", "nightly"] + +[dependencies.rhai] +version = "1.21.0" +features = ["sync"] diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..327e3ec --- /dev/null +++ b/src/command.rs @@ -0,0 +1,82 @@ +use log::error; +use std::io::prelude::*; +use std::net::TcpStream; +use std::sync::{Arc, Mutex}; + +use crate::error::AppError; +use crate::extensions::Extensions; +use crate::player::{Player, SpecialPlayers}; +use crate::utils::*; + +pub fn handle_command( + stream: &mut TcpStream, + client_number: u8, + players_arc_clone: &Arc>, + extensions: &Arc, + command_string: &String, +) -> Result<(), AppError> { + let vectorized_command = command_string.split(" ").collect::>(); + match vectorized_command[0] { + "kick" => { + let mut players = players_arc_clone + .lock() + .map_err(|e| AppError::MutexPoisoned(e.to_string()))?; + for i in 0..players.len() { + if players[i].id != 255 { + if players[i].username == vectorized_command[1] { + let _ = &mut players[i] + .outgoing_data + .extend_from_slice(&client_disconnect("KICKED!")); + players[i].id = 255; + break; + } + } + } + } + "tp" => { + let players = players_arc_clone + .lock() + .map_err(|e| AppError::MutexPoisoned(e.to_string()))?; + for i in 0..players.len() { + if players[i].id != 255 { + if players[i].username == vectorized_command[1] { + let _ = &mut stream.write(&set_position_and_orientation( + SpecialPlayers::SelfPlayer as u8, + players[i].position_x, + players[i].position_y, + players[i].position_z, + players[i].yaw, + players[i].pitch, + )); + break; + } + } + } + } + _ => { + let found = match extensions.run_command(vectorized_command[0].to_string(), client_number) { + Ok(result) => result, + Err(error) => { + error!("Rhai plugin error: {}", error); + let _ = &mut stream.write(&send_chat_message( + SpecialPlayers::SelfPlayer as u8, + "".to_string(), + "&cAn internal error occured while processing this command".to_string(), + )); + return Ok(()); + } + }; + + if found { + return Ok(()); + } + + let _ = &mut stream.write(&send_chat_message( + SpecialPlayers::SelfPlayer as u8, + "".to_string(), + "&cUnkown command!".to_string(), + )); + } + } + Ok(()) +} diff --git a/src/error.rs b/src/error.rs index 3eaa6c2..558ef05 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,34 @@ use std::fmt; +use std::num::{ParseIntError, ParseFloatError, TryFromIntError}; +use std::sync::PoisonError; + +use rhai::EvalAltResult; #[derive(Debug)] pub enum AppError { IoError(std::io::Error), + RegexError(regex::Error), + ParseIntError(ParseIntError), + ParseFloatError(ParseFloatError), + TryFromIntError(TryFromIntError), + RhaiError(Box), + MutexPoisoned(String), InvalidWorldFile, + // InvalidExtensionVersion, } impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AppError::IoError(err) => write!(f, "IO Error: {}", err), + AppError::RegexError(err) => write!(f, "Extension Regex Error: {}", err), + AppError::ParseIntError(err) => write!(f, "Parse int error: {}", err), + AppError::ParseFloatError(err) => write!(f, "Parse float error: {}", err), + AppError::TryFromIntError(err) => write!(f, "Integer conversion error: {}", err), + AppError::RhaiError(err) => write!(f, "Rhai compilation error: {}", err), + AppError::MutexPoisoned(err) => write!(f, "Poisoned mutex: {}", err), AppError::InvalidWorldFile => write!(f, "Invalid world file"), + // AppError::InvalidExtensionVersion => write!(f, "Invalid extension version"), } } } @@ -20,3 +38,39 @@ impl From for AppError { AppError::IoError(err) } } + +impl From for AppError { + fn from(err: regex::Error) -> Self { + AppError::RegexError(err) + } +} + +impl From for AppError { + fn from(err: ParseIntError) -> Self { + AppError::ParseIntError(err) + } +} + +impl From for AppError { + fn from(err: ParseFloatError) -> Self { + AppError::ParseFloatError(err) + } +} + +impl From for AppError { + fn from(err: TryFromIntError) -> Self { + AppError::TryFromIntError(err) + } +} + +impl From> for AppError { + fn from(err: Box) -> Self { + AppError::RhaiError(err) + } +} + +impl From> for AppError { + fn from(err: PoisonError) -> Self { + AppError::MutexPoisoned(err.to_string()) + } +} diff --git a/src/extensions.rs b/src/extensions.rs new file mode 100644 index 0000000..1c7a795 --- /dev/null +++ b/src/extensions.rs @@ -0,0 +1,271 @@ +use log::{error, info}; +use regex::Regex; +use rhai::{CustomType, Engine, EvalAltResult, FnPtr, Scope, TypeBuilder, AST}; +use std::{ + collections::HashMap, + ffi::OsStr, + fs, + path::Path, + sync::{Arc, Mutex}, +}; + +use crate::{error::AppError, player::Player, utils::send_chat_message}; + +pub struct Extensions { + extensions: Vec, +} + +impl Extensions { + pub fn run_command(&self, key: String, player: u8) -> Result { + // Bool success + for extension in &self.extensions { + if let Some(key_value) = extension.commands.get(&key) { + key_value.call::<()>(&extension.engine, &extension.ast, (player,))?; + return Ok(true); + } + } + + Ok(false) + } +} + +pub struct Extension { + ast: AST, + engine: Engine, + commands: HashMap, + metadata: ExtensionMetadata, +} + +////// BEGIN RHAI DEFINITIONS ////// +#[derive(Debug, Clone, Eq, PartialEq, CustomType)] +#[rhai_type(name = "Version", extra = Self::build_extra)] +struct Version { + major: u16, + minor: u16, + patch: u16, + prerelease: String, + build: String, +} + +impl Version { + pub fn display(&self) -> String { + let mut base = format!("{}.{}.{}", self.major, self.minor, self.patch); + + if !self.prerelease.is_empty() { + base.push_str(&format!("-{}", self.prerelease).to_string()); + } + + if !self.build.is_empty() { + base.push_str(&format!("+{}", self.build).to_string()); + } + + base + } + + fn parse(version_string: String) -> Result> { + let Ok(re) = Regex::new( + r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$", + ) else { + return Err("Failed to create regex".into()); + }; + + let Some(version_parts) = re.captures(&version_string) else { + return Err("Invalid Extension Version".into()); + }; + + let mut prerelease: String = "".to_string(); + let mut build: String = "".to_string(); + + if version_parts.name("prerelease").is_some() { + prerelease = version_parts["prerelease"].to_string(); + } + if version_parts.name("buildmetadata").is_some() { + build = version_parts["buildmetadata"].to_string(); + } + + Ok(Version { + major: version_parts["major"].parse::().unwrap(), + minor: version_parts["minor"].parse::().unwrap(), + patch: version_parts["patch"].parse::().unwrap(), + prerelease, + build, + }) + } + + fn build_extra(builder: &mut TypeBuilder) { + // Register constructor function + builder.with_fn("Version", Self::parse); + } +} + +#[derive(Debug, Clone, Eq, PartialEq, CustomType)] +#[rhai_type(name = "Metadata", extra = Self::build_extra)] +struct ExtensionMetadata { + name: String, + version: Version, +} + +impl ExtensionMetadata { + fn new(name: String, version: Version) -> Self { + Self { name, version } + } + + fn build_extra(builder: &mut TypeBuilder) { + // Register constructor function + builder.with_fn("Metadata", Self::new); + } +} + +#[derive(Debug, Clone, CustomType)] +#[rhai_type(name = "Player")] +struct RhaiPlayer { + id: u8, +} + +#[derive(Debug, Clone, CustomType)] +#[rhai_type(name = "PlayersWrapper")] +pub struct PlayersWrapper(Arc>); + +impl PlayersWrapper { + pub fn new(players: Arc>) -> Self { + Self(players) + } + + pub fn send_message(&self, player: RhaiPlayer, message: String) -> Result<(), Box> { + let mut players = self.0.lock().unwrap(); + + players[player.id as usize] + .outgoing_data + .extend_from_slice(&send_chat_message(255, "".to_string(), message)); + + Ok(()) + } +} + +#[derive(Debug, Clone, CustomType)] +#[rhai_type(name = "Context", extra = Self::build_extra)] +struct Context { + #[rhai_type(skip)] + commands: HashMap, + #[rhai_type(skip)] + players: PlayersWrapper, +} + +impl Context { + fn new(players: PlayersWrapper) -> Self { + Self { + commands: HashMap::new(), + players, + } + } + + pub fn players(&mut self) -> PlayersWrapper { + self.players.clone() + } + + fn register_command(&mut self, name: String, callback: FnPtr) { + self.commands.insert(name, callback); + } + + fn build_extra(builder: &mut TypeBuilder) { + builder.with_fn("Context", Self::new); + builder.with_fn("register_command", Self::register_command); + } +} +////// END RHAI DEFINITIONS ////// + +impl Extensions { + pub fn init(players: PlayersWrapper) -> Result { + if !Path::new("./extensions/").exists() { + let _ = fs::create_dir("./extensions/"); + } + + let extensions_listing = fs::read_dir("./extensions")?; + + let mut extensions = Extensions { + extensions: Vec::new(), + }; + + for extension in extensions_listing { + let extension_path = extension?.path(); + + if extension_path.extension() != Some(OsStr::new("rhai")) { + break; + } + info!("Loading extension {}", extension_path.display()); + + let mut engine = Engine::new(); + engine.build_type::(); + engine.build_type::(); + engine.build_type::(); + engine.build_type::(); + engine.build_type::(); + + let ast = engine.compile_file(extension_path.clone().into())?; + let mut scope = Scope::new(); + + let extension_metadata = + match engine.call_fn::(&mut scope, &ast, "metadata", ()) { + Ok(result) => result, + Err(error) => { + error!( + "Rhai plugin with path {} missing critical section metadata! {}", + extension_path.display(), + error + ); + break; + } + }; + + let mut current_extension = Extension { + ast, + engine, + commands: HashMap::new(), + metadata: extension_metadata, + }; + + let ctx = match current_extension.engine.call_fn::( + &mut scope, + ¤t_extension.ast, + "init", + (PlayersWrapper::new(players.0.clone()),) + ) { + Ok(result) => result, + Err(error) => { + error!( + "Plugin {} failed to init: {}", + current_extension.metadata.name, error + ); + break; + } + }; + + for (key, value) in ctx.commands.iter() { + current_extension + .commands + .insert(key.to_string(), value.clone()); + } + + info!( + "Loaded {} v{}", + current_extension.metadata.name, + current_extension.metadata.version.display() + ); + + extensions.extensions.push(current_extension); + } + + for extension in &extensions.extensions { + for command_name in extension.commands.keys() { + info!( + "Extension {} v{} has reserved command: {}", + extension.metadata.name, + extension.metadata.version.display(), + command_name + ); + } + } + + Ok(extensions) + } +} diff --git a/src/extensions/jimmy.rhai b/src/extensions/jimmy.rhai new file mode 100644 index 0000000..383ae86 --- /dev/null +++ b/src/extensions/jimmy.rhai @@ -0,0 +1,11 @@ +fn metadata() { + Metadata("jimmy", Version("2.0.0-balls+tweaking")) +} + +fn init(players) { + let ctx = Context(players); + ctx.register_command("jimmy", |player| { + players.send_message("He's dead") + }); + ctx +} diff --git a/src/extensions/ping-pong.rhai b/src/extensions/ping-pong.rhai new file mode 100644 index 0000000..6c8a1c1 --- /dev/null +++ b/src/extensions/ping-pong.rhai @@ -0,0 +1,10 @@ +fn metadata() { + Metadata("ping pong", Version("1.0.0")) +} + +fn init() { + let ctx = Context(); + ctx.register_command("ping", |player| player.sendMessage("Pong! (you smell)")); + ctx.register_command("test", |player| player.sendMessage("test")); + ctx +} diff --git a/src/main.rs b/src/main.rs index af426e8..108eb92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,16 @@ use simple_logger::SimpleLogger; use std::net::{SocketAddr, TcpListener}; use std::sync::{Arc, Mutex}; +mod command; mod error; +mod extensions; mod network; mod player; mod utils; mod world; use error::AppError; +use extensions::{Extensions, PlayersWrapper}; use network::handle_client; use player::{Player, SpecialPlayers}; use world::World; @@ -39,15 +42,26 @@ fn run() -> Result<(), AppError> { ctrlc::set_handler(move || { println!(""); info!("SAVING"); - let _ = World::save(world_arc_clone_main_thread.clone()); // Fortnite save the world + let _ = World::save(world_arc_clone_main_thread.clone()).unwrap(); // Fortnite save the world std::process::exit(0); }) .expect("Error handling control C, save on exit will not work"); + let extensions = Arc::new(Extensions::init(PlayersWrapper::new(players_arc.clone()))?); + + info!("Server listening on {}", 25565); + for stream in listener.incoming() { let players_arc_clone = Arc::clone(&players_arc); let world_arc_clone = Arc::clone(&world_arc); - handle_client(stream?, thread_number, players_arc_clone, world_arc_clone); + let extensions_arc_clone = Arc::clone(&extensions); + handle_client( + stream?, + thread_number, + players_arc_clone, + world_arc_clone, + extensions_arc_clone, + ); thread_number = thread_number.wrapping_add(1); } Ok(()) diff --git a/src/network.rs b/src/network.rs index 41a4d30..1dfe98e 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,20 +1,23 @@ -use std::net::TcpStream; -use std::io::prelude::*; -use std::sync::{Arc, Mutex}; use log::{info, warn}; +use std::io::prelude::*; +use std::net::TcpStream; +use std::sync::{Arc, Mutex}; use std::thread; use std::thread::sleep; use std::time::Duration; -use crate::player::{Player, SpecialPlayers, PlayerStatus}; -use crate::world::World; +use crate::command::handle_command; +use crate::extensions::Extensions; +use crate::player::{Player, PlayerStatus, SpecialPlayers}; use crate::utils::*; +use crate::world::World; pub fn handle_client( mut stream: TcpStream, client_number: u8, players_arc_clone: Arc>, world_arc_clone: Arc>, + extensions: Arc, ) { thread::spawn(move || { info!("Thread initialized with player ID: {}", client_number); @@ -90,7 +93,7 @@ pub fn handle_client( current_player.pitch = 0; current_player.operator = true; - bomb_server_details(&mut stream, ¤t_player, &world_arc_clone); + let _ = bomb_server_details(&mut stream, ¤t_player, &world_arc_clone); for i in 0..immediate_join.len() { if immediate_join[i] { @@ -207,49 +210,13 @@ pub fn handle_client( // Uh oh, command time info!("{}", message_string); let remaning_command = String::from_iter(&message[1..message.len()]); - let vectorized_command = remaning_command.split(" ").collect::>(); - match vectorized_command[0] { - "kick" => { - let mut players = players_arc_clone.lock().unwrap(); - for i in 0..players.len() { - if players[i].id != 255 { - if players[i].username == vectorized_command[1] { - let _ = &mut players[i] - .outgoing_data - .extend_from_slice(&client_disconnect("KICKED!")); - players[i].id = 255; - break; - } - } - } - } - "tp" => { - let players = players_arc_clone.lock().unwrap(); - for i in 0..players.len() { - if players[i].id != 255 { - if players[i].username == vectorized_command[1] { - let _ = - &mut stream.write(&set_position_and_orientation( - SpecialPlayers::SelfPlayer as u8, - players[i].position_x, - players[i].position_y, - players[i].position_z, - players[i].yaw, - players[i].pitch, - )); - break; - } - } - } - } - _ => { - let _ = &mut stream.write(&send_chat_message( - SpecialPlayers::SelfPlayer as u8, - "".to_string(), - "&cUnkown command!".to_string(), - )); - } - } + let _ = handle_command( + &mut stream, + client_number, + &players_arc_clone, + &extensions, + &remaning_command, + ); } else { let mut players = players_arc_clone.lock().unwrap(); let sender: u8 = players[client_number as usize].id; @@ -344,4 +311,3 @@ pub fn handle_client( ); }); } - diff --git a/src/utils.rs b/src/utils.rs index 5ceb169..a3ee32e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,6 +5,7 @@ use std::io::prelude::*; use std::net::TcpStream; use std::sync::{Arc, Mutex}; +use crate::error::AppError; use crate::World; use crate::SpecialPlayers; use crate::Player; @@ -63,17 +64,17 @@ pub fn init_level() -> Vec { vec![0x02] } -pub fn finalize_level(world_arc_clone: &Arc>) -> Vec { +pub fn finalize_level(world_arc_clone: &Arc>) -> Result, AppError> { let mut ret_val: Vec = vec![]; ret_val.push(0x04); - let world_dat = world_arc_clone.lock().unwrap(); + let world_dat = world_arc_clone.lock()?; ret_val.append(&mut stream_write_short(world_dat.size_x).to_vec()); ret_val.append(&mut stream_write_short(world_dat.size_y).to_vec()); ret_val.append(&mut stream_write_short(world_dat.size_z).to_vec()); - ret_val + Ok(ret_val) } pub fn spawn_player( @@ -140,9 +141,9 @@ pub fn set_position_and_orientation( ret_val } -pub fn send_level_data(world_arc_clone: &Arc>) -> Vec { +pub fn send_level_data(world_arc_clone: &Arc>) -> Result, AppError> { let mut ret_val: Vec = vec![]; - let mut world_dat = world_arc_clone.lock().unwrap().data.clone(); + let mut world_dat = world_arc_clone.lock()?.data.clone(); // Big endian fold lmao world_dat.insert(0, ((world_dat.len() & 0xFF) >> 0) as u8); @@ -154,7 +155,7 @@ pub fn send_level_data(world_arc_clone: &Arc>) -> Vec { let mut world_dat_compressor = GzEncoder::new(Vec::new(), Compression::fast()); let _ = world_dat_compressor.write_all(&world_dat); - let world_dat_gzipped = world_dat_compressor.finish().unwrap(); + let world_dat_gzipped = world_dat_compressor.finish()?; let number_of_chunks = ((world_dat_gzipped.len() as f32) / 1024.0_f32).ceil() as usize; let mut current_chunk = 0; @@ -177,7 +178,7 @@ pub fn send_level_data(world_arc_clone: &Arc>) -> Vec { percentage = 100; } - ret_val.push(percentage.try_into().unwrap()); + ret_val.push(percentage.try_into()?); current_chunk += 1; } @@ -189,7 +190,7 @@ pub fn send_level_data(world_arc_clone: &Arc>) -> Vec { ret_val.push(0x03); ret_val.append(&mut stream_write_short( - remaining_chunk_size.try_into().unwrap(), + remaining_chunk_size.try_into()?, )); let mut remaining_data_buffer = [0u8; 1024]; @@ -205,23 +206,23 @@ pub fn send_level_data(world_arc_clone: &Arc>) -> Vec { "World transmission size: {}KiB", ret_val.len() as f32 / 1024.0 ); - ret_val + Ok(ret_val) } pub fn bomb_server_details( stream: &mut TcpStream, current_player: &Player, world_arc_clone: &Arc>, -) { +) -> Result<(), AppError> { let mut compound_data: Vec = vec![]; compound_data.append(&mut server_identification(current_player.operator)); compound_data.append(&mut init_level()); // info!("Send level data"); - compound_data.append(&mut send_level_data(&world_arc_clone)); // Approaching Nirvana - Maw of the beast + compound_data.append(&mut send_level_data(&world_arc_clone)?); // Approaching Nirvana - Maw of the beast - compound_data.append(&mut finalize_level(&world_arc_clone)); + compound_data.append(&mut finalize_level(&world_arc_clone)?); info!("Spawning player: {}", ¤t_player.username); compound_data.append(&mut spawn_player( @@ -235,5 +236,6 @@ pub fn bomb_server_details( )); let _ = stream.write(&compound_data); + Ok(()) } diff --git a/src/world.rs b/src/world.rs index 67dd8bf..bf23363 100644 --- a/src/world.rs +++ b/src/world.rs @@ -72,10 +72,11 @@ impl World { } } - pub fn save(world_arc_clone: Arc>) -> std::io::Result<()> { + pub fn save(world_arc_clone: Arc>) -> Result<(), AppError> { let mut to_write: Vec = Vec::new(); { - let mut world_dat = world_arc_clone.lock().unwrap(); + let mut world_dat = world_arc_clone.lock().map_err(|e| AppError::MutexPoisoned(e.to_string()))?; + to_write.push((world_dat.size_x >> 8) as u8); to_write.push((world_dat.size_x & 0xFF) as u8); to_write.push((world_dat.size_y >> 8) as u8); @@ -86,6 +87,6 @@ impl World { } let mut file = File::create("world.wrld")?; - file.write_all(&to_write) + Ok(file.write_all(&to_write)?) } }