feat(plugin-api): use trappable

This commit is contained in:
Alexander Medvedev 2026-04-07 11:28:36 +02:00
parent 8b418813f8
commit 45ed583312
12 changed files with 1094 additions and 1054 deletions

View File

@ -98,7 +98,7 @@ interface command {
} }
variant command-sender-type { variant command-sender-type {
rcon(list<string>), rcon,
console, console,
player(player), player(player),
command-block(tuple<command-block-entity, %world>), command-block(tuple<command-block-entity, %world>),

View File

@ -15,79 +15,79 @@ interface player {
} }
resource player { resource player {
get-id: func () -> result<string, string>; get-id: func () -> string;
get-name: func () -> result<string, string>; get-name: func () -> string;
// Basic state // Basic state
get-position: func () -> result<position, string>; get-position: func () -> position;
get-rotation: func () -> result<tuple<f32, f32>, string>; get-rotation: func () -> tuple<f32, f32>;
get-world: func () -> result<%world, string>; get-world: func () -> %world;
get-gamemode: func () -> result<game-mode, string>; get-gamemode: func () -> game-mode;
set-gamemode: func (mode: game-mode) -> result<bool, string>; set-gamemode: func (mode: game-mode) -> bool;
get-locale: func () -> result<string, string>; get-locale: func () -> string;
get-ping: func () -> result<u32, string>; get-ping: func () -> u32;
// Permissions & metadata // Permissions & metadata
get-permission-level: func () -> result<permission-level, string>; get-permission-level: func () -> permission-level;
set-permission-level: func (level: permission-level) -> result<_, string>; set-permission-level: func (level: permission-level);
has-permission: func (node: string) -> result<bool, string>; has-permission: func (node: string) -> bool;
get-display-name: func () -> result<text-component, string>; get-display-name: func () -> text-component;
// Messaging & titles // Messaging & titles
send-system-message: func (text: text-component, overlay: bool) -> result<_, string>; send-system-message: func (text: text-component, overlay: bool);
show-title: func (text: text-component) -> result<_, string>; show-title: func (text: text-component);
show-subtitle: func (text: text-component) -> result<_, string>; show-subtitle: func (text: text-component);
show-actionbar: func (text: text-component) -> result<_, string>; show-actionbar: func (text: text-component);
send-title-animation: func (fade-in: s32, stay: s32, fade-out: s32) -> result<_, string>; send-title-animation: func (fade-in: s32, stay: s32, fade-out: s32);
// Teleport & kick // Teleport & kick
teleport: func (position: position, yaw: f32, pitch: f32) -> result<_, string>; teleport: func (position: position, yaw: f32, pitch: f32);
teleport-world: func (world-ref: %world, position: position, yaw: option<f32>, pitch: option<f32>) -> result<_, string>; teleport-world: func (world-ref: %world, position: position, yaw: option<f32>, pitch: option<f32>);
kick: func (message: text-component) -> result<_, string>; kick: func (message: text-component);
respawn: func () -> result<_, string>; respawn: func ();
ban: func (reason: option<text-component>) -> result<_, string>; ban: func (reason: option<text-component>);
ban-ip: func (reason: option<text-component>) -> result<_, string>; ban-ip: func (reason: option<text-component>);
// Inventory // Inventory
get-selected-slot: func () -> result<u8, string>; get-selected-slot: func () -> u8;
get-item-in-hand: func (hand: hand) -> result<option<item-stack>, string>; get-item-in-hand: func (hand: hand) -> option<item-stack>;
get-inventory-item: func (slot: u8) -> result<option<item-stack>, string>; get-inventory-item: func (slot: u8) -> option<item-stack>;
// Status // Status
get-health: func () -> result<f32, string>; get-health: func () -> f32;
set-health: func (health: f32) -> result<_, string>; set-health: func (health: f32);
get-max-health: func () -> result<f32, string>; get-max-health: func () -> f32;
set-max-health: func (max-health: f32) -> result<_, string>; set-max-health: func (max-health: f32);
get-food-level: func () -> result<u8, string>; get-food-level: func () -> u8;
set-food-level: func (food-level: u8) -> result<_, string>; set-food-level: func (food-level: u8);
get-saturation: func () -> result<f32, string>; get-saturation: func () -> f32;
set-saturation: func (saturation: f32) -> result<_, string>; set-saturation: func (saturation: f32);
get-exhaustion: func () -> result<f32, string>; get-exhaustion: func () -> f32;
set-exhaustion: func (exhaustion: f32) -> result<_, string>; set-exhaustion: func (exhaustion: f32);
get-absorption: func () -> result<f32, string>; get-absorption: func () -> f32;
set-absorption: func (absorption: f32) -> result<_, string>; set-absorption: func (absorption: f32);
get-experience-level: func() -> result<s32, string>; get-experience-level: func() -> s32;
get-experience-progress: func() -> result<f32, string>; get-experience-progress: func() -> f32;
get-experience-points: func() -> result<s32, string>; get-experience-points: func() -> s32;
set-experience-level: func(level: s32) -> result<_, string>; set-experience-level: func(level: s32);
set-experience-progress: func(progress: f32) -> result<_, string>; set-experience-progress: func(progress: f32);
set-experience-points: func(points: s32) -> result<_, string>; set-experience-points: func(points: s32);
add-experience-levels: func(levels: s32) -> result<_, string>; add-experience-levels: func(levels: s32);
add-experience-points: func(points: s32) -> result<_, string>; add-experience-points: func(points: s32);
is-sneaking: func() -> result<bool, string>; is-sneaking: func() -> bool;
set-sneaking: func(sneaking: bool) -> result<_, string>; set-sneaking: func(sneaking: bool);
is-sprinting: func() -> result<bool, string>; is-sprinting: func() -> bool;
set-sprinting: func(sprinting: bool) -> result<_, string>; set-sprinting: func(sprinting: bool);
is-on-ground: func() -> result<bool, string>; is-on-ground: func() -> bool;
is-flying: func() -> result<bool, string>; is-flying: func() -> bool;
set-flying: func(flying: bool) -> result<_, string>; set-flying: func(flying: bool);
get-abilities: func() -> result<player-abilities, string>; get-abilities: func() -> player-abilities;
set-abilities: func(abilities: player-abilities) -> result<_, string>; set-abilities: func(abilities: player-abilities);
get-ip: func() -> result<string, string>; get-ip: func() -> string;
} }
} }

View File

