use std::{ collections::HashMap, sync::{Arc, Weak}, }; use pumpkin_util::text::TextComponent; use tokio::sync::Mutex; use wasmtime::component::ResourceTable; use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; use wasmtime_wasi_http::{ WasiHttpCtx, p2::{ HttpError, HttpResult, WasiHttpCtxView, WasiHttpHooks, WasiHttpView, bindings::http::types::ErrorCode, default_send_request, }, }; use crate::{ command::{ CommandSender, args::ConsumedArgs, tree::{CommandTree, builder::NonLeafNodeBuilder}, }, entity::EntityBase, entity::player::Player, plugin::{ Context, api::gui::PluginGui, loader::wasm::wasm_host::{WasmPlugin, args::OwnedArg}, }, server::{RecipeManager, Server}, world::World, }; pub struct WasmResource { pub provider: T, } pub type ServerResource = WasmResource>; pub type ContextResource = WasmResource>; pub type PlayerResource = WasmResource>; pub type JavaPlayerResource = WasmResource>; pub type BedrockPlayerResource = WasmResource>; pub type EntityResource = WasmResource>; pub type WorldResource = WasmResource>; pub type ChunkResource = WasmResource<(Arc, Weak)>; pub type WorldBorderResource = WasmResource>; #[derive(Clone)] pub enum ScoreboardProvider { World(Arc), Player(Arc), } pub type ScoreboardResource = WasmResource; pub type BedrockScoreboardResource = WasmResource>; pub type GuiResource = WasmResource>>; pub type BossBarResource = WasmResource< Arc>, >; pub type TextComponentResource = WasmResource; pub type CommandResource = WasmResource; pub type CommandSenderResource = WasmResource; pub type ConsumedArgsResource = WasmResource; pub type CommandNodeResource = WasmResource; pub type ItemStackResource = WasmResource>>; pub type RecipeManagerResource = WasmResource>; pub type EnchantmentManagerResource = WasmResource>; pub type OpManagerResource = WasmResource>; pub type BanManagerResource = WasmResource>; pub type WhitelistManagerResource = WasmResource>; pub type DatapackManagerResource = WasmResource>; pub type BlockEntityResource = WasmResource>; #[derive(Clone)] pub enum InventoryProvider { Generic(Arc), PlayerMain(Arc), PlayerEnderChest(Arc), } pub type InventoryResource = WasmResource; pub type PlayerInventoryResource = WasmResource>; pub type LivingEntityResource = WasmResource>; pub type MobResource = WasmResource>; #[derive(Clone)] pub struct ContainerBlockEntity { pub provider: Arc, pub inventory: Arc, } pub type ContainerBlockEntityResource = WasmResource; pub type DisplayEntityResource = WasmResource>; pub type BlockDisplayEntityResource = WasmResource>; pub type ItemDisplayEntityResource = WasmResource>; pub type TextDisplayEntityResource = WasmResource>; pub type InteractionEntityResource = WasmResource>; #[derive(Clone, Copy)] pub struct ChunkBuffer { pub x: i32, pub z: i32, pub min_y: i32, pub height: u32, pub proto_chunk: *mut pumpkin_world::ProtoChunk, } // SAFETY: `ChunkBuffer` encapsulates a raw pointer to a proto chunk that is uniquely accessed during custom world generation phases. unsafe impl Send for ChunkBuffer {} // SAFETY: `ChunkBuffer` encapsulates a raw pointer to a proto chunk that is uniquely accessed during custom world generation phases. unsafe impl Sync for ChunkBuffer {} pub type ChunkBufferResource = WasmResource; pub type OwnedConsumedArgs = HashMap; pub struct PluginHostState { pub wasi_ctx: WasiCtx, pub wasi_http_ctx: WasiHttpCtx, pub wasi_http_hooks: PluginHttpHooks, pub resource_table: ResourceTable, pub limits: wasmtime::StoreLimits, pub plugin: Option>, pub server: Option>, pub permissions: Vec, pub name: Option, pub marketplace_metadata: Option, } impl Default for PluginHostState { fn default() -> Self { Self::new() } } impl PluginHostState { #[must_use] pub fn new() -> Self { let resource_table = ResourceTable::new(); Self { wasi_ctx: WasiCtxBuilder::new() .inherit_stdout() // allow messages & errors to be printed .inherit_stderr() // before `on_load`, e.g. during metadata retrieval .build(), wasi_http_ctx: WasiHttpCtx::new(), wasi_http_hooks: PluginHttpHooks::new(), resource_table, limits: wasmtime::StoreLimitsBuilder::new().build(), plugin: None, server: None, permissions: Vec::new(), name: None, marketplace_metadata: None, } } pub fn add_server( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(ServerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_context( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(ContextResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_player( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(PlayerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_java_player( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(JavaPlayerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_bedrock_player( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(BedrockPlayerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(EntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_living_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(LivingEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_mob( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(MobResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_world( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(WorldResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_chunk( &mut self, world: Arc, chunk: Weak, ) -> wasmtime::Result> { let resource = self.resource_table.push(ChunkResource { provider: (world, chunk), })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_world_border( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(WorldBorderResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_scoreboard( &mut self, provider: ScoreboardProvider, ) -> wasmtime::Result> { let resource = self.resource_table.push(ScoreboardResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_bedrock_scoreboard( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(BedrockScoreboardResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_gui( &mut self, provider: Arc>, ) -> wasmtime::Result> { let resource = self.resource_table.push(GuiResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_boss_bar( &mut self, provider: Arc< Mutex, >, ) -> wasmtime::Result> { let resource = self.resource_table.push(BossBarResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_text_component( &mut self, provider: TextComponent, ) -> wasmtime::Result> { let resource = self .resource_table .push(TextComponentResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_command( &mut self, provider: CommandTree, ) -> wasmtime::Result> { let resource = self.resource_table.push(CommandResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_command_sender( &mut self, command_sender: CommandSender, ) -> wasmtime::Result> { let resource = self.resource_table.push(CommandSenderResource { provider: command_sender, })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_consumed_args( &mut self, provider: &ConsumedArgs<'_>, ) -> wasmtime::Result> { let owned: HashMap = provider .iter() .map(|(k, v)| (k.to_string(), OwnedArg::from_arg(v))) .collect(); let resource = self .resource_table .push(ConsumedArgsResource { provider: owned })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_command_node( &mut self, provider: NonLeafNodeBuilder, ) -> wasmtime::Result> { let resource = self.resource_table.push(CommandNodeResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_item_stack( &mut self, provider: Arc>, ) -> wasmtime::Result> { let resource = self.resource_table.push(ItemStackResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_recipe_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(RecipeManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_enchantment_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(EnchantmentManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_op_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(OpManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_ban_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(BanManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_whitelist_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(WhitelistManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_datapack_manager( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(DatapackManagerResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_inventory( &mut self, provider: InventoryProvider, ) -> wasmtime::Result> { let resource = self.resource_table.push(InventoryResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_player_inventory( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(PlayerInventoryResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_block_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(BlockEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_container_block_entity( &mut self, provider: Arc, inventory: Arc, ) -> wasmtime::Result> { let resource = self.resource_table.push(ContainerBlockEntityResource { provider: ContainerBlockEntity { provider, inventory, }, })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn add_display_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(DisplayEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_display_entity_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&DisplayEntityResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } pub fn add_block_display_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(BlockDisplayEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_block_display_entity_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&BlockDisplayEntityResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } pub fn add_item_display_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(ItemDisplayEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_item_display_entity_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&ItemDisplayEntityResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } pub fn add_text_display_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(TextDisplayEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_text_display_entity_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&TextDisplayEntityResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } pub fn add_interaction_entity( &mut self, provider: Arc, ) -> wasmtime::Result> { let resource = self .resource_table .push(InteractionEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_interaction_entity_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&InteractionEntityResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } pub fn add_chunk_buffer( &mut self, provider: ChunkBuffer, ) -> wasmtime::Result> { let resource = self.resource_table.push(ChunkBufferResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } pub fn get_chunk_buffer_res( &self, resource: &wasmtime::component::Resource, ) -> wasmtime::Result<&ChunkBufferResource> { Ok(self .resource_table .get(&wasmtime::component::Resource::new_borrow(resource.rep()))?) } } pub struct PluginHttpHooks { pub allow_outbound: bool, } impl PluginHttpHooks { #[must_use] pub const fn new() -> Self { Self { allow_outbound: false, } } } impl Default for PluginHttpHooks { fn default() -> Self { Self::new() } } impl WasiHttpHooks for PluginHttpHooks { fn send_request( &mut self, request: hyper::Request, config: wasmtime_wasi_http::p2::types::OutgoingRequestConfig, ) -> HttpResult { if !self.allow_outbound { return Err(HttpError::from(ErrorCode::HttpRequestDenied)); } Ok(default_send_request(request, config)) } } impl wasmtime_wasi_http::p3::WasiHttpHooks for PluginHttpHooks {} impl WasiView for PluginHostState { fn ctx(&mut self) -> WasiCtxView<'_> { WasiCtxView { ctx: &mut self.wasi_ctx, table: &mut self.resource_table, } } } impl WasiHttpView for PluginHostState { fn http(&mut self) -> WasiHttpCtxView<'_> { WasiHttpCtxView { ctx: &mut self.wasi_http_ctx, table: &mut self.resource_table, hooks: &mut self.wasi_http_hooks, } } } impl wasmtime_wasi_http::p3::WasiHttpView for PluginHostState { fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> { wasmtime_wasi_http::p3::WasiHttpCtxView { ctx: &mut self.wasi_http_ctx, table: &mut self.resource_table, hooks: &mut self.wasi_http_hooks, } } }