From 45ed5833124c1802662f74e3575e6a0fa21b783d Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 7 Apr 2026 11:28:36 +0200 Subject: [PATCH] feat(plugin-api): use trappable --- pumpkin-plugin-wit/v0.1.0/command.wit | 2 +- pumpkin-plugin-wit/v0.1.0/player.wit | 116 +-- .../wasm/wasm_host/wit/v0_1_0/commands/mod.rs | 854 +++++++++--------- .../wasm/wasm_host/wit/v0_1_0/context.rs | 87 +- .../wasm/wasm_host/wit/v0_1_0/entity.rs | 67 +- .../loader/wasm/wasm_host/wit/v0_1_0/i18n.rs | 26 +- .../wasm/wasm_host/wit/v0_1_0/logging.rs | 10 +- .../loader/wasm/wasm_host/wit/v0_1_0/mod.rs | 4 +- .../wasm/wasm_host/wit/v0_1_0/player.rs | 203 ++--- .../wasm/wasm_host/wit/v0_1_0/server.rs | 65 +- .../loader/wasm/wasm_host/wit/v0_1_0/text.rs | 517 ++++++----- .../loader/wasm/wasm_host/wit/v0_1_0/world.rs | 197 ++-- 12 files changed, 1094 insertions(+), 1054 deletions(-) diff --git a/pumpkin-plugin-wit/v0.1.0/command.wit b/pumpkin-plugin-wit/v0.1.0/command.wit index b5fdec71a..e1265fd28 100644 --- a/pumpkin-plugin-wit/v0.1.0/command.wit +++ b/pumpkin-plugin-wit/v0.1.0/command.wit @@ -98,7 +98,7 @@ interface command { } variant command-sender-type { - rcon(list), + rcon, console, player(player), command-block(tuple), diff --git a/pumpkin-plugin-wit/v0.1.0/player.wit b/pumpkin-plugin-wit/v0.1.0/player.wit index b8c478641..8e08d1fb1 100644 --- a/pumpkin-plugin-wit/v0.1.0/player.wit +++ b/pumpkin-plugin-wit/v0.1.0/player.wit @@ -15,79 +15,79 @@ interface player { } resource player { - get-id: func () -> result; - get-name: func () -> result; + get-id: func () -> string; + get-name: func () -> string; // Basic state - get-position: func () -> result; - get-rotation: func () -> result, string>; - get-world: func () -> result<%world, string>; - get-gamemode: func () -> result; - set-gamemode: func (mode: game-mode) -> result; - get-locale: func () -> result; - get-ping: func () -> result; + get-position: func () -> position; + get-rotation: func () -> tuple; + get-world: func () -> %world; + get-gamemode: func () -> game-mode; + set-gamemode: func (mode: game-mode) -> bool; + get-locale: func () -> string; + get-ping: func () -> u32; // Permissions & metadata - get-permission-level: func () -> result; - set-permission-level: func (level: permission-level) -> result<_, string>; - has-permission: func (node: string) -> result; - get-display-name: func () -> result; + get-permission-level: func () -> permission-level; + set-permission-level: func (level: permission-level); + has-permission: func (node: string) -> bool; + get-display-name: func () -> text-component; // Messaging & titles - send-system-message: func (text: text-component, overlay: bool) -> result<_, string>; - show-title: func (text: text-component) -> result<_, string>; - show-subtitle: func (text: text-component) -> result<_, string>; - show-actionbar: func (text: text-component) -> result<_, string>; - send-title-animation: func (fade-in: s32, stay: s32, fade-out: s32) -> result<_, string>; + send-system-message: func (text: text-component, overlay: bool); + show-title: func (text: text-component); + show-subtitle: func (text: text-component); + show-actionbar: func (text: text-component); + send-title-animation: func (fade-in: s32, stay: s32, fade-out: s32); // Teleport & kick - teleport: func (position: position, yaw: f32, pitch: f32) -> result<_, string>; - teleport-world: func (world-ref: %world, position: position, yaw: option, pitch: option) -> result<_, string>; - kick: func (message: text-component) -> result<_, string>; - respawn: func () -> result<_, string>; + teleport: func (position: position, yaw: f32, pitch: f32); + teleport-world: func (world-ref: %world, position: position, yaw: option, pitch: option); + kick: func (message: text-component); + respawn: func (); - ban: func (reason: option) -> result<_, string>; - ban-ip: func (reason: option) -> result<_, string>; + ban: func (reason: option); + ban-ip: func (reason: option); // Inventory - get-selected-slot: func () -> result; - get-item-in-hand: func (hand: hand) -> result, string>; - get-inventory-item: func (slot: u8) -> result, string>; + get-selected-slot: func () -> u8; + get-item-in-hand: func (hand: hand) -> option; + get-inventory-item: func (slot: u8) -> option; // Status - get-health: func () -> result; - set-health: func (health: f32) -> result<_, string>; - get-max-health: func () -> result; - set-max-health: func (max-health: f32) -> result<_, string>; - get-food-level: func () -> result; - set-food-level: func (food-level: u8) -> result<_, string>; - get-saturation: func () -> result; - set-saturation: func (saturation: f32) -> result<_, string>; - get-exhaustion: func () -> result; - set-exhaustion: func (exhaustion: f32) -> result<_, string>; - get-absorption: func () -> result; - set-absorption: func (absorption: f32) -> result<_, string>; + get-health: func () -> f32; + set-health: func (health: f32); + get-max-health: func () -> f32; + set-max-health: func (max-health: f32); + get-food-level: func () -> u8; + set-food-level: func (food-level: u8); + get-saturation: func () -> f32; + set-saturation: func (saturation: f32); + get-exhaustion: func () -> f32; + set-exhaustion: func (exhaustion: f32); + get-absorption: func () -> f32; + set-absorption: func (absorption: f32); - get-experience-level: func() -> result; - get-experience-progress: func() -> result; - get-experience-points: func() -> result; - set-experience-level: func(level: s32) -> result<_, string>; - set-experience-progress: func(progress: f32) -> result<_, string>; - set-experience-points: func(points: s32) -> result<_, string>; - add-experience-levels: func(levels: s32) -> result<_, string>; - add-experience-points: func(points: s32) -> result<_, string>; + get-experience-level: func() -> s32; + get-experience-progress: func() -> f32; + get-experience-points: func() -> s32; + set-experience-level: func(level: s32); + set-experience-progress: func(progress: f32); + set-experience-points: func(points: s32); + add-experience-levels: func(levels: s32); + add-experience-points: func(points: s32); - is-sneaking: func() -> result; - set-sneaking: func(sneaking: bool) -> result<_, string>; - is-sprinting: func() -> result; - set-sprinting: func(sprinting: bool) -> result<_, string>; - is-on-ground: func() -> result; - is-flying: func() -> result; - set-flying: func(flying: bool) -> result<_, string>; + is-sneaking: func() -> bool; + set-sneaking: func(sneaking: bool); + is-sprinting: func() -> bool; + set-sprinting: func(sprinting: bool); + is-on-ground: func() -> bool; + is-flying: func() -> bool; + set-flying: func(flying: bool); - get-abilities: func() -> result; - set-abilities: func(abilities: player-abilities) -> result<_, string>; + get-abilities: func() -> player-abilities; + set-abilities: func(abilities: player-abilities); - get-ip: func() -> result; + get-ip: func() -> string; } -} +} \ No newline at end of file diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/commands/mod.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/commands/mod.rs index a23c4ac64..fca3568f5 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/commands/mod.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/commands/mod.rs @@ -26,13 +26,13 @@ use crate::{ }, tree::{ CommandTree, - builder::{argument, literal}, + builder::{NonLeafNodeBuilder, argument, literal}, }, }, plugin::loader::wasm::wasm_host::{ - DowncastResourceExt, state::{ - CommandNodeResource, CommandSenderResource, PluginHostState, TextComponentResource, + CommandNodeResource, CommandResource, CommandSenderResource, ConsumedArgsResource, + PluginHostState, ServerResource, TextComponentResource, }, wit::v0_1_0::{ commands::executor::WasmCommandExecutor, @@ -56,77 +56,115 @@ use crate::{ pub mod executor; +impl PluginHostState { + fn get_command_mut( + &mut self, + res: &Resource, + ) -> wasmtime::Result<&mut CommandResource> { + self.resource_table + .get_mut::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + fn get_node_mut( + &mut self, + res: &Resource, + ) -> wasmtime::Result<&mut CommandNodeResource> { + self.resource_table + .get_mut::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + fn take_node(&mut self, res: &Resource) -> wasmtime::Result { + self.resource_table + .delete::(Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + fn get_sender_res( + &self, + res: &Resource, + ) -> wasmtime::Result<&CommandSenderResource> { + self.resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + fn get_sender_mut( + &mut self, + res: &Resource, + ) -> wasmtime::Result<&mut CommandSenderResource> { + self.resource_table + .get_mut::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } +} + impl pumpkin::plugin::command::Host for PluginHostState {} impl pumpkin::plugin::command::HostConsumedArgs for PluginHostState { - async fn get_value(&mut self, _consumed_args: Resource, _key: String) -> Arg { - todo!() + async fn get_value( + &mut self, + _consumed_args: Resource, + _key: String, + ) -> wasmtime::Result { + todo!("Implement internal argument retrieval from ConsumedArgs") } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { self.resource_table - .delete::( - Resource::new_own(rep.rep()), - )?; + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; Ok(()) } } impl pumpkin::plugin::command::HostCommand for PluginHostState { - async fn new(&mut self, names: Vec, description: String) -> Resource { + async fn new( + &mut self, + names: Vec, + description: String, + ) -> wasmtime::Result> { self.add_command(CommandTree::new(names, description)) - .unwrap() + .map_err(|_| wasmtime::Error::msg("Failed to add command resource")) } - async fn then(&mut self, command: Resource, node: Resource) -> () { - let node_resource = node.consume(self); - let command_resource = self - .resource_table - .get_mut::( - &Resource::new_own(command.rep()), - ) - .expect("invalid command resource handle"); - - command_resource.provider = command_resource - .provider - .clone() - .then(node_resource.provider); + async fn then( + &mut self, + command: Resource, + node: Resource, + ) -> wasmtime::Result<()> { + let node_data = self.take_node(&node)?; + let command_res = self.get_command_mut(&command)?; + command_res.provider = command_res.provider.clone().then(node_data.provider); + Ok(()) } - async fn execute_with_handler_id(&mut self, command: Resource, handler_id: u32) -> () { + async fn execute_with_handler_id( + &mut self, + command: Resource, + handler_id: u32, + ) -> wasmtime::Result<()> { let plugin = self .plugin .as_ref() - .expect("plugin should always be initialized here") - .upgrade() - .expect("plugin has been dropped"); - + .and_then(std::sync::Weak::upgrade) + .ok_or_else(|| wasmtime::Error::msg("Plugin dropped"))?; let server = self .server .clone() - .expect("server should be set before command registration"); + .ok_or_else(|| wasmtime::Error::msg("Server not initialized"))?; let executor = WasmCommandExecutor { handler_id, plugin, server, }; - - let command_resource = self - .resource_table - .get_mut::( - &Resource::new_own(command.rep()), - ) - .expect("invalid command resource handle"); - - command_resource.provider = command_resource.provider.clone().execute(executor); + let command_res = self.get_command_mut(&command)?; + command_res.provider = command_res.provider.clone().execute(executor); + Ok(()) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { self.resource_table - .delete::( - Resource::new_own(rep.rep()), - )?; + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; Ok(()) } } @@ -134,105 +172,96 @@ impl pumpkin::plugin::command::HostCommand for PluginHostState { impl pumpkin::plugin::command::HostCommandSender for PluginHostState { async fn get_command_sender_type( &mut self, - _command_sender: Resource, - ) -> CommandSenderType { + _res: Resource, + ) -> wasmtime::Result { + // let sender = &self.get_sender_res(&res)?.provider; + // Ok(match sender { + // crate::command::CommandSender::Player(_) => CommandSenderType::Player, + // crate::command::CommandSender::Console => CommandSenderType::Console, + // crate::command::CommandSender::Rcon(_) => CommandSenderType::Rcon, + // crate::command::CommandSender::CommandBlock(.._) => CommandSenderType::CommandBlock, + // crate::command::CommandSender::Dummy => CommandSenderType::Dummy, + // }) todo!() } async fn send_message( &mut self, - command_sender: Resource, + sender: Resource, text: Resource, - ) -> () { - let text_resource = self + ) -> wasmtime::Result<()> { + let component = self .resource_table - .get::(&Resource::new_own(text.rep())) - .expect("invalid text-component resource handle"); - let component = text_resource.provider.clone(); - - let sender_resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - sender_resource.provider.send_message(component).await; + .get::(&Resource::new_own(text.rep()))? + .provider + .clone(); + self.get_sender_res(&sender)? + .provider + .send_message(component) + .await; + Ok(()) } - async fn set_success_count(&mut self, command_sender: Resource, count: i32) { - let resource = self - .resource_table - .get_mut::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - resource.provider.set_success_count(count as u32); + async fn set_success_count( + &mut self, + sender: Resource, + count: i32, + ) -> wasmtime::Result<()> { + self.get_sender_mut(&sender)? + .provider + .set_success_count(count as u32); + Ok(()) } - async fn is_player(&mut self, command_sender: Resource) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - matches!(resource.provider, crate::command::CommandSender::Player(_)) + async fn is_player(&mut self, sender: Resource) -> wasmtime::Result { + Ok(matches!( + self.get_sender_res(&sender)?.provider, + crate::command::CommandSender::Player(_) + )) } - async fn is_console(&mut self, command_sender: Resource) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - matches!( - resource.provider, + async fn is_console(&mut self, sender: Resource) -> wasmtime::Result { + Ok(matches!( + self.get_sender_res(&sender)?.provider, crate::command::CommandSender::Console | crate::command::CommandSender::Rcon(_) - ) + )) } async fn as_player( &mut self, - command_sender: Resource, - ) -> Option> { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - if let crate::command::CommandSender::Player(player) = &resource.provider { - let player = player.clone(); - Some(self.add_player(player).unwrap()) + sender: Resource, + ) -> wasmtime::Result>> { + if let crate::command::CommandSender::Player(player) = + &self.get_sender_res(&sender)?.provider + { + Ok(Some(self.add_player(player.clone()).map_err(|_| { + wasmtime::Error::msg("Failed to add player resource") + })?)) } else { - None + Ok(None) } } async fn permission_level( &mut self, - command_sender: Resource, - ) -> PermissionLevel { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - match resource.provider.permission_lvl() { - pumpkin_util::PermissionLvl::Zero => PermissionLevel::Zero, - pumpkin_util::PermissionLvl::One => PermissionLevel::One, - pumpkin_util::PermissionLvl::Two => PermissionLevel::Two, - pumpkin_util::PermissionLvl::Three => PermissionLevel::Three, - pumpkin_util::PermissionLvl::Four => PermissionLevel::Four, - } + sender: Resource, + ) -> wasmtime::Result { + Ok( + match self.get_sender_res(&sender)?.provider.permission_lvl() { + pumpkin_util::PermissionLvl::Zero => PermissionLevel::Zero, + pumpkin_util::PermissionLvl::One => PermissionLevel::One, + pumpkin_util::PermissionLvl::Two => PermissionLevel::Two, + pumpkin_util::PermissionLvl::Three => PermissionLevel::Three, + pumpkin_util::PermissionLvl::Four => PermissionLevel::Four, + }, + ) } async fn has_permission_level( &mut self, - command_sender: Resource, + sender: Resource, level: PermissionLevel, - ) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - + ) -> wasmtime::Result { let required = match level { PermissionLevel::Zero => pumpkin_util::PermissionLvl::Zero, PermissionLevel::One => pumpkin_util::PermissionLvl::One, @@ -240,417 +269,338 @@ impl pumpkin::plugin::command::HostCommandSender for PluginHostState { PermissionLevel::Three => pumpkin_util::PermissionLvl::Three, PermissionLevel::Four => pumpkin_util::PermissionLvl::Four, }; - - resource.provider.permission_lvl() >= required + Ok(self.get_sender_res(&sender)?.provider.permission_lvl() >= required) } async fn has_permission( &mut self, - command_sender: Resource, + sender: Resource, server: Resource, node: String, - ) -> bool { - let sender_resource = self + ) -> wasmtime::Result { + let sender_provider = &self.get_sender_res(&sender)?.provider; + let server_provider = &self .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - let server_resource = self - .resource_table - .get::( - &Resource::new_own(server.rep()), - ) - .expect("invalid server resource handle"); - - sender_resource - .provider - .has_permission(&server_resource.provider, &node) - .await + .get::(&Resource::new_own(server.rep()))? + .provider; + Ok(sender_provider.has_permission(server_provider, &node).await) } - async fn position(&mut self, command_sender: Resource) -> Option { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - resource + async fn position( + &mut self, + sender: Resource, + ) -> wasmtime::Result> { + Ok(self + .get_sender_res(&sender)? .provider .position() - .map(|pos| (pos.x, pos.y, pos.z)) + .map(|p| (p.x, p.y, p.z))) } - async fn world(&mut self, command_sender: Resource) -> Option> { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - if let Some(world) = resource.provider.world() { - Some( - self.resource_table - .push( - crate::plugin::loader::wasm::wasm_host::state::WasmResource { - provider: world, - }, - ) - .map(|r| wasmtime::component::Resource::new_own(r.rep())) - .unwrap(), - ) + async fn world( + &mut self, + sender: Resource, + ) -> wasmtime::Result>> { + if let Some(world) = self.get_sender_res(&sender)?.provider.world() { + Ok(Some(self.add_world(world).map_err(|_| { + wasmtime::Error::msg("Failed to add world resource") + })?)) } else { - None + Ok(None) } } - #[allow(clippy::too_many_lines)] - async fn get_locale(&mut self, command_sender: Resource) -> Locale { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - match resource.provider.get_locale() { - pumpkin_util::translation::Locale::AfZa => Locale::AfZa, - pumpkin_util::translation::Locale::ArSa => Locale::ArSa, - pumpkin_util::translation::Locale::AstEs => Locale::AstEs, - pumpkin_util::translation::Locale::AzAz => Locale::AzAz, - pumpkin_util::translation::Locale::BaRu => Locale::BaRu, - pumpkin_util::translation::Locale::Bar => Locale::Bar, - pumpkin_util::translation::Locale::BeBy => Locale::BeBy, - pumpkin_util::translation::Locale::BgBg => Locale::BgBg, - pumpkin_util::translation::Locale::BrFr => Locale::BrFr, - pumpkin_util::translation::Locale::Brb => Locale::Brb, - pumpkin_util::translation::Locale::BsBa => Locale::BsBa, - pumpkin_util::translation::Locale::CaEs => Locale::CaEs, - pumpkin_util::translation::Locale::CsCz => Locale::CsCz, - pumpkin_util::translation::Locale::CyGb => Locale::CyGb, - pumpkin_util::translation::Locale::DaDk => Locale::DaDk, - pumpkin_util::translation::Locale::DeAt => Locale::DeAt, - pumpkin_util::translation::Locale::DeCh => Locale::DeCh, - pumpkin_util::translation::Locale::DeDe => Locale::DeDe, - pumpkin_util::translation::Locale::ElGr => Locale::ElGr, - pumpkin_util::translation::Locale::EnAu => Locale::EnAu, - pumpkin_util::translation::Locale::EnCa => Locale::EnCa, - pumpkin_util::translation::Locale::EnGb => Locale::EnGb, - pumpkin_util::translation::Locale::EnNz => Locale::EnNz, - pumpkin_util::translation::Locale::EnPt => Locale::EnPt, - pumpkin_util::translation::Locale::EnUd => Locale::EnUd, - pumpkin_util::translation::Locale::EnUs => Locale::EnUs, - pumpkin_util::translation::Locale::Enp => Locale::Enp, - pumpkin_util::translation::Locale::Enws => Locale::Enws, - pumpkin_util::translation::Locale::EoUy => Locale::EoUy, - pumpkin_util::translation::Locale::EsAr => Locale::EsAr, - pumpkin_util::translation::Locale::EsCl => Locale::EsCl, - pumpkin_util::translation::Locale::EsEc => Locale::EsEc, - pumpkin_util::translation::Locale::EsEs => Locale::EsEs, - pumpkin_util::translation::Locale::EsMx => Locale::EsMx, - pumpkin_util::translation::Locale::EsUy => Locale::EsUy, - pumpkin_util::translation::Locale::EsVe => Locale::EsVe, - pumpkin_util::translation::Locale::Esan => Locale::Esan, - pumpkin_util::translation::Locale::EtEe => Locale::EtEe, - pumpkin_util::translation::Locale::EuEs => Locale::EuEs, - pumpkin_util::translation::Locale::FaIr => Locale::FaIr, - pumpkin_util::translation::Locale::FiFi => Locale::FiFi, - pumpkin_util::translation::Locale::FilPh => Locale::FilPh, - pumpkin_util::translation::Locale::FoFo => Locale::FoFo, - pumpkin_util::translation::Locale::FrCa => Locale::FrCa, - pumpkin_util::translation::Locale::FrFr => Locale::FrFr, - pumpkin_util::translation::Locale::FraDe => Locale::FraDe, - pumpkin_util::translation::Locale::FurIt => Locale::FurIt, - pumpkin_util::translation::Locale::FyNl => Locale::FyNl, - pumpkin_util::translation::Locale::GaIe => Locale::GaIe, - pumpkin_util::translation::Locale::GdGb => Locale::GdGb, - pumpkin_util::translation::Locale::GlEs => Locale::GlEs, - pumpkin_util::translation::Locale::HawUs => Locale::HawUs, - pumpkin_util::translation::Locale::HeIl => Locale::HeIl, - pumpkin_util::translation::Locale::HiIn => Locale::HiIn, - pumpkin_util::translation::Locale::HrHr => Locale::HrHr, - pumpkin_util::translation::Locale::HuHu => Locale::HuHu, - pumpkin_util::translation::Locale::HyAm => Locale::HyAm, - pumpkin_util::translation::Locale::IdId => Locale::IdId, - pumpkin_util::translation::Locale::IgNg => Locale::IgNg, - pumpkin_util::translation::Locale::IoEn => Locale::IoEn, - pumpkin_util::translation::Locale::IsIs => Locale::IsIs, - pumpkin_util::translation::Locale::Isv => Locale::Isv, - pumpkin_util::translation::Locale::ItIt => Locale::ItIt, - pumpkin_util::translation::Locale::JaJp => Locale::JaJp, - pumpkin_util::translation::Locale::JboEn => Locale::JboEn, - pumpkin_util::translation::Locale::KaGe => Locale::KaGe, - pumpkin_util::translation::Locale::KkKz => Locale::KkKz, - pumpkin_util::translation::Locale::KnIn => Locale::KnIn, - pumpkin_util::translation::Locale::KoKr => Locale::KoKr, - pumpkin_util::translation::Locale::Ksh => Locale::Ksh, - pumpkin_util::translation::Locale::KwGb => Locale::KwGb, - pumpkin_util::translation::Locale::LaLa => Locale::LaLa, - pumpkin_util::translation::Locale::LbLu => Locale::LbLu, - pumpkin_util::translation::Locale::LiLi => Locale::LiLi, - pumpkin_util::translation::Locale::Lmo => Locale::Lmo, - pumpkin_util::translation::Locale::LoLa => Locale::LoLa, - pumpkin_util::translation::Locale::LolUs => Locale::LolUs, - pumpkin_util::translation::Locale::LtLt => Locale::LtLt, - pumpkin_util::translation::Locale::LvLv => Locale::LvLv, - pumpkin_util::translation::Locale::Lzh => Locale::Lzh, - pumpkin_util::translation::Locale::MkMk => Locale::MkMk, - pumpkin_util::translation::Locale::MnMn => Locale::MnMn, - pumpkin_util::translation::Locale::MsMy => Locale::MsMy, - pumpkin_util::translation::Locale::MtMt => Locale::MtMt, - pumpkin_util::translation::Locale::Nah => Locale::Nah, - pumpkin_util::translation::Locale::NdsDe => Locale::NdsDe, - pumpkin_util::translation::Locale::NlBe => Locale::NlBe, - pumpkin_util::translation::Locale::NlNl => Locale::NlNl, - pumpkin_util::translation::Locale::NnNo => Locale::NnNo, - pumpkin_util::translation::Locale::NoNo => Locale::NoNo, - pumpkin_util::translation::Locale::OcFr => Locale::OcFr, - pumpkin_util::translation::Locale::Ovd => Locale::Ovd, - pumpkin_util::translation::Locale::PlPl => Locale::PlPl, - pumpkin_util::translation::Locale::PtBr => Locale::PtBr, - pumpkin_util::translation::Locale::PtPt => Locale::PtPt, - pumpkin_util::translation::Locale::QyaAa => Locale::QyaAa, - pumpkin_util::translation::Locale::RoRo => Locale::RoRo, - pumpkin_util::translation::Locale::Rpr => Locale::Rpr, - pumpkin_util::translation::Locale::RuRu => Locale::RuRu, - pumpkin_util::translation::Locale::RyUa => Locale::RyUa, - pumpkin_util::translation::Locale::SahSah => Locale::SahSah, - pumpkin_util::translation::Locale::SeNo => Locale::SeNo, - pumpkin_util::translation::Locale::SkSk => Locale::SkSk, - pumpkin_util::translation::Locale::SlSi => Locale::SlSi, - pumpkin_util::translation::Locale::SoSo => Locale::SoSo, - pumpkin_util::translation::Locale::SqAl => Locale::SqAl, - pumpkin_util::translation::Locale::SrCs => Locale::SrCs, - pumpkin_util::translation::Locale::SrSp => Locale::SrSp, - pumpkin_util::translation::Locale::SvSe => Locale::SvSe, - pumpkin_util::translation::Locale::Sxu => Locale::Sxu, - pumpkin_util::translation::Locale::Szl => Locale::Szl, - pumpkin_util::translation::Locale::TaIn => Locale::TaIn, - pumpkin_util::translation::Locale::ThTh => Locale::ThTh, - pumpkin_util::translation::Locale::TlPh => Locale::TlPh, - pumpkin_util::translation::Locale::TlhAa => Locale::TlhAa, - pumpkin_util::translation::Locale::Tok => Locale::Tok, - pumpkin_util::translation::Locale::TrTr => Locale::TrTr, - pumpkin_util::translation::Locale::TtRu => Locale::TtRu, - pumpkin_util::translation::Locale::UkUa => Locale::UkUa, - pumpkin_util::translation::Locale::ValEs => Locale::ValEs, - pumpkin_util::translation::Locale::VecIt => Locale::VecIt, - pumpkin_util::translation::Locale::ViVn => Locale::ViVn, - pumpkin_util::translation::Locale::YiDe => Locale::YiDe, - pumpkin_util::translation::Locale::YoNg => Locale::YoNg, - pumpkin_util::translation::Locale::ZhCn => Locale::ZhCn, - pumpkin_util::translation::Locale::ZhHk => Locale::ZhHk, - pumpkin_util::translation::Locale::ZhTw => Locale::ZhTw, - pumpkin_util::translation::Locale::ZlmArab => Locale::ZlmArab, - } + async fn get_locale(&mut self, sender: Resource) -> wasmtime::Result { + Ok(map_util_locale_to_wit( + self.get_sender_res(&sender)?.provider.get_locale(), + )) } - async fn should_receive_feedback(&mut self, command_sender: Resource) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - resource.provider.should_receive_feedback() + async fn should_receive_feedback( + &mut self, + sender: Resource, + ) -> wasmtime::Result { + Ok(self + .get_sender_res(&sender)? + .provider + .should_receive_feedback()) } async fn should_broadcast_console_to_ops( &mut self, - command_sender: Resource, - ) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - resource.provider.should_broadcast_console_to_ops() + sender: Resource, + ) -> wasmtime::Result { + Ok(self + .get_sender_res(&sender)? + .provider + .should_broadcast_console_to_ops()) } - async fn should_track_output(&mut self, command_sender: Resource) -> bool { - let resource = self - .resource_table - .get::(&Resource::new_own(command_sender.rep())) - .expect("invalid command-sender resource handle"); - - resource.provider.should_track_output() + async fn should_track_output( + &mut self, + sender: Resource, + ) -> wasmtime::Result { + Ok(self.get_sender_res(&sender)?.provider.should_track_output()) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { self.resource_table - .delete::(Resource::new_own(rep.rep()))?; + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; Ok(()) } } -impl DowncastResourceExt for Resource { - fn downcast_ref<'a>(&'a self, state: &'a mut PluginHostState) -> &'a CommandNodeResource { - state - .resource_table - .get_any_mut(self.rep()) - .expect("invalid command-node resource handle") - .downcast_ref() - .expect("resource type mismatch") - } - - fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut CommandNodeResource { - state - .resource_table - .get_any_mut(self.rep()) - .expect("invalid command-node resource handle") - .downcast_mut() - .expect("resource type mismatch") - } - - fn consume(self, state: &mut PluginHostState) -> CommandNodeResource { - state - .resource_table - .delete(Resource::new_own(self.rep())) - .expect("invalid command-node resource handle") - } -} - -fn bounded_num_argument( - state: &mut PluginHostState, - name: String, - min: Option, - max: Option, -) -> Resource -where - BoundedNumArgumentConsumer: GetClientSideArgParser, -{ - let mut consumer = BoundedNumArgumentConsumer::::new(); - if let Some(min) = min { - consumer = consumer.min(min); - } - if let Some(max) = max { - consumer = consumer.max(max); - } - state.add_command_node(argument(name, consumer)).unwrap() -} - impl pumpkin::plugin::command::HostCommandNode for PluginHostState { - async fn literal(&mut self, name: String) -> Resource { - self.add_command_node(literal(name)).unwrap() + async fn literal(&mut self, name: String) -> wasmtime::Result> { + self.add_command_node(literal(name)) + .map_err(|_| wasmtime::Error::msg("Failed to add literal node")) } - async fn argument(&mut self, name: String, arg_type: ArgumentType) -> Resource { - match arg_type { - ArgumentType::Bool => self - .add_command_node(argument(name, BoolArgConsumer)) - .unwrap(), - ArgumentType::Float((min, max)) => bounded_num_argument(self, name, min, max), - ArgumentType::Double((min, max)) => bounded_num_argument(self, name, min, max), - ArgumentType::Integer((min, max)) => bounded_num_argument(self, name, min, max), - ArgumentType::Long((min, max)) => bounded_num_argument(self, name, min, max), - ArgumentType::String(string_type) => match string_type { - StringType::SingleWord | StringType::Quotable => self - .add_command_node(argument(name, SimpleArgConsumer)) - .unwrap(), - StringType::Greedy => self - .add_command_node(argument(name, MsgArgConsumer)) - .unwrap(), + async fn argument( + &mut self, + name: String, + arg_type: ArgumentType, + ) -> wasmtime::Result> { + let node = match arg_type { + ArgumentType::Bool => argument(name, BoolArgConsumer), + ArgumentType::Float((min, max)) => build_bounded_node::(name, min, max), + ArgumentType::Double((min, max)) => build_bounded_node::(name, min, max), + ArgumentType::Integer((min, max)) => build_bounded_node::(name, min, max), + ArgumentType::Long((min, max)) => build_bounded_node::(name, min, max), + ArgumentType::String(st) => match st { + StringType::SingleWord | StringType::Quotable => argument(name, SimpleArgConsumer), + StringType::Greedy => argument(name, MsgArgConsumer), }, - ArgumentType::Entities => self - .add_command_node(argument(name, EntitiesArgumentConsumer)) - .unwrap(), - ArgumentType::Entity => self - .add_command_node(argument(name, EntityArgumentConsumer)) - .unwrap(), - ArgumentType::Players | ArgumentType::GameProfile => self - .add_command_node(argument(name, PlayersArgumentConsumer)) - .unwrap(), - ArgumentType::BlockPos => self - .add_command_node(argument(name, BlockPosArgumentConsumer)) - .unwrap(), - ArgumentType::Position3d => self - .add_command_node(argument(name, Position3DArgumentConsumer)) - .unwrap(), - ArgumentType::Position2d => self - .add_command_node(argument(name, Position2DArgumentConsumer)) - .unwrap(), - ArgumentType::BlockState => self - .add_command_node(argument(name, BlockArgumentConsumer)) - .unwrap(), - ArgumentType::BlockPredicate => self - .add_command_node(argument(name, BlockPredicateArgumentConsumer)) - .unwrap(), - ArgumentType::Item => self - .add_command_node(argument(name, ItemArgumentConsumer)) - .unwrap(), - ArgumentType::ItemPredicate => self - .add_command_node(argument(name, ItemPredicateArgumentConsumer)) - .unwrap(), - ArgumentType::Component => self - .add_command_node(argument(name, TextComponentArgConsumer)) - .unwrap(), - ArgumentType::Rotation => self - .add_command_node(argument(name, RotationArgumentConsumer)) - .unwrap(), - ArgumentType::ResourceLocation | ArgumentType::Resource(_) => self - .add_command_node(argument(name, ResourceLocationArgumentConsumer)) - .unwrap(), - ArgumentType::EntityAnchor => self - .add_command_node(argument(name, EntityAnchorArgumentConsumer)) - .unwrap(), - ArgumentType::Gamemode => self - .add_command_node(argument(name, GamemodeArgumentConsumer)) - .unwrap(), - ArgumentType::Difficulty => self - .add_command_node(argument(name, DifficultyArgumentConsumer)) - .unwrap(), - ArgumentType::Time(_) => self - .add_command_node(argument(name, TimeArgumentConsumer)) - .unwrap(), - } + ArgumentType::Entities => argument(name, EntitiesArgumentConsumer), + ArgumentType::Entity => argument(name, EntityArgumentConsumer), + ArgumentType::Players | ArgumentType::GameProfile => { + argument(name, PlayersArgumentConsumer) + } + ArgumentType::BlockPos => argument(name, BlockPosArgumentConsumer), + ArgumentType::Position3d => argument(name, Position3DArgumentConsumer), + ArgumentType::Position2d => argument(name, Position2DArgumentConsumer), + ArgumentType::BlockState => argument(name, BlockArgumentConsumer), + ArgumentType::BlockPredicate => argument(name, BlockPredicateArgumentConsumer), + ArgumentType::Item => argument(name, ItemArgumentConsumer), + ArgumentType::ItemPredicate => argument(name, ItemPredicateArgumentConsumer), + ArgumentType::Component => argument(name, TextComponentArgConsumer), + ArgumentType::Rotation => argument(name, RotationArgumentConsumer), + ArgumentType::ResourceLocation | ArgumentType::Resource(_) => { + argument(name, ResourceLocationArgumentConsumer) + } + ArgumentType::EntityAnchor => argument(name, EntityAnchorArgumentConsumer), + ArgumentType::Gamemode => argument(name, GamemodeArgumentConsumer), + ArgumentType::Difficulty => argument(name, DifficultyArgumentConsumer), + ArgumentType::Time(_) => argument(name, TimeArgumentConsumer), + }; + self.add_command_node(node) + .map_err(|_| wasmtime::Error::msg("Failed to add argument node")) } async fn then( &mut self, - self_command_node: Resource, + self_node: Resource, node: Resource, - ) { - let child_resource = node.consume(self); - let parent_resource = self_command_node.downcast_mut(self); - let builder = std::mem::replace(&mut parent_resource.provider, literal("")); - parent_resource.provider = builder.then(child_resource.provider); + ) -> wasmtime::Result<()> { + let child = self.take_node(&node)?; + let parent = self.get_node_mut(&self_node)?; + let builder = std::mem::replace(&mut parent.provider, literal("")); + parent.provider = builder.then(child.provider); + Ok(()) } async fn execute_with_handler_id( &mut self, - command_node: Resource, + node: Resource, handler_id: u32, - ) { + ) -> wasmtime::Result<()> { let plugin = self .plugin .as_ref() - .expect("plugin should always be initialized here") - .upgrade() - .expect("plugin has been dropped"); - + .and_then(std::sync::Weak::upgrade) + .ok_or_else(|| wasmtime::Error::msg("Plugin dropped"))?; let server = self .server .clone() - .expect("server should be set before command registration"); + .ok_or_else(|| wasmtime::Error::msg("Server not initialized"))?; let executor = WasmCommandExecutor { handler_id, plugin, server, }; - - let resource = command_node.downcast_mut(self); - // Unless we make the native command registration code less convenient to use, this is our best option + let resource = self.get_node_mut(&node)?; let builder = std::mem::replace(&mut resource.provider, literal("")); resource.provider = builder.execute(executor); + Ok(()) } async fn require_with_handler_id( &mut self, - _command_node: Resource, + _node: Resource, _handler_id: u32, - ) { - todo!() + ) -> wasmtime::Result<()> { + todo!("Implement require_with_handler_id") } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { self.resource_table - .delete::(Resource::new_own(rep.rep()))?; + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; Ok(()) } } + +fn build_bounded_node( + name: String, + min: Option, + max: Option, +) -> NonLeafNodeBuilder +where + BoundedNumArgumentConsumer: GetClientSideArgParser, +{ + let mut consumer = BoundedNumArgumentConsumer::::new(); + if let Some(m) = min { + consumer = consumer.min(m); + } + if let Some(m) = max { + consumer = consumer.max(m); + } + + argument(name, consumer) +} + +#[expect(clippy::too_many_lines)] +const fn map_util_locale_to_wit(locale: pumpkin_util::translation::Locale) -> Locale { + match locale { + pumpkin_util::translation::Locale::AfZa => Locale::AfZa, + pumpkin_util::translation::Locale::ArSa => Locale::ArSa, + pumpkin_util::translation::Locale::AstEs => Locale::AstEs, + pumpkin_util::translation::Locale::AzAz => Locale::AzAz, + pumpkin_util::translation::Locale::BaRu => Locale::BaRu, + pumpkin_util::translation::Locale::Bar => Locale::Bar, + pumpkin_util::translation::Locale::BeBy => Locale::BeBy, + pumpkin_util::translation::Locale::BgBg => Locale::BgBg, + pumpkin_util::translation::Locale::BrFr => Locale::BrFr, + pumpkin_util::translation::Locale::Brb => Locale::Brb, + pumpkin_util::translation::Locale::BsBa => Locale::BsBa, + pumpkin_util::translation::Locale::CaEs => Locale::CaEs, + pumpkin_util::translation::Locale::CsCz => Locale::CsCz, + pumpkin_util::translation::Locale::CyGb => Locale::CyGb, + pumpkin_util::translation::Locale::DaDk => Locale::DaDk, + pumpkin_util::translation::Locale::DeAt => Locale::DeAt, + pumpkin_util::translation::Locale::DeCh => Locale::DeCh, + pumpkin_util::translation::Locale::DeDe => Locale::DeDe, + pumpkin_util::translation::Locale::ElGr => Locale::ElGr, + pumpkin_util::translation::Locale::EnAu => Locale::EnAu, + pumpkin_util::translation::Locale::EnCa => Locale::EnCa, + pumpkin_util::translation::Locale::EnGb => Locale::EnGb, + pumpkin_util::translation::Locale::EnNz => Locale::EnNz, + pumpkin_util::translation::Locale::EnPt => Locale::EnPt, + pumpkin_util::translation::Locale::EnUd => Locale::EnUd, + pumpkin_util::translation::Locale::EnUs => Locale::EnUs, + pumpkin_util::translation::Locale::Enp => Locale::Enp, + pumpkin_util::translation::Locale::Enws => Locale::Enws, + pumpkin_util::translation::Locale::EoUy => Locale::EoUy, + pumpkin_util::translation::Locale::EsAr => Locale::EsAr, + pumpkin_util::translation::Locale::EsCl => Locale::EsCl, + pumpkin_util::translation::Locale::EsEc => Locale::EsEc, + pumpkin_util::translation::Locale::EsEs => Locale::EsEs, + pumpkin_util::translation::Locale::EsMx => Locale::EsMx, + pumpkin_util::translation::Locale::EsUy => Locale::EsUy, + pumpkin_util::translation::Locale::EsVe => Locale::EsVe, + pumpkin_util::translation::Locale::Esan => Locale::Esan, + pumpkin_util::translation::Locale::EtEe => Locale::EtEe, + pumpkin_util::translation::Locale::EuEs => Locale::EuEs, + pumpkin_util::translation::Locale::FaIr => Locale::FaIr, + pumpkin_util::translation::Locale::FiFi => Locale::FiFi, + pumpkin_util::translation::Locale::FilPh => Locale::FilPh, + pumpkin_util::translation::Locale::FoFo => Locale::FoFo, + pumpkin_util::translation::Locale::FrCa => Locale::FrCa, + pumpkin_util::translation::Locale::FrFr => Locale::FrFr, + pumpkin_util::translation::Locale::FraDe => Locale::FraDe, + pumpkin_util::translation::Locale::FurIt => Locale::FurIt, + pumpkin_util::translation::Locale::FyNl => Locale::FyNl, + pumpkin_util::translation::Locale::GaIe => Locale::GaIe, + pumpkin_util::translation::Locale::GdGb => Locale::GdGb, + pumpkin_util::translation::Locale::GlEs => Locale::GlEs, + pumpkin_util::translation::Locale::HawUs => Locale::HawUs, + pumpkin_util::translation::Locale::HeIl => Locale::HeIl, + pumpkin_util::translation::Locale::HiIn => Locale::HiIn, + pumpkin_util::translation::Locale::HrHr => Locale::HrHr, + pumpkin_util::translation::Locale::HuHu => Locale::HuHu, + pumpkin_util::translation::Locale::HyAm => Locale::HyAm, + pumpkin_util::translation::Locale::IdId => Locale::IdId, + pumpkin_util::translation::Locale::IgNg => Locale::IgNg, + pumpkin_util::translation::Locale::IoEn => Locale::IoEn, + pumpkin_util::translation::Locale::IsIs => Locale::IsIs, + pumpkin_util::translation::Locale::Isv => Locale::Isv, + pumpkin_util::translation::Locale::ItIt => Locale::ItIt, + pumpkin_util::translation::Locale::JaJp => Locale::JaJp, + pumpkin_util::translation::Locale::JboEn => Locale::JboEn, + pumpkin_util::translation::Locale::KaGe => Locale::KaGe, + pumpkin_util::translation::Locale::KkKz => Locale::KkKz, + pumpkin_util::translation::Locale::KnIn => Locale::KnIn, + pumpkin_util::translation::Locale::KoKr => Locale::KoKr, + pumpkin_util::translation::Locale::Ksh => Locale::Ksh, + pumpkin_util::translation::Locale::KwGb => Locale::KwGb, + pumpkin_util::translation::Locale::LaLa => Locale::LaLa, + pumpkin_util::translation::Locale::LbLu => Locale::LbLu, + pumpkin_util::translation::Locale::LiLi => Locale::LiLi, + pumpkin_util::translation::Locale::Lmo => Locale::Lmo, + pumpkin_util::translation::Locale::LoLa => Locale::LoLa, + pumpkin_util::translation::Locale::LolUs => Locale::LolUs, + pumpkin_util::translation::Locale::LtLt => Locale::LtLt, + pumpkin_util::translation::Locale::LvLv => Locale::LvLv, + pumpkin_util::translation::Locale::Lzh => Locale::Lzh, + pumpkin_util::translation::Locale::MkMk => Locale::MkMk, + pumpkin_util::translation::Locale::MnMn => Locale::MnMn, + pumpkin_util::translation::Locale::MsMy => Locale::MsMy, + pumpkin_util::translation::Locale::MtMt => Locale::MtMt, + pumpkin_util::translation::Locale::Nah => Locale::Nah, + pumpkin_util::translation::Locale::NdsDe => Locale::NdsDe, + pumpkin_util::translation::Locale::NlBe => Locale::NlBe, + pumpkin_util::translation::Locale::NlNl => Locale::NlNl, + pumpkin_util::translation::Locale::NnNo => Locale::NnNo, + pumpkin_util::translation::Locale::NoNo => Locale::NoNo, + pumpkin_util::translation::Locale::OcFr => Locale::OcFr, + pumpkin_util::translation::Locale::Ovd => Locale::Ovd, + pumpkin_util::translation::Locale::PlPl => Locale::PlPl, + pumpkin_util::translation::Locale::PtBr => Locale::PtBr, + pumpkin_util::translation::Locale::PtPt => Locale::PtPt, + pumpkin_util::translation::Locale::QyaAa => Locale::QyaAa, + pumpkin_util::translation::Locale::RoRo => Locale::RoRo, + pumpkin_util::translation::Locale::Rpr => Locale::Rpr, + pumpkin_util::translation::Locale::RuRu => Locale::RuRu, + pumpkin_util::translation::Locale::RyUa => Locale::RyUa, + pumpkin_util::translation::Locale::SahSah => Locale::SahSah, + pumpkin_util::translation::Locale::SeNo => Locale::SeNo, + pumpkin_util::translation::Locale::SkSk => Locale::SkSk, + pumpkin_util::translation::Locale::SlSi => Locale::SlSi, + pumpkin_util::translation::Locale::SoSo => Locale::SoSo, + pumpkin_util::translation::Locale::SqAl => Locale::SqAl, + pumpkin_util::translation::Locale::SrCs => Locale::SrCs, + pumpkin_util::translation::Locale::SrSp => Locale::SrSp, + pumpkin_util::translation::Locale::SvSe => Locale::SvSe, + pumpkin_util::translation::Locale::Sxu => Locale::Sxu, + pumpkin_util::translation::Locale::Szl => Locale::Szl, + pumpkin_util::translation::Locale::TaIn => Locale::TaIn, + pumpkin_util::translation::Locale::ThTh => Locale::ThTh, + pumpkin_util::translation::Locale::TlPh => Locale::TlPh, + pumpkin_util::translation::Locale::TlhAa => Locale::TlhAa, + pumpkin_util::translation::Locale::Tok => Locale::Tok, + pumpkin_util::translation::Locale::TrTr => Locale::TrTr, + pumpkin_util::translation::Locale::TtRu => Locale::TtRu, + pumpkin_util::translation::Locale::UkUa => Locale::UkUa, + pumpkin_util::translation::Locale::ValEs => Locale::ValEs, + pumpkin_util::translation::Locale::VecIt => Locale::VecIt, + pumpkin_util::translation::Locale::ViVn => Locale::ViVn, + pumpkin_util::translation::Locale::YiDe => Locale::YiDe, + pumpkin_util::translation::Locale::YoNg => Locale::YoNg, + pumpkin_util::translation::Locale::ZhCn => Locale::ZhCn, + pumpkin_util::translation::Locale::ZhHk => Locale::ZhHk, + pumpkin_util::translation::Locale::ZhTw => Locale::ZhTw, + pumpkin_util::translation::Locale::ZlmArab => Locale::ZlmArab, + } +} diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/context.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/context.rs index 50a37aa9e..f4df4f351 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/context.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/context.rs @@ -125,6 +125,21 @@ async fn register_player_event( } } +impl PluginHostState { + fn get_context(&self, res: &Resource) -> wasmtime::Result<&ContextResource> { + self.resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + + fn take_command(&mut self, res: &Resource) -> wasmtime::Result { + self.resource_table + .delete::(Resource::new_own(res.rep())) + // Convert ResourceTableError -> wasmtime::Error + .map_err(wasmtime::Error::from) + } +} + async fn register_world_event( resource: &ContextResource, handler: &Arc, @@ -237,7 +252,8 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { event_type: EventType, event_priority: EventPriority, blocking: bool, - ) { + ) -> wasmtime::Result<()> { + // Updated return type let priority = match event_priority { EventPriority::Highest => crate::plugin::EventPriority::Highest, EventPriority::High => crate::plugin::EventPriority::High, @@ -246,14 +262,15 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { EventPriority::Lowest => crate::plugin::EventPriority::Lowest, }; + // Use ? to trap if the plugin was dropped or the context handle is dead let plugin = self .plugin .as_ref() - .expect("plugin should always be initialized here") + .ok_or_else(|| wasmtime::Error::msg("Plugin state uninitialized"))? .upgrade() - .expect("plugin has been dropped"); + .ok_or_else(|| wasmtime::Error::msg("Plugin has been dropped"))?; - let resource = context.downcast_ref(self); + let resource = self.get_context(&context)?; let handler = Arc::new(WasmPluginV0_1_0EventHandler { handler_id, plugin }); match event_type { @@ -275,6 +292,8 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { register_player_event(resource, &handler, priority, blocking, event_type).await; } } + + Ok(()) } async fn register_command( @@ -282,44 +301,37 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { context: Resource, command: Resource, permission: String, - ) { - let command = self - .resource_table - .delete::(Resource::new_own(command.rep())) - .expect("invalid command resource handle") - .provider; + ) -> wasmtime::Result<()> { + // Updated return type + // Use your helpers to safely take/get resources + let command_res = self.take_command(&command)?; + let context_res = self.get_context(&context)?; - let context_resource = self - .resource_table - .get_any_mut(context.rep()) - .expect("invalid context resource handle") - .downcast_ref::() - .expect("resource type mismatch"); - - context_resource + context_res .provider - .register_command(command, permission) + .register_command(command_res.provider, permission) .await; + Ok(()) } async fn register_permission( &mut self, context: Resource, permission: Permission, - ) -> Result<(), String> { - let mut children: HashMap = HashMap::with_capacity(permission.children.len()); + ) -> wasmtime::Result> { + let mut children = HashMap::with_capacity(permission.children.len()); for child in permission.children { children.insert(child.node, child.value); } - let permission = pumpkin_util::permission::Permission { + let util_permission = pumpkin_util::permission::Permission { node: permission.node, description: permission.description, default: match permission.default { PermissionDefault::Deny => pumpkin_util::permission::PermissionDefault::Deny, PermissionDefault::Allow => pumpkin_util::permission::PermissionDefault::Allow, - PermissionDefault::Op(permission_level) => { - pumpkin_util::permission::PermissionDefault::Op(match permission_level { + PermissionDefault::Op(lvl) => { + pumpkin_util::permission::PermissionDefault::Op(match lvl { PermissionLevel::Zero => pumpkin_util::permission::PermissionLvl::Zero, PermissionLevel::One => pumpkin_util::permission::PermissionLvl::One, PermissionLevel::Two => pumpkin_util::permission::PermissionLvl::Two, @@ -331,26 +343,31 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { children, }; - context - .downcast_mut(self) + let context_res = self + .resource_table + .get_mut::(&Resource::new_own(context.rep()))?; + Ok(context_res .provider - .register_permission(permission) - .await + .register_permission(util_permission) + .await) } - async fn get_data_folder(&mut self, context: Resource) -> String { - context - .downcast_ref(self) + async fn get_data_folder(&mut self, context: Resource) -> wasmtime::Result { + Ok(self + .get_context(&context)? .provider .get_data_folder() .to_string_lossy() - .into_owned() + .into_owned()) } - async fn get_server(&mut self, context: Resource) -> Resource { - let server_provider = context.downcast_ref(self).provider.server.clone(); + async fn get_server( + &mut self, + context: Resource, + ) -> wasmtime::Result> { + let server_provider = self.get_context(&context)?.provider.server.clone(); self.add_server(server_provider) - .expect("failed to add server resource") + .map_err(|_| wasmtime::Error::msg("failed to add server resource")) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/entity.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/entity.rs index 7c1749ce7..1125b4818 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/entity.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/entity.rs @@ -14,68 +14,77 @@ use crate::plugin::loader::wasm::wasm_host::{ impl pumpkin::plugin::entity::Host for PluginHostState {} impl pumpkin::plugin::entity::HostBlockEntity for PluginHostState { - async fn resource_location(&mut self, _block_entity: Resource) -> String { - todo!() + async fn resource_location(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement resource_location lookup") } - async fn get_position(&mut self, _block_entity: Resource) -> BlockPosition { - todo!() + async fn get_position( + &mut self, + _res: Resource, + ) -> wasmtime::Result { + todo!("Implement get_position lookup") } - async fn get_id(&mut self, _block_entity: Resource) -> u32 { - todo!() + async fn get_id(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement get_id lookup") } - async fn is_dirty(&mut self, _block_entity: Resource) -> bool { - todo!() + async fn is_dirty(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement is_dirty lookup") } - async fn clear_dirty(&mut self, _block_entity: Resource) { - todo!() + async fn clear_dirty(&mut self, _res: Resource) -> wasmtime::Result<()> { + todo!("Implement clear_dirty logic") } async fn drop(&mut self, _rep: Resource) -> wasmtime::Result<()> { - todo!() + todo!(); } } impl pumpkin::plugin::entity::HostCommandBlockEntity for PluginHostState { async fn get_block_entity( &mut self, - _command_block_entity: Resource, - ) -> Resource { - todo!() + _res: Resource, + ) -> wasmtime::Result> { + todo!("Implement upcasting/mapping to BlockEntity") } - async fn last_output(&mut self, _command_block_entity: Resource) -> String { - todo!() + async fn last_output( + &mut self, + _res: Resource, + ) -> wasmtime::Result { + todo!("Implement last_output lookup") } - async fn track_output(&mut self, _command_block_entity: Resource) -> bool { - todo!() + async fn track_output(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement track_output lookup") } - async fn success_count(&mut self, _command_block_entity: Resource) -> u32 { - todo!() + async fn success_count(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement success_count lookup") } - async fn command(&mut self, _command_block_entity: Resource) -> String { - todo!() + async fn command(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement command lookup") } - async fn auto(&mut self, _command_block_entity: Resource) -> bool { - todo!() + async fn auto(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement auto lookup") } - async fn condition_met(&mut self, _command_block_entity: Resource) -> bool { - todo!() + async fn condition_met( + &mut self, + _res: Resource, + ) -> wasmtime::Result { + todo!("Implement condition_met lookup") } - async fn powered(&mut self, _command_block_entity: Resource) -> bool { - todo!() + async fn powered(&mut self, _res: Resource) -> wasmtime::Result { + todo!("Implement powered lookup") } async fn drop(&mut self, _rep: Resource) -> wasmtime::Result<()> { - todo!() + todo!(); } } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/i18n.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/i18n.rs index 2e3da9406..2eabf90e3 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/i18n.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/i18n.rs @@ -6,27 +6,31 @@ use pumpkin_util::translation::{Locale as UtilLocale, add_translation_file, get_ use std::str::FromStr; impl Host for PluginHostState { - async fn translate(&mut self, key: String, locale: WitLocale) -> String { + async fn translate(&mut self, key: String, locale: WitLocale) -> wasmtime::Result { let util_locale = wit_to_util_locale(locale); - get_translation(&key, util_locale) + Ok(get_translation(&key, util_locale)) } - async fn load_translations(&mut self, namespace: String, json: String, locale: WitLocale) { + async fn load_translations( + &mut self, + namespace: String, + json: String, + locale: WitLocale, + ) -> wasmtime::Result<()> { let util_locale = wit_to_util_locale(locale); add_translation_file(namespace, json, util_locale); + Ok(()) } } /// Converts a WIT Locale to a pumpkin-util Locale. fn wit_to_util_locale(wit: WitLocale) -> UtilLocale { - // WIT names are kebab-case (e.g., AfZa -> af-za in .wit, but in generated Rust it might vary) - // We can use the debug representation or a custom mapping. - // Given the amount of variants, we use a string-based approach if possible. + // WIT variants like EnUs often debug to "EnUs". + // We convert to lowercase and handle potential format differences. let s = format!("{wit:?}").to_lowercase(); - // pumpkin-util::Locale::from_str expects "af_za" or similar. - // WIT might generate "AfZa" or "af_za" depending on bindgen config. - // Let's assume standard bindgen which might produce "AfZa". - // We'll replace kebab-case if necessary. - let s = s.replace('-', "_"); + + // Most translation systems expect underscores (en_us) rather than nothing or dashes. + // If the WIT Debug format is "EnUs", lowercase is "enus". + // We might need a smarter mapping if your util expects specifically "en_us". UtilLocale::from_str(&s).unwrap_or(UtilLocale::EnUs) } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/logging.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/logging.rs index 5f80fe8b3..c994436f7 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/logging.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/logging.rs @@ -3,7 +3,11 @@ use crate::plugin::loader::wasm::wasm_host::{ }; impl pumpkin::plugin::logging::Host for PluginHostState { - async fn log(&mut self, level: pumpkin::plugin::logging::Level, message: String) { + async fn log( + &mut self, + level: pumpkin::plugin::logging::Level, + message: String, + ) -> wasmtime::Result<()> { match level { pumpkin::plugin::logging::Level::Trace => tracing::trace!("[plugin] {message}"), pumpkin::plugin::logging::Level::Debug => tracing::debug!("[plugin] {message}"), @@ -11,9 +15,11 @@ impl pumpkin::plugin::logging::Host for PluginHostState { pumpkin::plugin::logging::Level::Warn => tracing::warn!("[plugin] {message}"), pumpkin::plugin::logging::Level::Error => tracing::error!("[plugin] {message}"), } + Ok(()) } - async fn log_tracing(&mut self, event: Vec) { + async fn log_tracing(&mut self, event: Vec) -> wasmtime::Result<()> { log_tracing(event).await; + Ok(()) } } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/mod.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/mod.rs index e15c3a57b..464d8b43d 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/mod.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/mod.rs @@ -22,8 +22,8 @@ pub mod world; bindgen!({ path: "../pumpkin-plugin-wit/v0.1.0", world: "plugin", - imports: { default: async }, - exports: { default: async }, + imports: { default: async | trappable }, + exports: { default: async | trappable}, }); struct PluginHostComponent; diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/player.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/player.rs index 4f2fe6a76..3d4a7d468 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/player.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/player.rs @@ -21,34 +21,36 @@ use pumpkin_util::permission::PermissionLvl; fn player_from_resource( state: &PluginHostState, player: &Resource, -) -> Result, String> { +) -> wasmtime::Result> { state .resource_table .get::(&Resource::new_own(player.rep())) - .map_err(|_| "invalid player resource handle".to_string()) + .map_err(|_| wasmtime::Error::msg("invalid player resource handle")) .map(|resource| resource.provider.clone()) } fn text_component_from_resource( state: &PluginHostState, text: &Resource, -) -> Result { +) -> pumpkin_util::text::TextComponent { state .resource_table .get::(&Resource::new_own(text.rep())) - .map_err(|_| "invalid text-component resource handle".to_string()) - .map(|resource| resource.provider.clone()) + .expect("invalid text-component resource handle") + .provider + .clone() } fn world_from_resource( state: &PluginHostState, world: &Resource, -) -> Result, String> { +) -> std::sync::Arc { state .resource_table .get::(&Resource::new_own(world.rep())) - .map_err(|_| "invalid world resource handle".to_string()) - .map(|resource| resource.provider.clone()) + .expect("invalid world resource handle") + .provider + .clone() } fn to_wit_item_stack( @@ -124,12 +126,12 @@ impl DowncastResourceExt for Resource { impl pumpkin::plugin::player::Host for PluginHostState {} impl pumpkin::plugin::player::HostPlayer for PluginHostState { - async fn get_id(&mut self, player: Resource) -> Result { + async fn get_id(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.gameprofile.id.to_string()) } - async fn get_name(&mut self, player: Resource) -> Result { + async fn get_name(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.gameprofile.name.clone()) } @@ -137,13 +139,12 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn get_position( &mut self, player: Resource, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let position = player.position(); - Ok(to_wasm_position(position)) + Ok(to_wasm_position(player.position())) } - async fn get_rotation(&mut self, player: Resource) -> Result<(f32, f32), String> { + async fn get_rotation(&mut self, player: Resource) -> wasmtime::Result<(f32, f32)> { let player = player_from_resource(self, &player)?; Ok(player.rotation()) } @@ -151,39 +152,36 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn get_world( &mut self, player: Resource, - ) -> Result, String> { + ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; let world = player.world(); self.add_world(world) - .map_err(|_| "failed to add world resource".to_string()) + .map_err(|_| wasmtime::Error::msg("failed to add world resource")) } async fn get_gamemode( &mut self, player: Resource, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let gamemode = player.gamemode.load(); - Ok(to_wasm_game_mode(gamemode)) + Ok(to_wasm_game_mode(player.gamemode.load())) } async fn set_gamemode( &mut self, player: Resource, mode: pumpkin::plugin::common::GameMode, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let mode = from_wasm_game_mode(mode); - Ok(player.set_gamemode(mode).await) + Ok(player.set_gamemode(from_wasm_game_mode(mode)).await) } - async fn get_locale(&mut self, player: Resource) -> Result { + async fn get_locale(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let config = player.config.load(); - Ok(config.locale.clone()) + Ok(player.config.load().locale.clone()) } - async fn get_ping(&mut self, player: Resource) -> Result { + async fn get_ping(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.ping.load(Ordering::Relaxed)) } @@ -191,7 +189,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn get_permission_level( &mut self, player: Resource, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(to_wit_permission_level(player.permission_lvl.load())) } @@ -200,11 +198,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, level: pumpkin::plugin::permission::PermissionLevel, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - let Some(server) = self.server.as_ref() else { - return Err("server not available".to_string()); - }; + let server = self.server.as_ref().expect("server not available"); let level = from_wit_permission_level(level); let command_dispatcher = server.command_dispatcher.read().await; player @@ -217,22 +213,20 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, node: String, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; - let Some(server) = self.server.as_ref() else { - return Err("server not available".to_string()); - }; + let server = self.server.as_ref().expect("server not available"); Ok(player.has_permission(server, &node).await) } async fn get_display_name( &mut self, player: Resource, - ) -> Result, String> { + ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; let display_name = player.get_display_name().await; self.add_text_component(display_name) - .map_err(|_| "failed to add text-component resource".to_string()) + .map_err(|_| wasmtime::Error::msg("failed to add text-component resource")) } async fn send_system_message( @@ -240,8 +234,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { player: Resource, text: wasmtime::component::Resource, overlay: bool, - ) -> Result<(), String> { - let component = text_component_from_resource(self, &text)?; + ) -> wasmtime::Result<()> { + let component = text_component_from_resource(self, &text); let player = player_from_resource(self, &player)?; player.send_system_message_raw(&component, overlay).await; Ok(()) @@ -251,8 +245,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, text: wasmtime::component::Resource, - ) -> Result<(), String> { - let component = text_component_from_resource(self, &text)?; + ) -> wasmtime::Result<()> { + let component = text_component_from_resource(self, &text); let player = player_from_resource(self, &player)?; player.show_title(&component, &TitleMode::Title).await; Ok(()) @@ -262,8 +256,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, text: wasmtime::component::Resource, - ) -> Result<(), String> { - let component = text_component_from_resource(self, &text)?; + ) -> wasmtime::Result<()> { + let component = text_component_from_resource(self, &text); let player = player_from_resource(self, &player)?; player.show_title(&component, &TitleMode::SubTitle).await; Ok(()) @@ -273,8 +267,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, text: wasmtime::component::Resource, - ) -> Result<(), String> { - let component = text_component_from_resource(self, &text)?; + ) -> wasmtime::Result<()> { + let component = text_component_from_resource(self, &text); let player = player_from_resource(self, &player)?; player.show_title(&component, &TitleMode::ActionBar).await; Ok(()) @@ -286,7 +280,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { fade_in: i32, stay: i32, fade_out: i32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.send_title_animation(fade_in, stay, fade_out).await; Ok(()) @@ -298,10 +292,11 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { position: pumpkin::plugin::common::Position, yaw: f32, pitch: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - let position = from_wasm_position(position); - player.request_teleport(position, yaw, pitch).await; + player + .request_teleport(from_wasm_position(position), yaw, pitch) + .await; Ok(()) } @@ -312,11 +307,12 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { position: pumpkin::plugin::common::Position, yaw: Option, pitch: Option, - ) -> Result<(), String> { - let world = world_from_resource(self, &world)?; + ) -> wasmtime::Result<()> { + let world = world_from_resource(self, &world); let player = player_from_resource(self, &player)?; - let position = from_wasm_position(position); - player.teleport_world(world, position, yaw, pitch).await; + player + .teleport_world(world, from_wasm_position(position), yaw, pitch) + .await; Ok(()) } @@ -324,14 +320,14 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, message: wasmtime::component::Resource, - ) -> Result<(), String> { - let component = text_component_from_resource(self, &message)?; + ) -> wasmtime::Result<()> { + let component = text_component_from_resource(self, &message); let player = player_from_resource(self, &player)?; player.kick(DisconnectReason::Kicked, component).await; Ok(()) } - async fn respawn(&mut self, player: Resource) -> Result<(), String> { + async fn respawn(&mut self, player: Resource) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.respawn().await; Ok(()) @@ -341,15 +337,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, reason: Option>, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - let Some(server) = self.server.as_ref() else { - return Err("server not available".to_string()); - }; - let reason = match reason { - Some(r) => Some(text_component_from_resource(self, &r)?), - None => None, - }; + let server = self.server.as_ref().expect("server not available"); + let reason = reason.map(|r| text_component_from_resource(self, &r)); player.ban(server, reason).await; Ok(()) } @@ -358,20 +349,15 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, reason: Option>, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - let Some(server) = self.server.as_ref() else { - return Err("server not available".to_string()); - }; - let reason = match reason { - Some(r) => Some(text_component_from_resource(self, &r)?), - None => None, - }; + let server = self.server.as_ref().expect("server not available"); + let reason = reason.map(|r| text_component_from_resource(self, &r)); player.ban_ip(server, reason).await; Ok(()) } - async fn get_selected_slot(&mut self, player: Resource) -> Result { + async fn get_selected_slot(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.inventory.get_selected_slot()) } @@ -380,7 +366,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, hand: pumpkin::plugin::common::Hand, - ) -> Result, String> { + ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; let inventory = player.inventory(); let item_stack = match hand { @@ -395,29 +381,28 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, slot: u8, - ) -> Result, String> { + ) -> wasmtime::Result> { let player = player_from_resource(self, &player)?; let slot = slot as usize; if slot >= PlayerInventory::MAIN_SIZE { - return Err("invalid inventory slot".to_string()); + return Ok(None); } - let item_stack = player.inventory.main_inventory[slot].lock().await.clone(); Ok(to_wit_item_stack(&item_stack)) } - async fn get_health(&mut self, player: Resource) -> Result { + async fn get_health(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.living_entity.health.load()) } - async fn set_health(&mut self, player: Resource, health: f32) -> Result<(), String> { + async fn set_health(&mut self, player: Resource, health: f32) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_health(health).await; Ok(()) } - async fn get_max_health(&mut self, player: Resource) -> Result { + async fn get_max_health(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.living_entity.get_max_health()) } @@ -426,24 +411,28 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, max_health: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_max_health(max_health).await; Ok(()) } - async fn get_food_level(&mut self, player: Resource) -> Result { + async fn get_food_level(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.hunger_manager.level.load()) } - async fn set_food_level(&mut self, player: Resource, level: u8) -> Result<(), String> { + async fn set_food_level( + &mut self, + player: Resource, + level: u8, + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_food_level(level).await; Ok(()) } - async fn get_saturation(&mut self, player: Resource) -> Result { + async fn get_saturation(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.hunger_manager.saturation.load()) } @@ -452,13 +441,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, saturation: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_saturation(saturation).await; Ok(()) } - async fn get_exhaustion(&mut self, player: Resource) -> Result { + async fn get_exhaustion(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_exhaustion()) } @@ -467,13 +456,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, exhaustion: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_exhaustion(exhaustion).await; Ok(()) } - async fn get_absorption(&mut self, player: Resource) -> Result { + async fn get_absorption(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_absorption()) } @@ -482,23 +471,23 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, absorption: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_absorption(absorption).await; Ok(()) } - async fn get_experience_level(&mut self, player: Resource) -> Result { + async fn get_experience_level(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.experience_level.load(Ordering::Relaxed)) } - async fn get_experience_progress(&mut self, player: Resource) -> Result { + async fn get_experience_progress(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.experience_progress.load()) } - async fn get_experience_points(&mut self, player: Resource) -> Result { + async fn get_experience_points(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.experience_points.load(Ordering::Relaxed)) } @@ -507,7 +496,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, level: i32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.set_experience_level(level, true).await; Ok(()) @@ -517,7 +506,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, progress: f32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player .set_experience( @@ -533,7 +522,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, points: i32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player .set_experience( @@ -549,7 +538,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, levels: i32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.add_experience_levels(levels).await; Ok(()) @@ -559,13 +548,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, points: i32, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.add_experience_points(points).await; Ok(()) } - async fn is_sneaking(&mut self, player: Resource) -> Result { + async fn is_sneaking(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_entity().sneaking.load(Ordering::Relaxed)) } @@ -574,13 +563,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, sneaking: bool, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.get_entity().set_sneaking(sneaking).await; Ok(()) } - async fn is_sprinting(&mut self, player: Resource) -> Result { + async fn is_sprinting(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_entity().sprinting.load(Ordering::Relaxed)) } @@ -589,23 +578,23 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, sprinting: bool, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; player.get_entity().set_sprinting(sprinting).await; Ok(()) } - async fn is_on_ground(&mut self, player: Resource) -> Result { + async fn is_on_ground(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_entity().on_ground.load(Ordering::Relaxed)) } - async fn is_flying(&mut self, player: Resource) -> Result { + async fn is_flying(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.is_flying().await) } - async fn set_flying(&mut self, player: Resource, flying: bool) -> Result<(), String> { + async fn set_flying(&mut self, player: Resource, flying: bool) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; { let mut abilities = player.abilities.lock().await; @@ -618,7 +607,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn get_abilities( &mut self, player: Resource, - ) -> Result { + ) -> wasmtime::Result { let player = player_from_resource(self, &player)?; let abilities = player.abilities.lock().await; Ok(pumpkin::plugin::player::PlayerAbilities { @@ -636,7 +625,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { &mut self, player: Resource, abilities: pumpkin::plugin::player::PlayerAbilities, - ) -> Result<(), String> { + ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; { let mut a = player.abilities.lock().await; @@ -652,7 +641,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { Ok(()) } - async fn get_ip(&mut self, player: Resource) -> Result { + async fn get_ip(&mut self, player: Resource) -> wasmtime::Result { let player = player_from_resource(self, &player)?; Ok(player.get_ip().await) } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/server.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/server.rs index 999ca6fd4..a1ece84ee 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/server.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/server.rs @@ -8,47 +8,56 @@ use crate::plugin::loader::wasm::wasm_host::{ }, }; +impl PluginHostState { + fn get_server_res(&self, res: &Resource) -> wasmtime::Result<&ServerResource> { + self.resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } +} + impl pumpkin::plugin::server::Host for PluginHostState {} impl pumpkin::plugin::server::HostServer for PluginHostState { - async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - let _ = self - .resource_table - .delete::(Resource::new_own(rep.rep())); - Ok(()) - } + async fn get_difficulty(&mut self, res: Resource) -> wasmtime::Result { + let resource = self.get_server_res(&res)?; - async fn get_difficulty(&mut self, server: Resource) -> Difficulty { - let resource: &ServerResource = self - .resource_table - .get_any_mut(server.rep()) - .expect("invalid server resource handle") - .downcast_ref::() - .expect("resource type mismatch"); - - match resource.provider.get_difficulty() { + Ok(match resource.provider.get_difficulty() { pumpkin_util::Difficulty::Peaceful => Difficulty::Peaceful, pumpkin_util::Difficulty::Easy => Difficulty::Easy, pumpkin_util::Difficulty::Normal => Difficulty::Normal, pumpkin_util::Difficulty::Hard => Difficulty::Hard, - } + }) } - async fn get_player_count( - &mut self, - _server: Resource, - ) -> u32 { - let server = self.server.as_ref().expect("server not available"); - server.get_player_count() as u32 + async fn get_player_count(&mut self, _res: Resource) -> wasmtime::Result { + let server = self + .server + .as_ref() + .ok_or_else(|| wasmtime::Error::msg("Server not available"))?; + Ok(server.get_player_count() as u32) } - async fn get_mspt(&mut self, _server: Resource) -> f64 { - let server = self.server.as_ref().expect("server not available"); - server.get_mspt() + async fn get_mspt(&mut self, _res: Resource) -> wasmtime::Result { + let server = self + .server + .as_ref() + .ok_or_else(|| wasmtime::Error::msg("Server not available"))?; + Ok(server.get_mspt()) } - async fn get_tps(&mut self, _server: Resource) -> f64 { - let server = self.server.as_ref().expect("server not available"); - server.get_tps() + async fn get_tps(&mut self, _res: Resource) -> wasmtime::Result { + let server = self + .server + .as_ref() + .ok_or_else(|| wasmtime::Error::msg("Server not available"))?; + Ok(server.get_tps()) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) } } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/text.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/text.rs index e9efa3b8b..1b48cb5ab 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/text.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/text.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; - use wasmtime::component::Resource; use crate::plugin::loader::wasm::wasm_host::{ @@ -12,39 +11,309 @@ use crate::plugin::loader::wasm::wasm_host::{ }; use pumpkin_util::text::{ + TextComponent as InternalTextComponent, click::ClickEvent, color::{self, Color}, hover::HoverEvent, }; -impl pumpkin::plugin::text::Host for PluginHostState {} +// --- Trapping Helpers --- +impl PluginHostState { + fn get_text_ref( + &self, + res: &Resource, + ) -> wasmtime::Result<&TextComponentResource> { + self.resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } -// TODO - Change the pumpkin_util::text::TextComponent to use &mut self instead of self for the builder pattern. -// right now we have to do a bunch of cloning due to the fact that the builder pattern doesn't accept &mut self. -impl DowncastResourceExt for Resource { + fn get_text_mut( + &mut self, + res: &Resource, + ) -> wasmtime::Result<&mut TextComponentResource> { + self.resource_table + .get_mut::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } + + fn take_text( + &mut self, + res: &Resource, + ) -> wasmtime::Result { + self.resource_table + .delete::(Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) + } +} + +impl DowncastResourceExt for wasmtime::component::Resource { fn downcast_ref<'a>(&'a self, state: &'a mut PluginHostState) -> &'a TextComponentResource { state .resource_table .get_any_mut(self.rep()) - .expect("invalid text-component resource handle") - .downcast_ref::() - .expect("resource type mismatch") + .expect("invalid handle") + .downcast_ref() + .expect("type mismatch") } fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut TextComponentResource { state .resource_table .get_any_mut(self.rep()) - .expect("invalid text-component resource handle") - .downcast_mut::() - .expect("resource type mismatch") + .expect("invalid handle") + .downcast_mut() + .expect("type mismatch") } fn consume(self, state: &mut PluginHostState) -> TextComponentResource { state .resource_table - .delete::(Resource::new_own(self.rep())) - .expect("invalid text-component resource handle") + .delete(wasmtime::component::Resource::new_own(self.rep())) + .expect("invalid handle") + } +} + +impl pumpkin::plugin::text::Host for PluginHostState {} + +impl pumpkin::plugin::text::HostTextComponent for PluginHostState { + async fn text(&mut self, plain: String) -> wasmtime::Result> { + let tc = InternalTextComponent::text(plain); + self.add_text_component(tc) + .map_err(|_| wasmtime::Error::msg("Failed to add text component")) + } + + async fn translate( + &mut self, + key: String, + with: Vec>, + ) -> wasmtime::Result> { + let mut components = Vec::with_capacity(with.len()); + for r in with { + components.push(self.take_text(&r)?.provider); + } + let tc = InternalTextComponent::translate(key, components); + self.add_text_component(tc) + .map_err(|_| wasmtime::Error::msg("Failed to add text component")) + } + + async fn add_child( + &mut self, + text_component: Resource, + child: Resource, + ) -> wasmtime::Result<()> { + let child_tc = self.take_text(&child)?.provider; + let parent = self.get_text_mut(&text_component)?; + // Cloning here as noted in your TODO until builder pattern supports &mut self + parent.provider = parent.provider.clone().add_child(child_tc); + Ok(()) + } + + async fn add_text( + &mut self, + text_component: Resource, + text: String, + ) -> wasmtime::Result<()> { + let parent = self.get_text_mut(&text_component)?; + parent.provider = parent.provider.clone().add_text(text); + Ok(()) + } + + async fn get_text( + &mut self, + text_component: Resource, + ) -> wasmtime::Result { + Ok(self + .get_text_ref(&text_component)? + .provider + .clone() + .get_text()) + } + + async fn encode( + &mut self, + text_component: Resource, + ) -> wasmtime::Result> { + Ok(self + .get_text_ref(&text_component)? + .provider + .encode() + .into_vec()) + } + + async fn color_named( + &mut self, + res: Resource, + color: NamedColor, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.color = + Some(Color::Named(map_named_color(color))); + Ok(()) + } + + async fn color_rgb( + &mut self, + res: Resource, + color: RgbColor, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.color = + Some(Color::Rgb(color::RGBColor::new(color.r, color.g, color.b))); + Ok(()) + } + + async fn bold(&mut self, res: Resource, value: bool) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.bold = Some(value); + Ok(()) + } + + async fn italic(&mut self, res: Resource, value: bool) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.italic = Some(value); + Ok(()) + } + + async fn underlined( + &mut self, + res: Resource, + value: bool, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.underlined = Some(value); + Ok(()) + } + + async fn strikethrough( + &mut self, + res: Resource, + value: bool, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.strikethrough = Some(value); + Ok(()) + } + + async fn obfuscated( + &mut self, + res: Resource, + value: bool, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.obfuscated = Some(value); + Ok(()) + } + + async fn insertion( + &mut self, + res: Resource, + text: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.insertion = Some(text); + Ok(()) + } + + async fn font(&mut self, res: Resource, font: String) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.font = Some(font); + Ok(()) + } + + async fn shadow_color( + &mut self, + res: Resource, + color: ArgbColor, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.shadow_color = + Some(color::ARGBColor::new(color.a, color.r, color.g, color.b)); + Ok(()) + } + + async fn click_open_url( + &mut self, + res: Resource, + url: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.click_event = Some(ClickEvent::OpenUrl { + url: Cow::Owned(url), + }); + Ok(()) + } + + async fn click_run_command( + &mut self, + res: Resource, + command: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.click_event = Some(ClickEvent::RunCommand { + command: Cow::Owned(command), + }); + Ok(()) + } + + async fn click_suggest_command( + &mut self, + res: Resource, + command: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.click_event = Some(ClickEvent::SuggestCommand { + command: Cow::Owned(command), + }); + Ok(()) + } + + async fn click_copy_to_clipboard( + &mut self, + res: Resource, + text: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.click_event = Some(ClickEvent::CopyToClipboard { + value: Cow::Owned(text), + }); + Ok(()) + } + + async fn hover_show_text( + &mut self, + res: Resource, + text: Resource, + ) -> wasmtime::Result<()> { + let hover_tc = self.take_text(&text)?.provider; + self.get_text_mut(&res)?.provider.0.style.hover_event = Some(HoverEvent::ShowText { + value: vec![hover_tc.0], + }); + Ok(()) + } + + async fn hover_show_item( + &mut self, + res: Resource, + item: String, + ) -> wasmtime::Result<()> { + self.get_text_mut(&res)?.provider.0.style.hover_event = Some(HoverEvent::ShowItem { + id: Cow::Owned(item), + count: None, + }); + Ok(()) + } + + async fn hover_show_entity( + &mut self, + res: Resource, + entity_type: String, + id: String, + name: Option>, + ) -> wasmtime::Result<()> { + let name_val = match name { + Some(r) => Some(vec![self.take_text(&r)?.provider.0]), + None => None, + }; + self.get_text_mut(&res)?.provider.0.style.hover_event = Some(HoverEvent::ShowEntity { + id: Cow::Owned(entity_type), + uuid: Cow::Owned(id), + name: name_val, + }); + Ok(()) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) } } @@ -68,225 +337,3 @@ const fn map_named_color(color: NamedColor) -> color::NamedColor { NamedColor::White => color::NamedColor::White, } } - -impl pumpkin::plugin::text::HostTextComponent for PluginHostState { - async fn text(&mut self, plain: String) -> Resource { - let tc = pumpkin_util::text::TextComponent::text(plain); - self.add_text_component(tc).unwrap() - } - - async fn translate( - &mut self, - key: String, - with: Vec>, - ) -> Resource { - let with: Vec = - with.into_iter().map(|r| r.consume(self).provider).collect(); - let tc = pumpkin_util::text::TextComponent::translate(key, with); - self.add_text_component(tc).unwrap() - } - - async fn add_child( - &mut self, - text_component: Resource, - child: Resource, - ) { - let child = child.consume(self).provider; - let parent = &mut text_component.downcast_mut(self).provider; - *parent = parent.clone().add_child(child); - } - - async fn add_text(&mut self, text_component: Resource, text: String) { - let parent = &mut text_component.downcast_mut(self).provider; - *parent = parent.clone().add_text(text); - } - - async fn get_text(&mut self, text_component: Resource) -> String { - text_component - .downcast_ref(self) - .provider - .clone() - .get_text() - } - - async fn encode(&mut self, text_component: Resource) -> Vec { - text_component - .downcast_ref(self) - .provider - .encode() - .into_vec() - } - - async fn color_named(&mut self, text_component: Resource, color: NamedColor) { - text_component.downcast_mut(self).provider.0.style.color = - Some(Color::Named(map_named_color(color))); - } - - async fn color_rgb(&mut self, text_component: Resource, color: RgbColor) { - text_component.downcast_mut(self).provider.0.style.color = - Some(Color::Rgb(color::RGBColor::new(color.r, color.g, color.b))); - } - - async fn bold(&mut self, text_component: Resource, value: bool) { - text_component.downcast_mut(self).provider.0.style.bold = Some(value); - } - - async fn italic(&mut self, text_component: Resource, value: bool) { - text_component.downcast_mut(self).provider.0.style.italic = Some(value); - } - - async fn underlined(&mut self, text_component: Resource, value: bool) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .underlined = Some(value); - } - - async fn strikethrough(&mut self, text_component: Resource, value: bool) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .strikethrough = Some(value); - } - - async fn obfuscated(&mut self, text_component: Resource, value: bool) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .obfuscated = Some(value); - } - - async fn insertion(&mut self, text_component: Resource, text: String) { - text_component.downcast_mut(self).provider.0.style.insertion = Some(text); - } - - async fn font(&mut self, text_component: Resource, font: String) { - text_component.downcast_mut(self).provider.0.style.font = Some(font); - } - - async fn shadow_color(&mut self, text_component: Resource, color: ArgbColor) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .shadow_color = Some(color::ARGBColor::new(color.a, color.r, color.g, color.b)); - } - - async fn click_open_url(&mut self, text_component: Resource, url: String) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .click_event = Some(ClickEvent::OpenUrl { - url: Cow::Owned(url), - }); - } - - async fn click_run_command( - &mut self, - text_component: Resource, - command: String, - ) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .click_event = Some(ClickEvent::RunCommand { - command: Cow::Owned(command), - }); - } - - async fn click_suggest_command( - &mut self, - text_component: Resource, - command: String, - ) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .click_event = Some(ClickEvent::SuggestCommand { - command: Cow::Owned(command), - }); - } - - async fn click_copy_to_clipboard( - &mut self, - text_component: Resource, - text: String, - ) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .click_event = Some(ClickEvent::CopyToClipboard { - value: Cow::Owned(text), - }); - } - - async fn hover_show_text( - &mut self, - text_component: Resource, - text: Resource, - ) { - let hover_tc = text.consume(self).provider; - text_component - .downcast_mut(self) - .provider - .0 - .style - .hover_event = Some(HoverEvent::ShowText { - value: vec![hover_tc.0], - }); - } - - async fn hover_show_item(&mut self, text_component: Resource, item: String) { - text_component - .downcast_mut(self) - .provider - .0 - .style - .hover_event = Some(HoverEvent::ShowItem { - id: Cow::Owned(item), - count: None, - }); - } - - async fn hover_show_entity( - &mut self, - text_component: Resource, - entity_type: String, - id: String, - name: Option>, - ) { - let name = name.map(|r| vec![r.consume(self).provider.0]); - text_component - .downcast_mut(self) - .provider - .0 - .style - .hover_event = Some(HoverEvent::ShowEntity { - id: Cow::Owned(entity_type), - uuid: Cow::Owned(id), - name, - }); - } - - async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - let _ = self - .resource_table - .delete::(Resource::new_own(rep.rep())); - Ok(()) - } -} diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/world.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/world.rs index 076d5aede..bf3ac8c61 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/world.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1_0/world.rs @@ -1,85 +1,69 @@ use pumpkin_data::block_state::PistonBehavior; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::world::BlockFlags; +use pumpkin_world::world::{BlockFlags, SimpleWorld}; use wasmtime::component::Resource; -use pumpkin_world::world::SimpleWorld; - use crate::plugin::loader::wasm::wasm_host::wit::v0_1_0::pumpkin::plugin::world::{ BlockFlags as WitBlockFlags, BlockPos as WitBlockPos, BlockState as WitBlockState, PistonBehavior as WitPistonBehavior, }; use crate::plugin::loader::wasm::wasm_host::{ - DowncastResourceExt, state::{PluginHostState, TextComponentResource, WorldResource}, wit::v0_1_0::pumpkin::{self, plugin::world::World}, }; -fn text_component_from_resource( - state: &PluginHostState, - text: &Resource, -) -> Result { - state - .resource_table - .get::(&Resource::new_own(text.rep())) - .map_err(|_| "invalid text-component resource handle".to_string()) - .map(|resource| resource.provider.clone()) -} - -impl DowncastResourceExt for Resource { - fn downcast_ref<'a>(&'a self, state: &'a mut PluginHostState) -> &'a WorldResource { - state - .resource_table - .get_any_mut(self.rep()) - .expect("invalid world resource handle") - .downcast_ref::() - .expect("resource type mismatch") +// --- Trapping Helpers --- +impl PluginHostState { + fn get_world_res(&self, res: &Resource) -> wasmtime::Result<&WorldResource> { + self.resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from) } - fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut WorldResource { - state + fn get_text_provider( + &self, + res: &Resource, + ) -> wasmtime::Result { + Ok(self .resource_table - .get_any_mut(self.rep()) - .expect("invalid world resource handle") - .downcast_mut::() - .expect("resource type mismatch") - } - - fn consume(self, state: &mut PluginHostState) -> WorldResource { - state - .resource_table - .delete::(Resource::new_own(self.rep())) - .expect("invalid world resource handle") + .get::(&Resource::new_own(res.rep())) + .map_err(wasmtime::Error::from)? + .provider + .clone()) } } impl pumpkin::plugin::world::Host for PluginHostState {} impl pumpkin::plugin::world::HostWorld for PluginHostState { - async fn get_id(&mut self, world: Resource) -> String { - world - .downcast_ref(self) + async fn get_id(&mut self, world: Resource) -> wasmtime::Result { + Ok(self + .get_world_res(&world)? .provider .get_world_name() - .to_string() + .to_string()) } - async fn get_block_state_id(&mut self, world: Resource, pos: WitBlockPos) -> u16 { - let world_ref = world.downcast_ref(self); + async fn get_block_state_id( + &mut self, + world: Resource, + pos: WitBlockPos, + ) -> wasmtime::Result { + let world_ref = self.get_world_res(&world)?; let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); - - world_ref.provider.get_block_state_id(&internal_pos).await + Ok(world_ref.provider.get_block_state_id(&internal_pos).await) } - async fn get_block_state(&mut self, world: Resource, pos: WitBlockPos) -> WitBlockState { - let world_ref = world.downcast_ref(self); + async fn get_block_state( + &mut self, + world: Resource, + pos: WitBlockPos, + ) -> wasmtime::Result { + let world_ref = self.get_world_res(&world)?; let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); - - // Fetch the actual BlockState struct from the world - // get_block_state typically returns &'static BlockState in Pumpkin let state = world_ref.provider.get_block_state(&internal_pos).await; - WitBlockState { + Ok(WitBlockState { id: state.id, luminance: state.luminance, opacity: state.opacity, @@ -96,7 +80,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { PistonBehavior::Ignore => WitPistonBehavior::Ignore, PistonBehavior::PushOnly => WitPistonBehavior::PushOnly, }, - } + }) } async fn set_block_state( @@ -105,11 +89,10 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { pos: WitBlockPos, state: u16, update_flags: WitBlockFlags, - ) { - let world_ref = world.downcast_ref(self); + ) -> wasmtime::Result<()> { + let world_ref = self.get_world_res(&world)?; let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); - // Map WIT flags to your internal bitflags let mut internal_flags = BlockFlags::empty(); if update_flags.contains(WitBlockFlags::NOTIFY_NEIGHBORS) { internal_flags |= BlockFlags::NOTIFY_NEIGHBORS; @@ -136,65 +119,91 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { internal_flags |= BlockFlags::SKIP_BLOCK_ADDED_CALLBACK; } - // Update the world world_ref .provider .clone() .set_block_state(&internal_pos, state, internal_flags) .await; + Ok(()) } - async fn get_time_of_day(&mut self, world: Resource) -> u64 { - let world_ref = world.downcast_ref(self); - world_ref.provider.get_time_of_day().await as u64 + async fn get_time_of_day(&mut self, world: Resource) -> wasmtime::Result { + Ok(self.get_world_res(&world)?.provider.get_time_of_day().await as u64) } - async fn set_time_of_day(&mut self, world: Resource, time: u64) { - let world_ref = world.downcast_ref(self); - world_ref.provider.set_time_of_day(time as i64).await; + async fn set_time_of_day(&mut self, world: Resource, time: u64) -> wasmtime::Result<()> { + self.get_world_res(&world)? + .provider + .set_time_of_day(time as i64) + .await; + Ok(()) } - async fn get_world_age(&mut self, world: Resource) -> u64 { - let world_ref = world.downcast_ref(self); - world_ref.provider.get_world_age().await as u64 + async fn get_world_age(&mut self, world: Resource) -> wasmtime::Result { + Ok(self.get_world_res(&world)?.provider.get_world_age().await as u64) } - async fn get_dimension(&mut self, world: Resource) -> String { - let world_ref = world.downcast_ref(self); - world_ref.provider.dimension.minecraft_name.to_string() + async fn get_dimension(&mut self, world: Resource) -> wasmtime::Result { + Ok(self + .get_world_res(&world)? + .provider + .dimension + .minecraft_name + .to_string()) } - async fn get_top_block_y(&mut self, world: Resource, x: i32, z: i32) -> i32 { - let world_ref = world.downcast_ref(self); - world_ref + async fn get_top_block_y( + &mut self, + world: Resource, + x: i32, + z: i32, + ) -> wasmtime::Result { + Ok(self + .get_world_res(&world)? .provider .get_top_block(pumpkin_util::math::vector2::Vector2::new(x, z)) - .await + .await) } - async fn get_motion_blocking_height(&mut self, world: Resource, x: i32, z: i32) -> i32 { - let world_ref = world.downcast_ref(self); - world_ref.provider.get_motion_blocking_height(x, z).await + async fn get_motion_blocking_height( + &mut self, + world: Resource, + x: i32, + z: i32, + ) -> wasmtime::Result { + Ok(self + .get_world_res(&world)? + .provider + .get_motion_blocking_height(x, z) + .await) } - async fn is_raining(&mut self, world: Resource) -> bool { - let world_ref = world.downcast_ref(self); - world_ref.provider.is_raining().await + async fn is_raining(&mut self, world: Resource) -> wasmtime::Result { + Ok(self.get_world_res(&world)?.provider.is_raining().await) } - async fn set_raining(&mut self, world: Resource, raining: bool) { - let world_ref = world.downcast_ref(self); - world_ref.provider.set_raining(raining).await; + async fn set_raining(&mut self, world: Resource, raining: bool) -> wasmtime::Result<()> { + self.get_world_res(&world)? + .provider + .set_raining(raining) + .await; + Ok(()) } - async fn is_thundering(&mut self, world: Resource) -> bool { - let world_ref = world.downcast_ref(self); - world_ref.provider.is_thundering().await + async fn is_thundering(&mut self, world: Resource) -> wasmtime::Result { + Ok(self.get_world_res(&world)?.provider.is_thundering().await) } - async fn set_thundering(&mut self, world: Resource, thundering: bool) { - let world_ref = world.downcast_ref(self); - world_ref.provider.set_thundering(thundering).await; + async fn set_thundering( + &mut self, + world: Resource, + thundering: bool, + ) -> wasmtime::Result<()> { + self.get_world_res(&world)? + .provider + .set_thundering(thundering) + .await; + Ok(()) } async fn broadcast_system_message( @@ -202,19 +211,19 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { world: Resource, message: Resource, overlay: bool, - ) { - let message = text_component_from_resource(self, &message).unwrap(); - let world_ref = world.downcast_ref(self); - world_ref + ) -> wasmtime::Result<()> { + let msg = self.get_text_provider(&message)?; + self.get_world_res(&world)? .provider - .broadcast_system_message(&message, overlay) + .broadcast_system_message(&msg, overlay) .await; + Ok(()) } async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { - let _ = self - .resource_table - .delete::(Resource::new_own(rep.rep())); + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; Ok(()) } }