diff --git a/src/command.rs b/src/command.rs index 327e3ec..1bec603 100644 --- a/src/command.rs +++ b/src/command.rs @@ -54,7 +54,11 @@ pub fn handle_command( } } _ => { - let found = match extensions.run_command(vectorized_command[0].to_string(), client_number) { + let found = match extensions.run_command( + vectorized_command[0].to_string(), + client_number, + stream, + ) { Ok(result) => result, Err(error) => { error!("Rhai plugin error: {}", error); diff --git a/src/extensions.rs b/src/extensions.rs index f84f8b5..986b59b 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -5,19 +5,74 @@ use std::{ collections::HashMap, ffi::OsStr, fs, + net::TcpStream, path::Path, sync::{Arc, Mutex}, }; -use crate::{error::AppError, player::Player, utils::send_chat_message}; +use crate::{ + error::AppError, + player::Player, + utils::{send_chat_message, write_chat_stream}, +}; pub struct Extensions { extensions: Vec, } impl Extensions { - pub fn run_command(&self, key: String, player: u8) -> Result { - // Bool success + pub fn run_command( + &self, + key: String, + player: u8, + stream: &mut TcpStream, + ) -> Result { + // Here I'm calling write_chat_stream multiple times. This is because the stock minecraft + // chat has a length limit of 64 characters, which is pathetically small. There is a + // classic extension to support an unlimited number of characters, but it's not guaranteed + // that the client will support it, so the next best option is to just send multiple + // messages, they're newline seperated anyway. I am aware that repeated stream writes are + // not the best option however, and that at some point I should switch to buffered streams. + // TODO: Use buffered streams (That's everywhere not just here) + + // Reserve extension listing command + if &key == "extensions" { + let _ = write_chat_stream(stream, "Extension listing".to_string()); + + for extension in &self.extensions { + let _ = write_chat_stream( + stream, + format!( + "&a{} &bv{}", + extension.metadata.name, + extension.metadata.version.display() + ), + ); + } + + return Ok(true); + } + + // Reserve command listing command + if &key == "commands" { + let _ = write_chat_stream(stream, "Command listing".to_string()); + + for extension in &self.extensions { + for command in extension.commands.keys() { + let _ = write_chat_stream( + stream, + format!( + "&c{} &a[{}]", + command, + extension.metadata.name + ), + ); + } + } + + return Ok(true); + } + for extension in &self.extensions { if let Some(key_value) = extension.commands.get(&key) { key_value.call::<()>(&extension.engine, &extension.ast, (player,))?; @@ -233,7 +288,7 @@ impl Extensions { &mut scope, ¤t_extension.ast, "init", - (PlayersWrapper::new(players.0.clone()),) + (PlayersWrapper::new(players.0.clone()),), ) { Ok(result) => result, Err(error) => { diff --git a/src/extensions/ping-pong.rhai b/src/extensions/ping-pong.rhai new file mode 100644 index 0000000..3384758 --- /dev/null +++ b/src/extensions/ping-pong.rhai @@ -0,0 +1,16 @@ +fn metadata() { + Metadata("ping-pong", Version("1.0.0")) +} + +fn init(players) { + let ctx = Context(players); + + ctx.register_command("ping", |player| { + players.send_message(player, "pong") + }); + ctx.register_command("foo", |player| { + players.send_message(player, "bar") + }); + + ctx +} diff --git a/src/utils.rs b/src/utils.rs index a3ee32e..d6ab66a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,9 +6,9 @@ use std::net::TcpStream; use std::sync::{Arc, Mutex}; use crate::error::AppError; -use crate::World; -use crate::SpecialPlayers; use crate::Player; +use crate::SpecialPlayers; +use crate::World; pub fn to_mc_string(text: &str) -> [u8; 64] { let text_vec: Vec = text.chars().take(64).collect(); @@ -104,7 +104,11 @@ pub fn despawn_player(player_id: u8) -> Vec { [0x0C, player_id].to_vec() } -pub fn send_chat_message(source_id: u8, mut source_username: String, mut message: String) -> Vec { +pub fn send_chat_message( + source_id: u8, + mut source_username: String, + mut message: String, +) -> Vec { let mut ret_val: Vec = vec![]; ret_val.push(0x0D); @@ -119,6 +123,14 @@ pub fn send_chat_message(source_id: u8, mut source_username: String, mut message ret_val } +pub fn write_chat_stream(stream: &mut TcpStream, message: String) { + let _ = stream.write(&send_chat_message( + SpecialPlayers::SelfPlayer as u8, + "".to_string(), + message, + )); +} + pub fn set_position_and_orientation( player_id: u8, pos_x: i16, @@ -189,9 +201,7 @@ pub fn send_level_data(world_arc_clone: &Arc>) -> Result, A if remaining_chunk_size > 0 { ret_val.push(0x03); - ret_val.append(&mut stream_write_short( - remaining_chunk_size.try_into()?, - )); + ret_val.append(&mut stream_write_short(remaining_chunk_size.try_into()?)); let mut remaining_data_buffer = [0u8; 1024]; for i in 0..remaining_chunk_size { @@ -238,4 +248,3 @@ pub fn bomb_server_details( let _ = stream.write(&compound_data); Ok(()) } - diff --git a/src/world.rs b/src/world.rs index bf23363..25dfe63 100644 --- a/src/world.rs +++ b/src/world.rs @@ -2,6 +2,8 @@ use std::fs::{self, File}; use std::io::Write; use std::sync::{Arc, Mutex}; +use log::info; + use crate::error::AppError; #[derive(Debug)] @@ -61,8 +63,10 @@ impl World { } world.data = world_data_raw[6..].to_vec(); + info!("Loaded world {}", "world.wrld"); Ok(world) } else { + info!("Creating word {}", "world.wrld"); Ok(World { size_x: 64, size_y: 32, @@ -75,7 +79,9 @@ impl World { pub fn save(world_arc_clone: Arc>) -> Result<(), AppError> { let mut to_write: Vec = Vec::new(); { - let mut world_dat = world_arc_clone.lock().map_err(|e| AppError::MutexPoisoned(e.to_string()))?; + 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);