@ -125,6 +125,21 @@ async fn register_player_event(
} }
} }
impl PluginHostState {
fn get_context(&self, res: &Resource<Context>) -> wasmtime::Result<&ContextResource> {
self.resource_table
.get::<ContextResource>(&Resource::new_own(res.rep()))
.map_err(wasmtime::Error::from)
}
fn take_command(&mut self, res: &Resource<Command>) -> wasmtime::Result<CommandResource> {
self.resource_table
.delete::<CommandResource>(Resource::new_own(res.rep()))
// Convert ResourceTableError -> wasmtime::Error
.map_err(wasmtime::Error::from)
}
}
async fn register_world_event( async fn register_world_event(
resource: &ContextResource, resource: &ContextResource,
handler: &Arc<WasmPluginV0_1_0EventHandler>, handler: &Arc<WasmPluginV0_1_0EventHandler>,
@ -237,7 +252,8 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
event_type: EventType, event_type: EventType,
event_priority: EventPriority, event_priority: EventPriority,
blocking: bool, blocking: bool,
) { ) -> wasmtime::Result<()> {
// Updated return type
let priority = match event_priority { let priority = match event_priority {
EventPriority::Highest => crate::plugin::EventPriority::Highest, EventPriority::Highest => crate::plugin::EventPriority::Highest,
EventPriority::High => crate::plugin::EventPriority::High, EventPriority::High => crate::plugin::EventPriority::High,
@ -246,14 +262,15 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
EventPriority::Lowest => crate::plugin::EventPriority::Lowest, EventPriority::Lowest => crate::plugin::EventPriority::Lowest,
}; };
// Use ? to trap if the plugin was dropped or the context handle is dead
let plugin = self let plugin = self
.plugin .plugin
.as_ref() .as_ref()
.expect("plugin should always be initialized here") .ok_or_else(|| wasmtime::Error::msg("Plugin state uninitialized"))?
.upgrade() .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 }); let handler = Arc::new(WasmPluginV0_1_0EventHandler { handler_id, plugin });
match event_type { match event_type {
@ -275,6 +292,8 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
register_player_event(resource, &handler, priority, blocking, event_type).await; register_player_event(resource, &handler, priority, blocking, event_type).await;
} }
} }
Ok(())
} }
async fn register_command( async fn register_command(
@ -282,44 +301,37 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
context: Resource<Context>, context: Resource<Context>,
command: Resource<Command>, command: Resource<Command>,
permission: String, permission: String,
) { ) -> wasmtime::Result<()> {
let command = self // Updated return type
.resource_table // Use your helpers to safely take/get resources
.delete::<CommandResource>(Resource::new_own(command.rep())) let command_res = self.take_command(&command)?;
.expect("invalid command resource handle") let context_res = self.get_context(&context)?;
.provider;
let context_resource = self context_res
.resource_table
.get_any_mut(context.rep())
.expect("invalid context resource handle")
.downcast_ref::<ContextResource>()
.expect("resource type mismatch");
context_resource
.provider .provider
.register_command(command, permission) .register_command(command_res.provider, permission)
.await; .await;
Ok(())
} }
async fn register_permission( async fn register_permission(
&mut self, &mut self,
context: Resource<Context>, context: Resource<Context>,
permission: Permission, permission: Permission,
) -> Result<(), String> { ) -> wasmtime::Result<Result<(), String>> {
let mut children: HashMap<String, bool> = HashMap::with_capacity(permission.children.len()); let mut children = HashMap::with_capacity(permission.children.len());
for child in permission.children { for child in permission.children {
children.insert(child.node, child.value); children.insert(child.node, child.value);
} }
let permission = pumpkin_util::permission::Permission { let util_permission = pumpkin_util::permission::Permission {
node: permission.node, node: permission.node,
description: permission.description, description: permission.description,
default: match permission.default { default: match permission.default {
PermissionDefault::Deny => pumpkin_util::permission::PermissionDefault::Deny, PermissionDefault::Deny => pumpkin_util::permission::PermissionDefault::Deny,
PermissionDefault::Allow => pumpkin_util::permission::PermissionDefault::Allow, PermissionDefault::Allow => pumpkin_util::permission::PermissionDefault::Allow,
PermissionDefault::Op(permission_level) => { PermissionDefault::Op(lvl) => {
pumpkin_util::permission::PermissionDefault::Op(match permission_level { pumpkin_util::permission::PermissionDefault::Op(match lvl {
PermissionLevel::Zero => pumpkin_util::permission::PermissionLvl::Zero, PermissionLevel::Zero => pumpkin_util::permission::PermissionLvl::Zero,
PermissionLevel::One => pumpkin_util::permission::PermissionLvl::One, PermissionLevel::One => pumpkin_util::permission::PermissionLvl::One,
PermissionLevel::Two => pumpkin_util::permission::PermissionLvl::Two, PermissionLevel::Two => pumpkin_util::permission::PermissionLvl::Two,
@ -331,26 +343,31 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
children, children,
}; };
context let context_res = self
.downcast_mut(self) .resource_table
.get_mut::<ContextResource>(&Resource::new_own(context.rep()))?;
Ok(context_res
.provider .provider
.register_permission(permission) .register_permission(util_permission)
.await .await)
} }
async fn get_data_folder(&mut self, context: Resource<Context>) -> String { async fn get_data_folder(&mut self, context: Resource<Context>) -> wasmtime::Result<String> {
context Ok(self
.downcast_ref(self) .get_context(&context)?
.provider .provider
.get_data_folder() .get_data_folder()
.to_string_lossy() .to_string_lossy()
.into_owned() .into_owned())
} }
async fn get_server(&mut self, context: Resource<Context>) -> Resource<Server> { async fn get_server(
let server_provider = context.downcast_ref(self).provider.server.clone(); &mut self,
context: Resource<Context>,
) -> wasmtime::Result<Resource<Server>> {
let server_provider = self.get_context(&context)?.provider.server.clone();
self.add_server(server_provider) 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<Context>) -> wasmtime::Result<()> { async fn drop(&mut self, rep: Resource<Context>) -> wasmtime::Result<()> {

View File

@ -14,68 +14,77 @@ use crate::plugin::loader::wasm::wasm_host::{
impl pumpkin::plugin::entity::Host for PluginHostState {} impl pumpkin::plugin::entity::Host for PluginHostState {}
impl pumpkin::plugin::entity::HostBlockEntity for PluginHostState { impl pumpkin::plugin::entity::HostBlockEntity for PluginHostState {
async fn resource_location(&mut self, _block_entity: Resource<BlockEntity>) -> String { async fn resource_location(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<String> {
todo!() todo!("Implement resource_location lookup")
} }
async fn get_position(&mut self, _block_entity: Resource<BlockEntity>) -> BlockPosition { async fn get_position(
todo!() &mut self,
_res: Resource<BlockEntity>,
) -> wasmtime::Result<BlockPosition> {
todo!("Implement get_position lookup")
} }
async fn get_id(&mut self, _block_entity: Resource<BlockEntity>) -> u32 { async fn get_id(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<u32> {
todo!() todo!("Implement get_id lookup")
} }
async fn is_dirty(&mut self, _block_entity: Resource<BlockEntity>) -> bool { async fn is_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<bool> {
todo!() todo!("Implement is_dirty lookup")
} }
async fn clear_dirty(&mut self, _block_entity: Resource<BlockEntity>) { async fn clear_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<()> {
todo!() todo!("Implement clear_dirty logic")
} }
async fn drop(&mut self, _rep: Resource<BlockEntity>) -> wasmtime::Result<()> { async fn drop(&mut self, _rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
todo!() todo!();
} }
} }
impl pumpkin::plugin::entity::HostCommandBlockEntity for PluginHostState { impl pumpkin::plugin::entity::HostCommandBlockEntity for PluginHostState {
async fn get_block_entity( async fn get_block_entity(
&mut self, &mut self,
_command_block_entity: Resource<CommandBlockEntity>, _res: Resource<CommandBlockEntity>,
) -> Resource<BlockEntity> { ) -> wasmtime::Result<Resource<BlockEntity>> {
todo!() todo!("Implement upcasting/mapping to BlockEntity")
} }
async fn last_output(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> String { async fn last_output(
todo!() &mut self,
_res: Resource<CommandBlockEntity>,
) -> wasmtime::Result<String> {
todo!("Implement last_output lookup")
} }
async fn track_output(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> bool { async fn track_output(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
todo!() todo!("Implement track_output lookup")
} }
async fn success_count(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> u32 { async fn success_count(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
todo!() todo!("Implement success_count lookup")
} }
async fn command(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> String { async fn command(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
todo!() todo!("Implement command lookup")
} }
async fn auto(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> bool { async fn auto(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
todo!() todo!("Implement auto lookup")
} }
async fn condition_met(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> bool { async fn condition_met(
todo!() &mut self,
_res: Resource<CommandBlockEntity>,
) -> wasmtime::Result<bool> {
todo!("Implement condition_met lookup")
} }
async fn powered(&mut self, _command_block_entity: Resource<CommandBlockEntity>) -> bool { async fn powered(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
todo!() todo!("Implement powered lookup")
} }
async fn drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> { async fn drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
todo!() todo!();
} }
} }

View File

@ -6,27 +6,31 @@ use pumpkin_util::translation::{Locale as UtilLocale, add_translation_file, get_
use std::str::FromStr; use std::str::FromStr;
impl Host for PluginHostState { 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<String> {
let util_locale = wit_to_util_locale(locale); 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); let util_locale = wit_to_util_locale(locale);
add_translation_file(namespace, json, util_locale); add_translation_file(namespace, json, util_locale);
Ok(())
} }
} }
/// Converts a WIT Locale to a pumpkin-util Locale. /// Converts a WIT Locale to a pumpkin-util Locale.
fn wit_to_util_locale(wit: WitLocale) -> UtilLocale { 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) // WIT variants like EnUs often debug to "EnUs".
// We can use the debug representation or a custom mapping. // We convert to lowercase and handle potential format differences.
// Given the amount of variants, we use a string-based approach if possible.
let s = format!("{wit:?}").to_lowercase(); 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. // Most translation systems expect underscores (en_us) rather than nothing or dashes.
// Let's assume standard bindgen which might produce "AfZa". // If the WIT Debug format is "EnUs", lowercase is "enus".
// We'll replace kebab-case if necessary. // We might need a smarter mapping if your util expects specifically "en_us".
let s = s.replace('-', "_");
UtilLocale::from_str(&s).unwrap_or(UtilLocale::EnUs) UtilLocale::from_str(&s).unwrap_or(UtilLocale::EnUs)
} }

View File

@ -3,7 +3,11 @@ use crate::plugin::loader::wasm::wasm_host::{
}; };
impl pumpkin::plugin::logging::Host for PluginHostState { 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 { match level {
pumpkin::plugin::logging::Level::Trace => tracing::trace!("[plugin] {message}"), pumpkin::plugin::logging::Level::Trace => tracing::trace!("[plugin] {message}"),
pumpkin::plugin::logging::Level::Debug => tracing::debug!("[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::Warn => tracing::warn!("[plugin] {message}"),
pumpkin::plugin::logging::Level::Error => tracing::error!("[plugin] {message}"), pumpkin::plugin::logging::Level::Error => tracing::error!("[plugin] {message}"),
} }
Ok(())
} }
async fn log_tracing(&mut self, event: Vec<u8>) { async fn log_tracing(&mut self, event: Vec<u8>) -> wasmtime::Result<()> {
log_tracing(event).await; log_tracing(event).await;
Ok(())
} }
} }

View File

@ -22,8 +22,8 @@ pub mod world;
bindgen!({ bindgen!({
path: "../pumpkin-plugin-wit/v0.1.0", path: "../pumpkin-plugin-wit/v0.1.0",
world: "plugin", world: "plugin",
imports: { default: async }, imports: { default: async | trappable },
exports: { default: async }, exports: { default: async | trappable},
}); });
struct PluginHostComponent; struct PluginHostComponent;

View File

@ -21,34 +21,36 @@ use pumpkin_util::permission::PermissionLvl;
fn player_from_resource( fn player_from_resource(
state: &PluginHostState, state: &PluginHostState,
player: &Resource<Player>, player: &Resource<Player>,
) -> Result<std::sync::Arc<crate::entity::player::Player>, String> { ) -> wasmtime::Result<std::sync::Arc<crate::entity::player::Player>> {
state state
.resource_table .resource_table
.get::<PlayerResource>(&Resource::new_own(player.rep())) .get::<PlayerResource>(&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()) .map(|resource| resource.provider.clone())
} }
fn text_component_from_resource( fn text_component_from_resource(
state: &PluginHostState, state: &PluginHostState,
text: &Resource<pumpkin::plugin::text::TextComponent>, text: &Resource<pumpkin::plugin::text::TextComponent>,
) -> Result<pumpkin_util::text::TextComponent, String> { ) -> pumpkin_util::text::TextComponent {
state state
.resource_table .resource_table
.get::<TextComponentResource>(&Resource::new_own(text.rep())) .get::<TextComponentResource>(&Resource::new_own(text.rep()))
.map_err(|_| "invalid text-component resource handle".to_string()) .expect("invalid text-component resource handle")
.map(|resource| resource.provider.clone()) .provider
.clone()
} }
fn world_from_resource( fn world_from_resource(
state: &PluginHostState, state: &PluginHostState,
world: &Resource<pumpkin::plugin::world::World>, world: &Resource<pumpkin::plugin::world::World>,
) -> Result<std::sync::Arc<crate::world::World>, String> { ) -> std::sync::Arc<crate::world::World> {
state state
.resource_table .resource_table
.get::<WorldResource>(&Resource::new_own(world.rep())) .get::<WorldResource>(&Resource::new_own(world.rep()))
.map_err(|_| "invalid world resource handle".to_string()) .expect("invalid world resource handle")
.map(|resource| resource.provider.clone()) .provider
.clone()
} }
fn to_wit_item_stack( fn to_wit_item_stack(
@ -124,12 +126,12 @@ impl DowncastResourceExt<PlayerResource> for Resource<Player> {
impl pumpkin::plugin::player::Host for PluginHostState {} impl pumpkin::plugin::player::Host for PluginHostState {}
impl pumpkin::plugin::player::HostPlayer for PluginHostState { impl pumpkin::plugin::player::HostPlayer for PluginHostState {
async fn get_id(&mut self, player: Resource<Player>) -> Result<String, String> { async fn get_id(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.gameprofile.id.to_string()) Ok(player.gameprofile.id.to_string())
} }
async fn get_name(&mut self, player: Resource<Player>) -> Result<String, String> { async fn get_name(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.gameprofile.name.clone()) Ok(player.gameprofile.name.clone())
} }
@ -137,13 +139,12 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
async fn get_position( async fn get_position(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<pumpkin::plugin::common::Position, String> { ) -> wasmtime::Result<pumpkin::plugin::common::Position> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let position = player.position(); Ok(to_wasm_position(player.position()))
Ok(to_wasm_position(position))
} }
async fn get_rotation(&mut self, player: Resource<Player>) -> Result<(f32, f32), String> { async fn get_rotation(&mut self, player: Resource<Player>) -> wasmtime::Result<(f32, f32)> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.rotation()) Ok(player.rotation())
} }
@ -151,39 +152,36 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
async fn get_world( async fn get_world(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<wasmtime::component::Resource<pumpkin::plugin::world::World>, String> { ) -> wasmtime::Result<wasmtime::component::Resource<pumpkin::plugin::world::World>> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let world = player.world(); let world = player.world();
self.add_world(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( async fn get_gamemode(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<pumpkin::plugin::common::GameMode, String> { ) -> wasmtime::Result<pumpkin::plugin::common::GameMode> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let gamemode = player.gamemode.load(); Ok(to_wasm_game_mode(player.gamemode.load()))
Ok(to_wasm_game_mode(gamemode))
} }
async fn set_gamemode( async fn set_gamemode(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
mode: pumpkin::plugin::common::GameMode, mode: pumpkin::plugin::common::GameMode,
) -> Result<bool, String> { ) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let mode = from_wasm_game_mode(mode); Ok(player.set_gamemode(from_wasm_game_mode(mode)).await)
Ok(player.set_gamemode(mode).await)
} }
async fn get_locale(&mut self, player: Resource<Player>) -> Result<String, String> { async fn get_locale(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let config = player.config.load(); Ok(player.config.load().locale.clone())
Ok(config.locale.clone())
} }
async fn get_ping(&mut self, player: Resource<Player>) -> Result<u32, String> { async fn get_ping(&mut self, player: Resource<Player>) -> wasmtime::Result<u32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.ping.load(Ordering::Relaxed)) Ok(player.ping.load(Ordering::Relaxed))
} }
@ -191,7 +189,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
async fn get_permission_level( async fn get_permission_level(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<pumpkin::plugin::permission::PermissionLevel, String> { ) -> wasmtime::Result<pumpkin::plugin::permission::PermissionLevel> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(to_wit_permission_level(player.permission_lvl.load())) Ok(to_wit_permission_level(player.permission_lvl.load()))
} }
@ -200,11 +198,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
level: pumpkin::plugin::permission::PermissionLevel, level: pumpkin::plugin::permission::PermissionLevel,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let Some(server) = self.server.as_ref() else { let server = self.server.as_ref().expect("server not available");
return Err("server not available".to_string());
};
let level = from_wit_permission_level(level); let level = from_wit_permission_level(level);
let command_dispatcher = server.command_dispatcher.read().await; let command_dispatcher = server.command_dispatcher.read().await;
player player
@ -217,22 +213,20 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
node: String, node: String,
) -> Result<bool, String> { ) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let Some(server) = self.server.as_ref() else { let server = self.server.as_ref().expect("server not available");
return Err("server not available".to_string());
};
Ok(player.has_permission(server, &node).await) Ok(player.has_permission(server, &node).await)
} }
async fn get_display_name( async fn get_display_name(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<Resource<pumpkin::plugin::text::TextComponent>, String> { ) -> wasmtime::Result<Resource<pumpkin::plugin::text::TextComponent>> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let display_name = player.get_display_name().await; let display_name = player.get_display_name().await;
self.add_text_component(display_name) 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( async fn send_system_message(
@ -240,8 +234,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
player: Resource<Player>, player: Resource<Player>,
text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>, text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>,
overlay: bool, overlay: bool,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let component = text_component_from_resource(self, &text)?; let component = text_component_from_resource(self, &text);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.send_system_message_raw(&component, overlay).await; player.send_system_message_raw(&component, overlay).await;
Ok(()) Ok(())
@ -251,8 +245,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>, text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let component = text_component_from_resource(self, &text)?; let component = text_component_from_resource(self, &text);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.show_title(&component, &TitleMode::Title).await; player.show_title(&component, &TitleMode::Title).await;
Ok(()) Ok(())
@ -262,8 +256,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>, text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let component = text_component_from_resource(self, &text)?; let component = text_component_from_resource(self, &text);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.show_title(&component, &TitleMode::SubTitle).await; player.show_title(&component, &TitleMode::SubTitle).await;
Ok(()) Ok(())
@ -273,8 +267,8 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>, text: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let component = text_component_from_resource(self, &text)?; let component = text_component_from_resource(self, &text);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.show_title(&component, &TitleMode::ActionBar).await; player.show_title(&component, &TitleMode::ActionBar).await;
Ok(()) Ok(())
@ -286,7 +280,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
fade_in: i32, fade_in: i32,
stay: i32, stay: i32,
fade_out: i32, fade_out: i32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.send_title_animation(fade_in, stay, fade_out).await; player.send_title_animation(fade_in, stay, fade_out).await;
Ok(()) Ok(())
@ -298,10 +292,11 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
position: pumpkin::plugin::common::Position, position: pumpkin::plugin::common::Position,
yaw: f32, yaw: f32,
pitch: f32, pitch: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let position = from_wasm_position(position); player
player.request_teleport(position, yaw, pitch).await; .request_teleport(from_wasm_position(position), yaw, pitch)
.await;
Ok(()) Ok(())
} }
@ -312,11 +307,12 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
position: pumpkin::plugin::common::Position, position: pumpkin::plugin::common::Position,
yaw: Option<f32>, yaw: Option<f32>,
pitch: Option<f32>, pitch: Option<f32>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let world = world_from_resource(self, &world)?; let world = world_from_resource(self, &world);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let position = from_wasm_position(position); player
player.teleport_world(world, position, yaw, pitch).await; .teleport_world(world, from_wasm_position(position), yaw, pitch)
.await;
Ok(()) Ok(())
} }
@ -324,14 +320,14 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
message: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>, message: wasmtime::component::Resource<pumpkin::plugin::text::TextComponent>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let component = text_component_from_resource(self, &message)?; let component = text_component_from_resource(self, &message);
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.kick(DisconnectReason::Kicked, component).await; player.kick(DisconnectReason::Kicked, component).await;
Ok(()) Ok(())
} }
async fn respawn(&mut self, player: Resource<Player>) -> Result<(), String> { async fn respawn(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.respawn().await; player.respawn().await;
Ok(()) Ok(())
@ -341,15 +337,10 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
reason: Option<Resource<pumpkin::plugin::text::TextComponent>>, reason: Option<Resource<pumpkin::plugin::text::TextComponent>>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let Some(server) = self.server.as_ref() else { let server = self.server.as_ref().expect("server not available");
return Err("server not available".to_string()); let reason = reason.map(|r| text_component_from_resource(self, &r));
};
let reason = match reason {
Some(r) => Some(text_component_from_resource(self, &r)?),
None => None,
};
player.ban(server, reason).await; player.ban(server, reason).await;
Ok(()) Ok(())
} }
@ -358,20 +349,15 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
reason: Option<Resource<pumpkin::plugin::text::TextComponent>>, reason: Option<Resource<pumpkin::plugin::text::TextComponent>>,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let Some(server) = self.server.as_ref() else { let server = self.server.as_ref().expect("server not available");
return Err("server not available".to_string()); let reason = reason.map(|r| text_component_from_resource(self, &r));
};
let reason = match reason {
Some(r) => Some(text_component_from_resource(self, &r)?),
None => None,
};
player.ban_ip(server, reason).await; player.ban_ip(server, reason).await;
Ok(()) Ok(())
} }
async fn get_selected_slot(&mut self, player: Resource<Player>) -> Result<u8, String> { async fn get_selected_slot(&mut self, player: Resource<Player>) -> wasmtime::Result<u8> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.inventory.get_selected_slot()) Ok(player.inventory.get_selected_slot())
} }
@ -380,7 +366,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
hand: pumpkin::plugin::common::Hand, hand: pumpkin::plugin::common::Hand,
) -> Result<Option<pumpkin::plugin::common::ItemStack>, String> { ) -> wasmtime::Result<Option<pumpkin::plugin::common::ItemStack>> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let inventory = player.inventory(); let inventory = player.inventory();
let item_stack = match hand { let item_stack = match hand {
@ -395,29 +381,28 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
slot: u8, slot: u8,
) -> Result<Option<pumpkin::plugin::common::ItemStack>, String> { ) -> wasmtime::Result<Option<pumpkin::plugin::common::ItemStack>> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let slot = slot as usize; let slot = slot as usize;
if slot >= PlayerInventory::MAIN_SIZE { 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(); let item_stack = player.inventory.main_inventory[slot].lock().await.clone();
Ok(to_wit_item_stack(&item_stack)) Ok(to_wit_item_stack(&item_stack))
} }
async fn get_health(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_health(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.living_entity.health.load()) Ok(player.living_entity.health.load())
} }
async fn set_health(&mut self, player: Resource<Player>, health: f32) -> Result<(), String> { async fn set_health(&mut self, player: Resource<Player>, health: f32) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_health(health).await; player.set_health(health).await;
Ok(()) Ok(())
} }
async fn get_max_health(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_max_health(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.living_entity.get_max_health()) Ok(player.living_entity.get_max_health())
} }
@ -426,24 +411,28 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
max_health: f32, max_health: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_max_health(max_health).await; player.set_max_health(max_health).await;
Ok(()) Ok(())
} }
async fn get_food_level(&mut self, player: Resource<Player>) -> Result<u8, String> { async fn get_food_level(&mut self, player: Resource<Player>) -> wasmtime::Result<u8> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.hunger_manager.level.load()) Ok(player.hunger_manager.level.load())
} }
async fn set_food_level(&mut self, player: Resource<Player>, level: u8) -> Result<(), String> { async fn set_food_level(
&mut self,
player: Resource<Player>,
level: u8,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_food_level(level).await; player.set_food_level(level).await;
Ok(()) Ok(())
} }
async fn get_saturation(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_saturation(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.hunger_manager.saturation.load()) Ok(player.hunger_manager.saturation.load())
} }
@ -452,13 +441,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
saturation: f32, saturation: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_saturation(saturation).await; player.set_saturation(saturation).await;
Ok(()) Ok(())
} }
async fn get_exhaustion(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_exhaustion(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_exhaustion()) Ok(player.get_exhaustion())
} }
@ -467,13 +456,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
exhaustion: f32, exhaustion: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_exhaustion(exhaustion).await; player.set_exhaustion(exhaustion).await;
Ok(()) Ok(())
} }
async fn get_absorption(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_absorption(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_absorption()) Ok(player.get_absorption())
} }
@ -482,23 +471,23 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
absorption: f32, absorption: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_absorption(absorption).await; player.set_absorption(absorption).await;
Ok(()) Ok(())
} }
async fn get_experience_level(&mut self, player: Resource<Player>) -> Result<i32, String> { async fn get_experience_level(&mut self, player: Resource<Player>) -> wasmtime::Result<i32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.experience_level.load(Ordering::Relaxed)) Ok(player.experience_level.load(Ordering::Relaxed))
} }
async fn get_experience_progress(&mut self, player: Resource<Player>) -> Result<f32, String> { async fn get_experience_progress(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.experience_progress.load()) Ok(player.experience_progress.load())
} }
async fn get_experience_points(&mut self, player: Resource<Player>) -> Result<i32, String> { async fn get_experience_points(&mut self, player: Resource<Player>) -> wasmtime::Result<i32> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.experience_points.load(Ordering::Relaxed)) Ok(player.experience_points.load(Ordering::Relaxed))
} }
@ -507,7 +496,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
level: i32, level: i32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.set_experience_level(level, true).await; player.set_experience_level(level, true).await;
Ok(()) Ok(())
@ -517,7 +506,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
progress: f32, progress: f32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player player
.set_experience( .set_experience(
@ -533,7 +522,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
points: i32, points: i32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player player
.set_experience( .set_experience(
@ -549,7 +538,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
levels: i32, levels: i32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.add_experience_levels(levels).await; player.add_experience_levels(levels).await;
Ok(()) Ok(())
@ -559,13 +548,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
points: i32, points: i32,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.add_experience_points(points).await; player.add_experience_points(points).await;
Ok(()) Ok(())
} }
async fn is_sneaking(&mut self, player: Resource<Player>) -> Result<bool, String> { async fn is_sneaking(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_entity().sneaking.load(Ordering::Relaxed)) Ok(player.get_entity().sneaking.load(Ordering::Relaxed))
} }
@ -574,13 +563,13 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
sneaking: bool, sneaking: bool,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.get_entity().set_sneaking(sneaking).await; player.get_entity().set_sneaking(sneaking).await;
Ok(()) Ok(())
} }
async fn is_sprinting(&mut self, player: Resource<Player>) -> Result<bool, String> { async fn is_sprinting(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_entity().sprinting.load(Ordering::Relaxed)) Ok(player.get_entity().sprinting.load(Ordering::Relaxed))
} }
@ -589,23 +578,23 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
sprinting: bool, sprinting: bool,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
player.get_entity().set_sprinting(sprinting).await; player.get_entity().set_sprinting(sprinting).await;
Ok(()) Ok(())
} }
async fn is_on_ground(&mut self, player: Resource<Player>) -> Result<bool, String> { async fn is_on_ground(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_entity().on_ground.load(Ordering::Relaxed)) Ok(player.get_entity().on_ground.load(Ordering::Relaxed))
} }
async fn is_flying(&mut self, player: Resource<Player>) -> Result<bool, String> { async fn is_flying(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.is_flying().await) Ok(player.is_flying().await)
} }
async fn set_flying(&mut self, player: Resource<Player>, flying: bool) -> Result<(), String> { async fn set_flying(&mut self, player: Resource<Player>, flying: bool) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
{ {
let mut abilities = player.abilities.lock().await; let mut abilities = player.abilities.lock().await;
@ -618,7 +607,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
async fn get_abilities( async fn get_abilities(
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
) -> Result<pumpkin::plugin::player::PlayerAbilities, String> { ) -> wasmtime::Result<pumpkin::plugin::player::PlayerAbilities> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
let abilities = player.abilities.lock().await; let abilities = player.abilities.lock().await;
Ok(pumpkin::plugin::player::PlayerAbilities { Ok(pumpkin::plugin::player::PlayerAbilities {
@ -636,7 +625,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
&mut self, &mut self,
player: Resource<Player>, player: Resource<Player>,
abilities: pumpkin::plugin::player::PlayerAbilities, abilities: pumpkin::plugin::player::PlayerAbilities,
) -> Result<(), String> { ) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
{ {
let mut a = player.abilities.lock().await; let mut a = player.abilities.lock().await;
@ -652,7 +641,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
Ok(()) Ok(())
} }
async fn get_ip(&mut self, player: Resource<Player>) -> Result<String, String> { async fn get_ip(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
let player = player_from_resource(self, &player)?; let player = player_from_resource(self, &player)?;
Ok(player.get_ip().await) Ok(player.get_ip().await)
} }

View File

@ -8,47 +8,56 @@ use crate::plugin::loader::wasm::wasm_host::{
}, },
}; };
impl PluginHostState {
fn get_server_res(&self, res: &Resource<Server>) -> wasmtime::Result<&ServerResource> {
self.resource_table
.get::<ServerResource>(&Resource::new_own(res.rep()))
.map_err(wasmtime::Error::from)
}
}
impl pumpkin::plugin::server::Host for PluginHostState {} impl pumpkin::plugin::server::Host for PluginHostState {}
impl pumpkin::plugin::server::HostServer for PluginHostState { impl pumpkin::plugin::server::HostServer for PluginHostState {
async fn drop(&mut self, rep: Resource<Server>) -> wasmtime::Result<()> { async fn get_difficulty(&mut self, res: Resource<Server>) -> wasmtime::Result<Difficulty> {
let _ = self let resource = self.get_server_res(&res)?;
.resource_table
.delete::<ServerResource>(Resource::new_own(rep.rep()));
Ok(())
}
async fn get_difficulty(&mut self, server: Resource<Server>) -> Difficulty { Ok(match resource.provider.get_difficulty() {
let resource: &ServerResource = self
.resource_table
.get_any_mut(server.rep())
.expect("invalid server resource handle")
.downcast_ref::<ServerResource>()
.expect("resource type mismatch");
match resource.provider.get_difficulty() {
pumpkin_util::Difficulty::Peaceful => Difficulty::Peaceful, pumpkin_util::Difficulty::Peaceful => Difficulty::Peaceful,
pumpkin_util::Difficulty::Easy => Difficulty::Easy, pumpkin_util::Difficulty::Easy => Difficulty::Easy,
pumpkin_util::Difficulty::Normal => Difficulty::Normal, pumpkin_util::Difficulty::Normal => Difficulty::Normal,
pumpkin_util::Difficulty::Hard => Difficulty::Hard, pumpkin_util::Difficulty::Hard => Difficulty::Hard,
} })
} }
async fn get_player_count( async fn get_player_count(&mut self, _res: Resource<Server>) -> wasmtime::Result<u32> {
&mut self, let server = self
_server: Resource<pumpkin::plugin::server::Server>, .server
) -> u32 { .as_ref()
let server = self.server.as_ref().expect("server not available"); .ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
server.get_player_count() as u32 Ok(server.get_player_count() as u32)
} }
async fn get_mspt(&mut self, _server: Resource<pumpkin::plugin::server::Server>) -> f64 { async fn get_mspt(&mut self, _res: Resource<Server>) -> wasmtime::Result<f64> {
let server = self.server.as_ref().expect("server not available"); let server = self
server.get_mspt() .server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server.get_mspt())
} }
async fn get_tps(&mut self, _server: Resource<pumpkin::plugin::server::Server>) -> f64 { async fn get_tps(&mut self, _res: Resource<Server>) -> wasmtime::Result<f64> {
let server = self.server.as_ref().expect("server not available"); let server = self
server.get_tps() .server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server.get_tps())
}
async fn drop(&mut self, rep: Resource<Server>) -> wasmtime::Result<()> {
self.resource_table
.delete::<ServerResource>(Resource::new_own(rep.rep()))
.map_err(wasmtime::Error::from)?;
Ok(())
} }
} }

View File

@ -1,5 +1,4 @@
use std::borrow::Cow; use std::borrow::Cow;
use wasmtime::component::Resource; use wasmtime::component::Resource;
use crate::plugin::loader::wasm::wasm_host::{ use crate::plugin::loader::wasm::wasm_host::{
@ -12,39 +11,309 @@ use crate::plugin::loader::wasm::wasm_host::{
}; };
use pumpkin_util::text::{ use pumpkin_util::text::{
TextComponent as InternalTextComponent,
click::ClickEvent, click::ClickEvent,
color::{self, Color}, color::{self, Color},
hover::HoverEvent, hover::HoverEvent,
}; };
impl pumpkin::plugin::text::Host for PluginHostState {} // --- Trapping Helpers ---
impl PluginHostState {
fn get_text_ref(
&self,
res: &Resource<TextComponent>,
) -> wasmtime::Result<&TextComponentResource> {
self.resource_table
.get::<TextComponentResource>(&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. fn get_text_mut(
// right now we have to do a bunch of cloning due to the fact that the builder pattern doesn't accept &mut self. &mut self,
impl DowncastResourceExt<TextComponentResource> for Resource<TextComponent> { res: &Resource<TextComponent>,
) -> wasmtime::Result<&mut TextComponentResource> {
self.resource_table
.get_mut::<TextComponentResource>(&Resource::new_own(res.rep()))
.map_err(wasmtime::Error::from)
}
fn take_text(
&mut self,
res: &Resource<TextComponent>,
) -> wasmtime::Result<TextComponentResource> {
self.resource_table
.delete::<TextComponentResource>(Resource::new_own(res.rep()))
.map_err(wasmtime::Error::from)
}
}
impl DowncastResourceExt<TextComponentResource> for wasmtime::component::Resource<TextComponent> {
fn downcast_ref<'a>(&'a self, state: &'a mut PluginHostState) -> &'a TextComponentResource { fn downcast_ref<'a>(&'a self, state: &'a mut PluginHostState) -> &'a TextComponentResource {
state state
.resource_table .resource_table
.get_any_mut(self.rep()) .get_any_mut(self.rep())
.expect("invalid text-component resource handle") .expect("invalid handle")
.downcast_ref::<TextComponentResource>() .downcast_ref()
.expect("resource type mismatch") .expect("type mismatch")
} }
fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut TextComponentResource { fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut TextComponentResource {
state state
.resource_table .resource_table
.get_any_mut(self.rep()) .get_any_mut(self.rep())
.expect("invalid text-component resource handle") .expect("invalid handle")
.downcast_mut::<TextComponentResource>() .downcast_mut()
.expect("resource type mismatch") .expect("type mismatch")
} }
fn consume(self, state: &mut PluginHostState) -> TextComponentResource { fn consume(self, state: &mut PluginHostState) -> TextComponentResource {
state state
.resource_table .resource_table
.delete::<TextComponentResource>(Resource::new_own(self.rep())) .delete(wasmtime::component::Resource::new_own(self.rep()))
.expect("invalid text-component resource handle") .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<Resource<TextComponent>> {
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<Resource<TextComponent>>,
) -> wasmtime::Result<Resource<TextComponent>> {
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<TextComponent>,
child: Resource<TextComponent>,
) -> 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<TextComponent>,
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<TextComponent>,
) -> wasmtime::Result<String> {
Ok(self
.get_text_ref(&text_component)?
.provider
.clone()
.get_text())
}
async fn encode(
&mut self,
text_component: Resource<TextComponent>,
) -> wasmtime::Result<Vec<u8>> {
Ok(self
.get_text_ref(&text_component)?
.provider
.encode()
.into_vec())
}
async fn color_named(
&mut self,
res: Resource<TextComponent>,
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<TextComponent>,
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<TextComponent>, value: bool) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.bold = Some(value);
Ok(())
}
async fn italic(&mut self, res: Resource<TextComponent>, value: bool) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.italic = Some(value);
Ok(())
}
async fn underlined(
&mut self,
res: Resource<TextComponent>,
value: bool,
) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.underlined = Some(value);
Ok(())
}
async fn strikethrough(
&mut self,
res: Resource<TextComponent>,
value: bool,
) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.strikethrough = Some(value);
Ok(())
}
async fn obfuscated(
&mut self,
res: Resource<TextComponent>,
value: bool,
) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.obfuscated = Some(value);
Ok(())
}
async fn insertion(
&mut self,
res: Resource<TextComponent>,
text: String,
) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.insertion = Some(text);
Ok(())
}
async fn font(&mut self, res: Resource<TextComponent>, font: String) -> wasmtime::Result<()> {
self.get_text_mut(&res)?.provider.0.style.font = Some(font);
Ok(())
}
async fn shadow_color(
&mut self,
res: Resource<TextComponent>,
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<TextComponent>,
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<TextComponent>,
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<TextComponent>,
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<TextComponent>,
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<TextComponent>,
text: Resource<TextComponent>,
) -> 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<TextComponent>,
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<TextComponent>,
entity_type: String,
id: String,
name: Option<Resource<TextComponent>>,
) -> 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<TextComponent>) -> wasmtime::Result<()> {
self.resource_table
.delete::<TextComponentResource>(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, NamedColor::White => color::NamedColor::White,
} }
} }
impl pumpkin::plugin::text::HostTextComponent for PluginHostState {
async fn text(&mut self, plain: String) -> Resource<TextComponent> {
let tc = pumpkin_util::text::TextComponent::text(plain);
self.add_text_component(tc).unwrap()
}
async fn translate(
&mut self,
key: String,
with: Vec<Resource<TextComponent>>,
) -> Resource<TextComponent> {
let with: Vec<pumpkin_util::text::TextComponent> =
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<TextComponent>,
child: Resource<TextComponent>,
) {
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<TextComponent>, 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<TextComponent>) -> String {
text_component
.downcast_ref(self)
.provider
.clone()
.get_text()
}
async fn encode(&mut self, text_component: Resource<TextComponent>) -> Vec<u8> {
text_component
.downcast_ref(self)
.provider
.encode()
.into_vec()
}
async fn color_named(&mut self, text_component: Resource<TextComponent>, 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<TextComponent>, 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<TextComponent>, value: bool) {
text_component.downcast_mut(self).provider.0.style.bold = Some(value);
}
async fn italic(&mut self, text_component: Resource<TextComponent>, value: bool) {
text_component.downcast_mut(self).provider.0.style.italic = Some(value);
}
async fn underlined(&mut self, text_component: Resource<TextComponent>, value: bool) {
text_component
.downcast_mut(self)
.provider
.0
.style
.underlined = Some(value);
}
async fn strikethrough(&mut self, text_component: Resource<TextComponent>, value: bool) {
text_component
.downcast_mut(self)
.provider
.0
.style
.strikethrough = Some(value);
}
async fn obfuscated(&mut self, text_component: Resource<TextComponent>, value: bool) {
text_component
.downcast_mut(self)
.provider
.0
.style
.obfuscated = Some(value);
}
async fn insertion(&mut self, text_component: Resource<TextComponent>, text: String) {
text_component.downcast_mut(self).provider.0.style.insertion = Some(text);
}
async fn font(&mut self, text_component: Resource<TextComponent>, font: String) {
text_component.downcast_mut(self).provider.0.style.font = Some(font);
}
async fn shadow_color(&mut self, text_component: Resource<TextComponent>, 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<TextComponent>, 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<TextComponent>,
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<TextComponent>,
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<TextComponent>,
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<TextComponent>,
text: Resource<TextComponent>,
) {
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<TextComponent>, 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<TextComponent>,
entity_type: String,
id: String,
name: Option<Resource<TextComponent>>,
) {
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<TextComponent>) -> wasmtime::Result<()> {
let _ = self
.resource_table
.delete::<TextComponentResource>(Resource::new_own(rep.rep()));
Ok(())
}
}

