Add extension information commands

This commit is contained in:
illegitimate-egg 2025-03-02 15:59:17 +00:00
parent 7ab11f7f54
commit a58b1c9afe
5 changed files with 103 additions and 13 deletions

View File

@ -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, Ok(result) => result,
Err(error) => { Err(error) => {
error!("Rhai plugin error: {}", error); error!("Rhai plugin error: {}", error);

View File

@ -5,19 +5,74 @@ use std::{
collections::HashMap, collections::HashMap,
ffi::OsStr, ffi::OsStr,
fs, fs,
net::TcpStream,
path::Path, path::Path,
sync::{Arc, Mutex}, 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 { pub struct Extensions {
extensions: Vec<Extension>, extensions: Vec<Extension>,
} }
impl Extensions { impl Extensions {
pub fn run_command(&self, key: String, player: u8) -> Result<bool, AppError> { pub fn run_command(
// Bool success &self,
key: String,
player: u8,
stream: &mut TcpStream,
) -> Result<bool, AppError> {
// 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 { for extension in &self.extensions {
if let Some(key_value) = extension.commands.get(&key) { if let Some(key_value) = extension.commands.get(&key) {
key_value.call::<()>(&extension.engine, &extension.ast, (player,))?; key_value.call::<()>(&extension.engine, &extension.ast, (player,))?;
@ -233,7 +288,7 @@ impl Extensions {
&mut scope, &mut scope,
&current_extension.ast, &current_extension.ast,
"init", "init",
(PlayersWrapper::new(players.0.clone()),) (PlayersWrapper::new(players.0.clone()),),
) { ) {
Ok(result) => result, Ok(result) => result,
Err(error) => { Err(error) => {

View File

@ -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
}

View File

@ -6,9 +6,9 @@ use std::net::TcpStream;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::error::AppError; use crate::error::AppError;
use crate::World;
use crate::SpecialPlayers;
use crate::Player; use crate::Player;
use crate::SpecialPlayers;
use crate::World;
pub fn to_mc_string(text: &str) -> [u8; 64] { pub fn to_mc_string(text: &str) -> [u8; 64] {
let text_vec: Vec<char> = text.chars().take(64).collect(); let text_vec: Vec<char> = text.chars().take(64).collect();
@ -104,7 +104,11 @@ pub fn despawn_player(player_id: u8) -> Vec<u8> {
[0x0C, player_id].to_vec() [0x0C, player_id].to_vec()
} }
pub fn send_chat_message(source_id: u8, mut source_username: String, mut message: String) -> Vec<u8> { pub fn send_chat_message(
source_id: u8,
mut source_username: String,
mut message: String,
) -> Vec<u8> {
let mut ret_val: Vec<u8> = vec![]; let mut ret_val: Vec<u8> = vec![];
ret_val.push(0x0D); ret_val.push(0x0D);
@ -119,6 +123,14 @@ pub fn send_chat_message(source_id: u8, mut source_username: String, mut message
ret_val 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( pub fn set_position_and_orientation(
player_id: u8, player_id: u8,
pos_x: i16, pos_x: i16,
@ -189,9 +201,7 @@ pub fn send_level_data(world_arc_clone: &Arc<Mutex<World>>) -> Result<Vec<u8>, A
if remaining_chunk_size > 0 { if remaining_chunk_size > 0 {
ret_val.push(0x03); ret_val.push(0x03);
ret_val.append(&mut stream_write_short( ret_val.append(&mut stream_write_short(remaining_chunk_size.try_into()?));
remaining_chunk_size.try_into()?,
));
let mut remaining_data_buffer = [0u8; 1024]; let mut remaining_data_buffer = [0u8; 1024];
for i in 0..remaining_chunk_size { for i in 0..remaining_chunk_size {
@ -238,4 +248,3 @@ pub fn bomb_server_details(
let _ = stream.write(&compound_data); let _ = stream.write(&compound_data);
Ok(()) Ok(())
} }

View File

@ -2,6 +2,8 @@ use std::fs::{self, File};
use std::io::Write; use std::io::Write;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use log::info;
use crate::error::AppError; use crate::error::AppError;
#[derive(Debug)] #[derive(Debug)]
@ -61,8 +63,10 @@ impl World {
} }
world.data = world_data_raw[6..].to_vec(); world.data = world_data_raw[6..].to_vec();
info!("Loaded world {}", "world.wrld");
Ok(world) Ok(world)
} else { } else {
info!("Creating word {}", "world.wrld");
Ok(World { Ok(World {
size_x: 64, size_x: 64,
size_y: 32, size_y: 32,
@ -75,7 +79,9 @@ impl World {
pub fn save(world_arc_clone: Arc<Mutex<World>>) -> Result<(), AppError> { pub fn save(world_arc_clone: Arc<Mutex<World>>) -> Result<(), AppError> {
let mut to_write: Vec<u8> = Vec::new(); let mut to_write: Vec<u8> = 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 >> 8) as u8);
to_write.push((world_dat.size_x & 0xFF) as u8); to_write.push((world_dat.size_x & 0xFF) as u8);