feat(plugin-api): expand item stack
This commit is contained in:
parent
0c56e47f43
commit
cfeb5d80fc
@ -2,7 +2,8 @@ interface command {
|
||||
use %world.{%world, entity};
|
||||
use player.{player};
|
||||
use block-entity.{command-block-entity};
|
||||
use common.{position, block-position, locale, item-stack, game-mode};
|
||||
use common.{position, block-position, locale, game-mode};
|
||||
use item-stack.{item-stack};
|
||||
use server.{server, difficulty};
|
||||
use text.{text-component};
|
||||
|
||||
|
||||
@ -59,34 +59,6 @@ interface common {
|
||||
b: u8,
|
||||
}
|
||||
|
||||
use data-components.{data-component};
|
||||
use enchantments.{enchantment};
|
||||
|
||||
/// Represents an enchantment and its level.
|
||||
record enchantment-value {
|
||||
enchantment: enchantment,
|
||||
level: u32,
|
||||
}
|
||||
|
||||
/// Represents a data component and its serialized value.
|
||||
record data-component-value {
|
||||
component: data-component,
|
||||
/// The serialized value of the component.
|
||||
value: list<u8>,
|
||||
}
|
||||
|
||||
/// Represents a stack of items with its type and count.
|
||||
record item-stack {
|
||||
/// The unique registry key (e.g., "minecraft:diamond").
|
||||
registry-key: string,
|
||||
/// The number of items in the stack.
|
||||
count: u8,
|
||||
/// The data components associated with this item stack.
|
||||
components: list<data-component-value>,
|
||||
/// The enchantments associated with this item stack.
|
||||
enchantments: list<enchantment-value>,
|
||||
}
|
||||
|
||||
/// Represents the type of click interaction in a GUI.
|
||||
enum click-type {
|
||||
left,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
interface event {
|
||||
use common.{block-position, game-mode, hand, item-stack, position, click-type};
|
||||
use common.{block-position, game-mode, hand, position, click-type};
|
||||
use item-stack.{item-stack};
|
||||
use player.{player};
|
||||
use text.{text-component};
|
||||
use %world.{%world};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
interface gui {
|
||||
use text.{text-component};
|
||||
use common.{item-stack, click-type};
|
||||
use common.{click-type};
|
||||
use item-stack.{item-stack};
|
||||
|
||||
enum gui-type {
|
||||
generic-9x1,
|
||||
|
||||
59
pumpkin-plugin-wit/v0.1/item-stack.wit
Normal file
59
pumpkin-plugin-wit/v0.1/item-stack.wit
Normal file
@ -0,0 +1,59 @@
|
||||
interface item-stack {
|
||||
use data-components.{data-component};
|
||||
use enchantments.{enchantment};
|
||||
use text.{text-component};
|
||||
|
||||
/// Represents an enchantment and its level.
|
||||
record enchantment-value {
|
||||
enchantment: enchantment,
|
||||
level: u32,
|
||||
}
|
||||
|
||||
/// Represents a data component and its serialized value.
|
||||
record data-component-value {
|
||||
component: data-component,
|
||||
/// The serialized value of the component.
|
||||
value: list<u8>,
|
||||
}
|
||||
|
||||
resource item-stack {
|
||||
constructor(registry-key: string, count: u8);
|
||||
|
||||
/// Returns the unique registry key of the item (e.g., "minecraft:diamond").
|
||||
get-registry-key: func() -> string;
|
||||
|
||||
/// Returns the number of items in the stack.
|
||||
get-count: func() -> u8;
|
||||
/// Sets the number of items in the stack.
|
||||
set-count: func(count: u8);
|
||||
|
||||
/// Returns the maximum number of items that can be in this stack.
|
||||
get-max-count: func() -> u8;
|
||||
|
||||
/// Returns all enchantments on this item stack.
|
||||
get-enchantments: func() -> list<enchantment-value>;
|
||||
/// Adds an enchantment to this item stack.
|
||||
add-enchantment: func(enchantment: enchantment, level: u32);
|
||||
/// Removes an enchantment from this item stack.
|
||||
remove-enchantment: func(enchantment: enchantment);
|
||||
|
||||
/// Returns the lore of this item stack.
|
||||
get-lore: func() -> list<text-component>;
|
||||
/// Sets the lore of this item stack.
|
||||
set-lore: func(lore: list<text-component>);
|
||||
/// Adds a line to the lore of this item stack.
|
||||
add-lore: func(line: text-component);
|
||||
|
||||
/// Returns the custom name of this item stack, if set.
|
||||
get-custom-name: func() -> option<text-component>;
|
||||
/// Sets the custom name of this item stack.
|
||||
set-custom-name: func(name: option<text-component>);
|
||||
|
||||
/// Returns all data components on this item stack.
|
||||
get-components: func() -> list<data-component-value>;
|
||||
/// Sets a data component on this item stack.
|
||||
set-component: func(component: data-component, value: list<u8>);
|
||||
/// Removes a data component from this item stack.
|
||||
remove-component: func(component: data-component);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
/// and interactive inputs (checkboxes, text fields, sliders).
|
||||
interface java-dialogs {
|
||||
use text.{text-component};
|
||||
use common.{item-stack};
|
||||
use item-stack.{item-stack};
|
||||
|
||||
/// Represents the layout and button configuration of a dialog.
|
||||
enum dialog-type {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
/// Represents a player connected to the Minecraft server.
|
||||
interface player {
|
||||
use common.{hand, item-stack, position, game-mode};
|
||||
use common.{hand, position, game-mode};
|
||||
use item-stack.{item-stack};
|
||||
use %world.{%world, entity};
|
||||
use permission.{permission-level};
|
||||
use text.{text-component};
|
||||
|
||||
@ -246,6 +246,18 @@ interface %world {
|
||||
/// Returns the minimum Y coordinate (bottom of the world).
|
||||
get-min-y: func() -> s32;
|
||||
|
||||
/// Returns the sky light level at the specified position.
|
||||
get-sky-light: func(pos: block-pos) -> u8;
|
||||
|
||||
/// Sets the sky light level at the specified position.
|
||||
set-sky-light: func(pos: block-pos, level: u8);
|
||||
|
||||
/// Returns the block light level at the specified position.
|
||||
get-block-light: func(pos: block-pos) -> u8;
|
||||
|
||||
/// Sets the block light level at the specified position.
|
||||
set-block-light: func(pos: block-pos, level: u8);
|
||||
|
||||
/// Returns a list of all entities in this world.
|
||||
get-entities: func() -> list<entity>;
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ pub type CommandResource = WasmResource<CommandTree>;
|
||||
pub type CommandSenderResource = WasmResource<CommandSender>;
|
||||
pub type ConsumedArgsResource = WasmResource<OwnedConsumedArgs>;
|
||||
pub type CommandNodeResource = WasmResource<NonLeafNodeBuilder>;
|
||||
pub type ItemStackResource = WasmResource<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>>;
|
||||
|
||||
pub type OwnedConsumedArgs = HashMap<String, OwnedArg>;
|
||||
|
||||
@ -220,6 +221,14 @@ impl PluginHostState {
|
||||
let resource = self.resource_table.push(CommandNodeResource { provider })?;
|
||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||
}
|
||||
|
||||
pub fn add_item_stack<T>(
|
||||
&mut self,
|
||||
provider: Arc<Mutex<pumpkin_data::item_stack::ItemStack>>,
|
||||
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
||||
let resource = self.resource_table.push(ItemStackResource { provider })?;
|
||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PluginHttpHooks {
|
||||
|
||||
@ -10,7 +10,6 @@ use crate::plugin::{
|
||||
to_wasm_click_type, to_wasm_entity_interaction_action, to_wasm_entity_type,
|
||||
to_wasm_game_mode, to_wasm_hand, to_wasm_position,
|
||||
},
|
||||
player::to_wit_item_stack,
|
||||
pumpkin::plugin::event::{
|
||||
BedrockFormResponseEventData, CustomClickActionEventData, Event,
|
||||
InteractAction as WasmInteractAction, InventoryClickEventData,
|
||||
@ -105,6 +104,9 @@ impl ToFromWasmEvent for InventoryCloseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
impl ToFromWasmEvent for InventoryClickEvent {
|
||||
fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {
|
||||
let player = state
|
||||
@ -117,8 +119,16 @@ impl ToFromWasmEvent for InventoryClickEvent {
|
||||
click_type: to_wasm_click_type(self.click_type),
|
||||
slot: self.slot,
|
||||
raw_slot: self.raw_slot,
|
||||
clicked_item: self.clicked_item.as_ref().and_then(to_wit_item_stack),
|
||||
cursor: self.cursor.as_ref().and_then(to_wit_item_stack),
|
||||
clicked_item: self.clicked_item.as_ref().map(|stack| {
|
||||
state
|
||||
.add_item_stack(Arc::new(Mutex::new(stack.clone())))
|
||||
.expect("failed to add item stack resource")
|
||||
}),
|
||||
cursor: self.cursor.as_ref().map(|stack| {
|
||||
state
|
||||
.add_item_stack(Arc::new(Mutex::new(stack.clone())))
|
||||
.expect("failed to add item stack resource")
|
||||
}),
|
||||
hotbar_button: self.hotbar_button,
|
||||
cancelled: self.cancelled,
|
||||
})
|
||||
|
||||
@ -2,13 +2,12 @@ use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
use super::player::{from_wit_item_stack, to_wit_item_stack};
|
||||
use crate::plugin::api::gui::{PluginGui, PluginInventory};
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::{GuiResource, PluginHostState},
|
||||
wit::v0_1::pumpkin::plugin::{
|
||||
common::ItemStack as WitItemStack,
|
||||
gui::{self, Gui, GuiType},
|
||||
item_stack::ItemStack as WitHostItemStack,
|
||||
},
|
||||
};
|
||||
|
||||
@ -85,11 +84,12 @@ impl gui::HostGui for PluginHostState {
|
||||
&mut self,
|
||||
res: Resource<Gui>,
|
||||
slot: u32,
|
||||
item: WitItemStack,
|
||||
item: Resource<WitHostItemStack>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||
if (slot as usize) < gui.inventory.slots.len() {
|
||||
let item_stack = from_wit_item_stack(item);
|
||||
let item_stack = self.get_item_stack(&item)?;
|
||||
let item_stack = item_stack.lock().await.clone();
|
||||
*gui.inventory.slots[slot as usize].lock().await = item_stack;
|
||||
}
|
||||
Ok(())
|
||||
@ -99,11 +99,23 @@ impl gui::HostGui for PluginHostState {
|
||||
&mut self,
|
||||
res: Resource<Gui>,
|
||||
slot: u32,
|
||||
) -> wasmtime::Result<Option<WitItemStack>> {
|
||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||
if (slot as usize) < gui.inventory.slots.len() {
|
||||
let stack = gui.inventory.slots[slot as usize].lock().await;
|
||||
Ok(to_wit_item_stack(&stack))
|
||||
) -> wasmtime::Result<Option<Resource<WitHostItemStack>>> {
|
||||
let stack = {
|
||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||
if (slot as usize) < gui.inventory.slots.len() {
|
||||
let stack = gui.inventory.slots[slot as usize].lock().await;
|
||||
if stack.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(stack.clone())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(stack) = stack {
|
||||
Ok(Some(self.add_item_stack(Arc::new(Mutex::new(stack)))?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
352
pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs
Normal file
352
pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/item_stack.rs
Normal file
@ -0,0 +1,352 @@
|
||||
use crate::plugin::loader::wasm::wasm_host::state::{ItemStackResource, PluginHostState};
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::data_components::DataComponent as WitDataComponent;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::enchantments::Enchantment as WitEnchantment;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::{
|
||||
DataComponentValue as WitDataComponentValue, EnchantmentValue as WitEnchantmentValue,
|
||||
Host as ItemStackInterfaceHost, HostItemStack, ItemStack as ItemStackHandle,
|
||||
};
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::text::TextComponent as WitTextComponent;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::{
|
||||
WitSeqAccess, text_component_from_resource,
|
||||
};
|
||||
use pumpkin_data::Enchantment;
|
||||
use pumpkin_data::data_component::DataComponent;
|
||||
use pumpkin_data::data_component_impl::{CustomNameImpl, EnchantmentsImpl};
|
||||
use pumpkin_protocol::codec::data_component::{deserialize, serialize};
|
||||
use pumpkin_protocol::ser::deserializer::Deserializer;
|
||||
use pumpkin_protocol::ser::serializer::Serializer;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::ser::Serializer as _;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub(crate) fn to_wit_data_component(id: DataComponent) -> WitDataComponent {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id as u8) }
|
||||
}
|
||||
|
||||
pub(crate) fn from_wit_data_component(id: WitDataComponent) -> DataComponent {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id as u8) }
|
||||
}
|
||||
|
||||
pub(crate) fn to_wit_enchantment(id: &Enchantment) -> WitEnchantment {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id.id) }
|
||||
}
|
||||
|
||||
pub(crate) fn from_wit_enchantment(id: WitEnchantment) -> &'static Enchantment {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
Enchantment::from_id(id as u8).unwrap()
|
||||
}
|
||||
|
||||
impl PluginHostState {
|
||||
pub fn get_item_stack(
|
||||
&self,
|
||||
res: &Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>> {
|
||||
self.resource_table
|
||||
.get::<ItemStackResource>(&Resource::new_own(res.rep()))
|
||||
.map(|r| r.provider.clone())
|
||||
.map_err(wasmtime::Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemStackInterfaceHost for PluginHostState {}
|
||||
|
||||
impl HostItemStack for PluginHostState {
|
||||
async fn new(
|
||||
&mut self,
|
||||
registry_key: String,
|
||||
count: u8,
|
||||
) -> wasmtime::Result<Resource<ItemStackHandle>> {
|
||||
let item = pumpkin_data::item::Item::from_registry_key(
|
||||
registry_key
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(®istry_key),
|
||||
)
|
||||
.unwrap_or(&pumpkin_data::item::Item::AIR);
|
||||
let stack = pumpkin_data::item_stack::ItemStack::new(count, item);
|
||||
self.add_item_stack(Arc::new(Mutex::new(stack)))
|
||||
}
|
||||
|
||||
async fn get_registry_key(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<String> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
Ok(stack.item.registry_key.to_string())
|
||||
}
|
||||
|
||||
async fn get_count(&mut self, res: Resource<ItemStackHandle>) -> wasmtime::Result<u8> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
Ok(stack.item_count)
|
||||
}
|
||||
|
||||
async fn set_count(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
count: u8,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
stack.item_count = count;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_max_count(&mut self, res: Resource<ItemStackHandle>) -> wasmtime::Result<u8> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
// Search in components for MaxStackSize
|
||||
if let Some((_, data)) = stack
|
||||
.item
|
||||
.components
|
||||
.iter()
|
||||
.find(|(id, _)| *id == DataComponent::MaxStackSize)
|
||||
&& let Some(max_size) = data
|
||||
.as_any()
|
||||
.downcast_ref::<pumpkin_data::data_component_impl::MaxStackSizeImpl>()
|
||||
{
|
||||
return Ok(max_size.size);
|
||||
}
|
||||
Ok(64) // Default
|
||||
}
|
||||
|
||||
async fn get_enchantments(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<Vec<WitEnchantmentValue>> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
let mut enchantments = Vec::new();
|
||||
if let Some((_, Some(data))) = stack
|
||||
.patch
|
||||
.iter()
|
||||
.find(|(id, _)| *id == DataComponent::Enchantments)
|
||||
&& let Some(enc_impl) = data.as_any().downcast_ref::<EnchantmentsImpl>()
|
||||
{
|
||||
for (enc, level) in enc_impl.enchantment.iter() {
|
||||
enchantments.push(WitEnchantmentValue {
|
||||
enchantment: to_wit_enchantment(enc),
|
||||
level: *level as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(enchantments)
|
||||
}
|
||||
|
||||
async fn add_enchantment(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
enchantment: WitEnchantment,
|
||||
level: u32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
let enc = from_wit_enchantment(enchantment);
|
||||
|
||||
let mut current_encs = if let Some((_, Some(data))) = stack
|
||||
.patch
|
||||
.iter()
|
||||
.find(|(id, _)| *id == DataComponent::Enchantments)
|
||||
{
|
||||
data.as_any()
|
||||
.downcast_ref::<EnchantmentsImpl>()
|
||||
.map(|e| e.enchantment.clone().into_owned())
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
current_encs.retain(|(e, _)| e.id != enc.id);
|
||||
current_encs.push((enc, level as i32));
|
||||
|
||||
if let Some((_, data)) = stack
|
||||
.patch
|
||||
.iter_mut()
|
||||
.find(|(id, _)| *id == DataComponent::Enchantments)
|
||||
{
|
||||
*data = Some(Box::new(EnchantmentsImpl {
|
||||
enchantment: Cow::from(current_encs),
|
||||
}));
|
||||
} else {
|
||||
stack.patch.push((
|
||||
DataComponent::Enchantments,
|
||||
Some(Box::new(EnchantmentsImpl {
|
||||
enchantment: Cow::from(current_encs),
|
||||
})),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_enchantment(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
enchantment: WitEnchantment,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
let enc = from_wit_enchantment(enchantment);
|
||||
|
||||
if let Some((_, Some(data))) = stack
|
||||
.patch
|
||||
.iter_mut()
|
||||
.find(|(id, _)| *id == DataComponent::Enchantments)
|
||||
&& let Some(enc_impl) = data.as_mut_any().downcast_mut::<EnchantmentsImpl>()
|
||||
{
|
||||
let mut encs = enc_impl.enchantment.clone().into_owned();
|
||||
encs.retain(|(e, _)| e.id != enc.id);
|
||||
enc_impl.enchantment = Cow::from(encs);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_lore(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<Vec<Resource<WitTextComponent>>> {
|
||||
let _stack = self.get_item_stack(&res)?;
|
||||
// LoreImpl is currently not fully implemented with data.
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
async fn set_lore(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
_lore: Vec<Resource<WitTextComponent>>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let _stack = self.get_item_stack(&res)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_lore(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
_line: Resource<WitTextComponent>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let _stack = self.get_item_stack(&res)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_custom_name(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<Option<Resource<WitTextComponent>>> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
if let Some((_, Some(data))) = stack
|
||||
.patch
|
||||
.iter()
|
||||
.find(|(id, _)| *id == DataComponent::CustomName)
|
||||
&& let Some(name_impl) = data.as_any().downcast_ref::<CustomNameImpl>()
|
||||
{
|
||||
let text = pumpkin_util::text::TextComponent::text(name_impl.name.clone());
|
||||
return Ok(Some(self.add_text_component(text)?));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn set_custom_name(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
name: Option<Resource<WitTextComponent>>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
if let Some(name_res) = name {
|
||||
let text = text_component_from_resource(self, &name_res);
|
||||
// This is a hack to get some string representation.
|
||||
let name_str = format!("{:?}", text.0.content);
|
||||
if let Some((_, data)) = stack
|
||||
.patch
|
||||
.iter_mut()
|
||||
.find(|(id, _)| *id == DataComponent::CustomName)
|
||||
{
|
||||
*data = Some(Box::new(CustomNameImpl { name: name_str }));
|
||||
} else {
|
||||
stack.patch.push((
|
||||
DataComponent::CustomName,
|
||||
Some(Box::new(CustomNameImpl { name: name_str })),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
stack
|
||||
.patch
|
||||
.retain(|(id, _)| *id != DataComponent::CustomName);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_components(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
) -> wasmtime::Result<Vec<WitDataComponentValue>> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let stack = stack.lock().await;
|
||||
let mut components = Vec::new();
|
||||
for (id, data) in &stack.patch {
|
||||
if let Some(data) = data {
|
||||
let mut buf = Vec::new();
|
||||
let mut serializer = Serializer::new(&mut buf);
|
||||
let mut struct_ser = serializer.serialize_struct("", 0).unwrap();
|
||||
serialize(*id, data.as_ref(), &mut struct_ser).unwrap();
|
||||
struct_ser.end().unwrap();
|
||||
|
||||
components.push(WitDataComponentValue {
|
||||
component: to_wit_data_component(*id),
|
||||
value: buf,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(components)
|
||||
}
|
||||
|
||||
async fn set_component(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
component: WitDataComponent,
|
||||
value: Vec<u8>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
let id = from_wit_data_component(component);
|
||||
let mut deserializer = Deserializer::new(value.as_slice());
|
||||
let mut seq = WitSeqAccess {
|
||||
deserializer: &mut deserializer,
|
||||
};
|
||||
|
||||
if let Ok(component_impl) = deserialize(id, &mut seq) {
|
||||
if let Some((_, data)) = stack.patch.iter_mut().find(|(pid, _)| *pid == id) {
|
||||
*data = Some(component_impl);
|
||||
} else {
|
||||
stack.patch.push((id, Some(component_impl)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_component(
|
||||
&mut self,
|
||||
res: Resource<ItemStackHandle>,
|
||||
component: WitDataComponent,
|
||||
) -> wasmtime::Result<()> {
|
||||
let stack = self.get_item_stack(&res)?;
|
||||
let mut stack = stack.lock().await;
|
||||
let id = from_wit_data_component(component);
|
||||
stack.patch.retain(|(pid, _)| *pid != id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<ItemStackHandle>) -> wasmtime::Result<()> {
|
||||
self.resource_table
|
||||
.delete::<ItemStackResource>(Resource::new_own(rep.rep()))
|
||||
.map_err(wasmtime::Error::from)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ pub mod forms;
|
||||
pub mod generated_packets;
|
||||
pub mod gui;
|
||||
pub mod i18n;
|
||||
pub mod item_stack;
|
||||
pub mod java_dialogs;
|
||||
pub mod logging;
|
||||
pub mod permission;
|
||||
|
||||
@ -47,8 +47,12 @@ use pumpkin_protocol::bedrock::client::set_actor_data::{
|
||||
use pumpkin_protocol::codec::var_ulong::VarULong;
|
||||
use pumpkin_util::version::{BedrockMinecraftVersion, JavaMinecraftVersion};
|
||||
|
||||
struct WitSeqAccess<'a> {
|
||||
deserializer: &'a mut Deserializer<&'a [u8]>,
|
||||
use pumpkin_protocol::ser::ReadingError;
|
||||
use pumpkin_protocol::ser::deserializer::Deserializer;
|
||||
use serde::de::SeqAccess;
|
||||
|
||||
pub(crate) struct WitSeqAccess<'a> {
|
||||
pub(crate) deserializer: &'a mut Deserializer<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'de> SeqAccess<'de> for WitSeqAccess<'_> {
|
||||
@ -494,132 +498,6 @@ fn world_from_resource(
|
||||
.clone()
|
||||
}
|
||||
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::DataComponentValue as WitDataComponentValue;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::EnchantmentValue as WitEnchantmentValue;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::data_components::DataComponent as WitDataComponent;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::enchantments::Enchantment as WitEnchantment;
|
||||
use pumpkin_data::Enchantment;
|
||||
use pumpkin_data::data_component::DataComponent;
|
||||
use pumpkin_data::data_component_impl::{DataComponentImpl, EnchantmentsImpl};
|
||||
use pumpkin_protocol::codec::data_component::{deserialize, serialize};
|
||||
use pumpkin_protocol::ser::ReadingError;
|
||||
use pumpkin_protocol::ser::deserializer::Deserializer;
|
||||
use pumpkin_protocol::ser::serializer::Serializer;
|
||||
use serde::de::SeqAccess;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::ser::Serializer as _;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub(super) fn to_wit_data_component(id: DataComponent) -> WitDataComponent {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id as u8) }
|
||||
}
|
||||
|
||||
pub(super) fn from_wit_data_component(id: WitDataComponent) -> DataComponent {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id as u8) }
|
||||
}
|
||||
|
||||
pub(super) fn to_wit_enchantment(id: &Enchantment) -> WitEnchantment {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
unsafe { std::mem::transmute(id.id) }
|
||||
}
|
||||
|
||||
pub(super) fn from_wit_enchantment(id: WitEnchantment) -> &'static Enchantment {
|
||||
// Safety: WIT enum is generated in the same order as the internal enum
|
||||
Enchantment::from_id(id as u8).unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn to_wit_item_stack(
|
||||
stack: &pumpkin_data::item_stack::ItemStack,
|
||||
) -> Option<pumpkin::plugin::common::ItemStack> {
|
||||
if stack.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut components = Vec::new();
|
||||
let mut enchantments = Vec::new();
|
||||
for (id, data) in &stack.patch {
|
||||
if let Some(data) = data {
|
||||
if *id == DataComponent::Enchantments {
|
||||
if let Some(enc_impl) = data.as_any().downcast_ref::<EnchantmentsImpl>() {
|
||||
for (enc, level) in enc_impl.enchantment.iter() {
|
||||
enchantments.push(WitEnchantmentValue {
|
||||
enchantment: to_wit_enchantment(enc),
|
||||
level: *level as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut serializer = Serializer::new(&mut buf);
|
||||
let mut struct_ser = serializer.serialize_struct("", 0).unwrap();
|
||||
serialize(*id, data.as_ref(), &mut struct_ser).unwrap();
|
||||
struct_ser.end().unwrap();
|
||||
|
||||
components.push(WitDataComponentValue {
|
||||
component: to_wit_data_component(*id),
|
||||
value: buf,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Some(pumpkin::plugin::common::ItemStack {
|
||||
registry_key: stack.item.registry_key.to_string(),
|
||||
count: stack.item_count,
|
||||
components,
|
||||
enchantments,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn from_wit_item_stack(
|
||||
stack: pumpkin::plugin::common::ItemStack,
|
||||
) -> pumpkin_data::item_stack::ItemStack {
|
||||
let mut patch: Vec<(DataComponent, Option<Box<dyn DataComponentImpl>>)> = Vec::new();
|
||||
for component_value in stack.components {
|
||||
let id = from_wit_data_component(component_value.component);
|
||||
let mut deserializer = Deserializer::new(component_value.value.as_slice());
|
||||
|
||||
let mut seq = WitSeqAccess {
|
||||
deserializer: &mut deserializer,
|
||||
};
|
||||
|
||||
if let Ok(component_impl) = deserialize(id, &mut seq) {
|
||||
patch.push((id, Some(component_impl)));
|
||||
}
|
||||
}
|
||||
|
||||
if !stack.enchantments.is_empty() {
|
||||
let mut encs = Vec::new();
|
||||
for enc_val in stack.enchantments {
|
||||
encs.push((
|
||||
from_wit_enchantment(enc_val.enchantment),
|
||||
enc_val.level as i32,
|
||||
));
|
||||
}
|
||||
patch.push((
|
||||
DataComponent::Enchantments,
|
||||
Some(Box::new(EnchantmentsImpl {
|
||||
enchantment: Cow::from(encs),
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
pumpkin_data::item_stack::ItemStack::new_with_component(
|
||||
stack.count,
|
||||
pumpkin_data::item::Item::from_registry_key(
|
||||
stack
|
||||
.registry_key
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(&stack.registry_key),
|
||||
)
|
||||
.unwrap_or(&pumpkin_data::item::Item::AIR),
|
||||
patch,
|
||||
)
|
||||
}
|
||||
|
||||
const fn to_wit_permission_level(
|
||||
level: PermissionLvl,
|
||||
) -> pumpkin::plugin::permission::PermissionLevel {
|
||||
@ -705,18 +583,21 @@ use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
||||
use pumpkin_protocol::java::client::play::CSetContainerSlot;
|
||||
use pumpkin_world::inventory::Inventory;
|
||||
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::ItemStack as WitHostItemStack;
|
||||
|
||||
impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||
async fn set_item_in_hand(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
hand: pumpkin::plugin::common::Hand,
|
||||
stack: Option<pumpkin::plugin::common::ItemStack>,
|
||||
stack: Option<Resource<WitHostItemStack>>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let stack = stack.map_or_else(
|
||||
|| pumpkin_data::item_stack::ItemStack::EMPTY.clone(),
|
||||
from_wit_item_stack,
|
||||
);
|
||||
let stack = if let Some(stack_res) = stack {
|
||||
self.get_item_stack(&stack_res)?.lock().await.clone()
|
||||
} else {
|
||||
pumpkin_data::item_stack::ItemStack::EMPTY.clone()
|
||||
};
|
||||
|
||||
let hand = from_wasm_hand(hand);
|
||||
let slot = match hand {
|
||||
@ -738,13 +619,14 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
slot: u8,
|
||||
stack: Option<pumpkin::plugin::common::ItemStack>,
|
||||
stack: Option<Resource<WitHostItemStack>>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let stack = stack.map_or_else(
|
||||
|| pumpkin_data::item_stack::ItemStack::EMPTY.clone(),
|
||||
from_wit_item_stack,
|
||||
);
|
||||
let stack = if let Some(stack_res) = stack {
|
||||
self.get_item_stack(&stack_res)?.lock().await.clone()
|
||||
} else {
|
||||
pumpkin_data::item_stack::ItemStack::EMPTY.clone()
|
||||
};
|
||||
|
||||
player
|
||||
.inventory()
|
||||
@ -759,6 +641,35 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_inventory_item(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
slot: u8,
|
||||
) -> wasmtime::Result<Option<Resource<WitHostItemStack>>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let stack = player.inventory().get_stack(slot as usize).await;
|
||||
if stack.lock().await.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(self.add_item_stack(stack)?))
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_item_in_hand(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
hand: pumpkin::plugin::common::Hand,
|
||||
) -> wasmtime::Result<Option<Resource<WitHostItemStack>>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let hand = from_wasm_hand(hand);
|
||||
let stack = player.inventory().get_stack_in_hand(hand).await;
|
||||
if stack.lock().await.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(self.add_item_stack(stack)?))
|
||||
}
|
||||
}
|
||||
|
||||
async fn as_entity(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
@ -1188,35 +1099,6 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||
Ok(player.inventory.get_selected_slot())
|
||||
}
|
||||
|
||||
async fn get_item_in_hand(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
hand: pumpkin::plugin::common::Hand,
|
||||
) -> wasmtime::Result<Option<pumpkin::plugin::common::ItemStack>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let inventory = player.inventory();
|
||||
let item_stack = match hand {
|
||||
pumpkin::plugin::common::Hand::Left => inventory.off_hand_item().await,
|
||||
pumpkin::plugin::common::Hand::Right => inventory.held_item(),
|
||||
};
|
||||
let item_stack = item_stack.lock().await.clone();
|
||||
Ok(to_wit_item_stack(&item_stack))
|
||||
}
|
||||
|
||||
async fn get_inventory_item(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
slot: u8,
|
||||
) -> wasmtime::Result<Option<pumpkin::plugin::common::ItemStack>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let slot = slot as usize;
|
||||
if slot >= PlayerInventory::MAIN_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
let item_stack = player.inventory.main_inventory[slot].lock().await.clone();
|
||||
Ok(to_wit_item_stack(&item_stack))
|
||||
}
|
||||
|
||||
async fn get_health(&mut self, player: Resource<Player>) -> wasmtime::Result<f32> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.living_entity.health.load())
|
||||
|
||||
@ -354,6 +354,55 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
||||
Ok(self.get_world_res(&world)?.provider.min_y)
|
||||
}
|
||||
|
||||
async fn get_sky_light(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitBlockPos,
|
||||
) -> wasmtime::Result<u8> {
|
||||
let world_ref = self.get_world_res(&world)?;
|
||||
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||
Ok(world_ref.provider.get_sky_light_level(&internal_pos))
|
||||
}
|
||||
|
||||
async fn set_sky_light(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitBlockPos,
|
||||
level: u8,
|
||||
) -> wasmtime::Result<()> {
|
||||
let world_ref = self.get_world_res(&world)?;
|
||||
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||
world_ref.provider.set_sky_light_level(&internal_pos, level);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_block_light(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitBlockPos,
|
||||
) -> wasmtime::Result<u8> {
|
||||
let world_ref = self.get_world_res(&world)?;
|
||||
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||
Ok(world_ref
|
||||
.provider
|
||||
.get_block_light_level(&internal_pos)
|
||||
.unwrap_or(0))
|
||||
}
|
||||
|
||||
async fn set_block_light(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitBlockPos,
|
||||
level: u8,
|
||||
) -> wasmtime::Result<()> {
|
||||
let world_ref = self.get_world_res(&world)?;
|
||||
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||
world_ref
|
||||
.provider
|
||||
.set_block_light_level(&internal_pos, level);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_entities(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
|
||||
@ -3428,6 +3428,20 @@ impl World {
|
||||
.get_sky_light_level(&self.level, position)
|
||||
}
|
||||
|
||||
pub fn set_block_light_level(&self, position: &BlockPos, light_level: u8) {
|
||||
let _ = self
|
||||
.level
|
||||
.light_engine
|
||||
.set_block_light_level(&self.level, position, light_level);
|
||||
}
|
||||
|
||||
pub fn set_sky_light_level(&self, position: &BlockPos, light_level: u8) {
|
||||
let _ = self
|
||||
.level
|
||||
.light_engine
|
||||
.set_sky_light_level(&self.level, position, light_level);
|
||||
}
|
||||
|
||||
pub fn schedule_block_tick(
|
||||
&self,
|
||||
block: &Block,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user