View File

@ -1,85 +1,69 @@
use pumpkin_data::block_state::PistonBehavior; use pumpkin_data::block_state::PistonBehavior;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::{BlockFlags, SimpleWorld};
use wasmtime::component::Resource; use wasmtime::component::Resource;
use pumpkin_world::world::SimpleWorld;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1_0::pumpkin::plugin::world::{ use crate::plugin::loader::wasm::wasm_host::wit::v0_1_0::pumpkin::plugin::world::{
BlockFlags as WitBlockFlags, BlockPos as WitBlockPos, BlockState as WitBlockState, BlockFlags as WitBlockFlags, BlockPos as WitBlockPos, BlockState as WitBlockState,
PistonBehavior as WitPistonBehavior, PistonBehavior as WitPistonBehavior,
}; };
use crate::plugin::loader::wasm::wasm_host::{ use crate::plugin::loader::wasm::wasm_host::{
DowncastResourceExt,
state::{PluginHostState, TextComponentResource, WorldResource}, state::{PluginHostState, TextComponentResource, WorldResource},
wit::v0_1_0::pumpkin::{self, plugin::world::World}, wit::v0_1_0::pumpkin::{self, plugin::world::World},
}; };
fn text_component_from_resource( // --- Trapping Helpers ---
state: &PluginHostState, impl PluginHostState {
text: &Resource<pumpkin::plugin::text::TextComponent>, fn get_world_res(&self, res: &Resource<World>) -> wasmtime::Result<&WorldResource> {
) -> Result<pumpkin_util::text::TextComponent, String> { self.resource_table
state .get::<WorldResource>(&Resource::new_own(res.rep()))
.resource_table .map_err(wasmtime::Error::from)
.get::<TextComponentResource>(&Resource::new_own(text.rep()))
.map_err(|_| "invalid text-component resource handle".to_string())
.map(|resource| resource.provider.clone())
}
impl DowncastResourceExt<WorldResource> for Resource<World> {
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::<WorldResource>()
.expect("resource type mismatch")
} }
fn downcast_mut<'a>(&'a self, state: &'a mut PluginHostState) -> &'a mut WorldResource { fn get_text_provider(
state &self,
res: &Resource<pumpkin::plugin::text::TextComponent>,
) -> wasmtime::Result<pumpkin_util::text::TextComponent> {
Ok(self
.resource_table .resource_table
.get_any_mut(self.rep()) .get::<TextComponentResource>(&Resource::new_own(res.rep()))
.expect("invalid world resource handle") .map_err(wasmtime::Error::from)?
.downcast_mut::<WorldResource>() .provider
.expect("resource type mismatch") .clone())
}
fn consume(self, state: &mut PluginHostState) -> WorldResource {
state
.resource_table
.delete::<WorldResource>(Resource::new_own(self.rep()))
.expect("invalid world resource handle")
} }
} }
impl pumpkin::plugin::world::Host for PluginHostState {} impl pumpkin::plugin::world::Host for PluginHostState {}
impl pumpkin::plugin::world::HostWorld for PluginHostState { impl pumpkin::plugin::world::HostWorld for PluginHostState {
async fn get_id(&mut self, world: Resource<World>) -> String { async fn get_id(&mut self, world: Resource<World>) -> wasmtime::Result<String> {
world Ok(self
.downcast_ref(self) .get_world_res(&world)?
.provider .provider
.get_world_name() .get_world_name()
.to_string() .to_string())
} }
async fn get_block_state_id(&mut self, world: Resource<World>, pos: WitBlockPos) -> u16 { async fn get_block_state_id(
let world_ref = world.downcast_ref(self); &mut self,
world: Resource<World>,
pos: WitBlockPos,
) -> wasmtime::Result<u16> {
let world_ref = self.get_world_res(&world)?;
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
Ok(world_ref.provider.get_block_state_id(&internal_pos).await)
world_ref.provider.get_block_state_id(&internal_pos).await
} }
async fn get_block_state(&mut self, world: Resource<World>, pos: WitBlockPos) -> WitBlockState { async fn get_block_state(
let world_ref = world.downcast_ref(self); &mut self,
world: Resource<World>,
pos: WitBlockPos,
) -> wasmtime::Result<WitBlockState> {
let world_ref = self.get_world_res(&world)?;
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); 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; let state = world_ref.provider.get_block_state(&internal_pos).await;
WitBlockState { Ok(WitBlockState {
id: state.id, id: state.id,
luminance: state.luminance, luminance: state.luminance,
opacity: state.opacity, opacity: state.opacity,
@ -96,7 +80,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
PistonBehavior::Ignore => WitPistonBehavior::Ignore, PistonBehavior::Ignore => WitPistonBehavior::Ignore,
PistonBehavior::PushOnly => WitPistonBehavior::PushOnly, PistonBehavior::PushOnly => WitPistonBehavior::PushOnly,
}, },
} })
} }
async fn set_block_state( async fn set_block_state(
@ -105,11 +89,10 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
pos: WitBlockPos, pos: WitBlockPos,
state: u16, state: u16,
update_flags: WitBlockFlags, update_flags: WitBlockFlags,
) { ) -> wasmtime::Result<()> {
let world_ref = world.downcast_ref(self); let world_ref = self.get_world_res(&world)?;
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z); let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
// Map WIT flags to your internal bitflags
let mut internal_flags = BlockFlags::empty(); let mut internal_flags = BlockFlags::empty();
if update_flags.contains(WitBlockFlags::NOTIFY_NEIGHBORS) { if update_flags.contains(WitBlockFlags::NOTIFY_NEIGHBORS) {
internal_flags |= BlockFlags::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; internal_flags |= BlockFlags::SKIP_BLOCK_ADDED_CALLBACK;
} }
// Update the world
world_ref world_ref
.provider .provider
.clone() .clone()
.set_block_state(&internal_pos, state, internal_flags) .set_block_state(&internal_pos, state, internal_flags)
.await; .await;
Ok(())
} }
async fn get_time_of_day(&mut self, world: Resource<World>) -> u64 { async fn get_time_of_day(&mut self, world: Resource<World>) -> wasmtime::Result<u64> {
let world_ref = world.downcast_ref(self); Ok(self.get_world_res(&world)?.provider.get_time_of_day().await as u64)
world_ref.provider.get_time_of_day().await as u64
} }
async fn set_time_of_day(&mut self, world: Resource<World>, time: u64) { async fn set_time_of_day(&mut self, world: Resource<World>, time: u64) -> wasmtime::Result<()> {
let world_ref = world.downcast_ref(self); self.get_world_res(&world)?
world_ref.provider.set_time_of_day(time as i64).await; .provider
.set_time_of_day(time as i64)
.await;
Ok(())
} }
async fn get_world_age(&mut self, world: Resource<World>) -> u64 { async fn get_world_age(&mut self, world: Resource<World>) -> wasmtime::Result<u64> {
let world_ref = world.downcast_ref(self); Ok(self.get_world_res(&world)?.provider.get_world_age().await as u64)
world_ref.provider.get_world_age().await as u64
} }
async fn get_dimension(&mut self, world: Resource<World>) -> String { async fn get_dimension(&mut self, world: Resource<World>) -> wasmtime::Result<String> {
let world_ref = world.downcast_ref(self); Ok(self
world_ref.provider.dimension.minecraft_name.to_string() .get_world_res(&world)?
.provider
.dimension
.minecraft_name
.to_string())
} }
async fn get_top_block_y(&mut self, world: Resource<World>, x: i32, z: i32) -> i32 { async fn get_top_block_y(
let world_ref = world.downcast_ref(self); &mut self,
world_ref world: Resource<World>,
x: i32,
z: i32,
) -> wasmtime::Result<i32> {
Ok(self
.get_world_res(&world)?
.provider .provider
.get_top_block(pumpkin_util::math::vector2::Vector2::new(x, z)) .get_top_block(pumpkin_util::math::vector2::Vector2::new(x, z))
.await .await)
} }
async fn get_motion_blocking_height(&mut self, world: Resource<World>, x: i32, z: i32) -> i32 { async fn get_motion_blocking_height(
let world_ref = world.downcast_ref(self); &mut self,
world_ref.provider.get_motion_blocking_height(x, z).await world: Resource<World>,
x: i32,
z: i32,
) -> wasmtime::Result<i32> {
Ok(self
.get_world_res(&world)?
.provider
.get_motion_blocking_height(x, z)
.await)
} }
async fn is_raining(&mut self, world: Resource<World>) -> bool { async fn is_raining(&mut self, world: Resource<World>) -> wasmtime::Result<bool> {
let world_ref = world.downcast_ref(self); Ok(self.get_world_res(&world)?.provider.is_raining().await)
world_ref.provider.is_raining().await
} }
async fn set_raining(&mut self, world: Resource<World>, raining: bool) { async fn set_raining(&mut self, world: Resource<World>, raining: bool) -> wasmtime::Result<()> {
let world_ref = world.downcast_ref(self); self.get_world_res(&world)?
world_ref.provider.set_raining(raining).await; .provider
.set_raining(raining)
.await;
Ok(())
} }
async fn is_thundering(&mut self, world: Resource<World>) -> bool { async fn is_thundering(&mut self, world: Resource<World>) -> wasmtime::Result<bool> {
let world_ref = world.downcast_ref(self); Ok(self.get_world_res(&world)?.provider.is_thundering().await)
world_ref.provider.is_thundering().await
} }
async fn set_thundering(&mut self, world: Resource<World>, thundering: bool) { async fn set_thundering(
let world_ref = world.downcast_ref(self); &mut self,
world_ref.provider.set_thundering(thundering).await; world: Resource<World>,
thundering: bool,
) -> wasmtime::Result<()> {
self.get_world_res(&world)?
.provider
.set_thundering(thundering)
.await;
Ok(())
} }
async fn broadcast_system_message( async fn broadcast_system_message(
@ -202,19 +211,19 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
world: Resource<World>, world: Resource<World>,
message: Resource<pumpkin::plugin::text::TextComponent>, message: Resource<pumpkin::plugin::text::TextComponent>,
overlay: bool, overlay: bool,
) { ) -> wasmtime::Result<()> {
let message = text_component_from_resource(self, &message).unwrap(); let msg = self.get_text_provider(&message)?;
let world_ref = world.downcast_ref(self); self.get_world_res(&world)?
world_ref
.provider .provider
.broadcast_system_message(&message, overlay) .broadcast_system_message(&msg, overlay)
.await; .await;
Ok(())
} }
async fn drop(&mut self, rep: Resource<World>) -> wasmtime::Result<()> { async fn drop(&mut self, rep: Resource<World>) -> wasmtime::Result<()> {
let _ = self self.resource_table
.resource_table .delete::<WorldResource>(Resource::new_own(rep.rep()))
.delete::<WorldResource>(Resource::new_own(rep.rep())); .map_err(wasmtime::Error::from)?;
Ok(()) Ok(())
} }
} }