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 OpManagerResource = WasmResource>; pub type BanManagerResource = WasmResource>; pub type WhitelistManagerResource = WasmResource>; pub type BlockEntityResource = WasmResource>; #[derive(Clone)] pub struct ContainerBlockEntity { pub provider: Arc, pub inventory: Arc, } pub type ContainerBlockEntityResource = 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 plugin: Option>, pub server: Option>, pub permissions: Vec, pub name: 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, plugin: None, server: None, permissions: Vec::new(), name: 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_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_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_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 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 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, } } }