feat(plugin-api): parse data_component and enchantment to .wit
This commit is contained in:
parent
c786bb0dc2
commit
0c56e47f43
38
pumpkin-codegen/src/wit/data_component.rs
Normal file
38
pumpkin-codegen/src/wit/data_component.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use semver::Version;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||
|
||||
pub fn build() -> String {
|
||||
let data_components: BTreeMap<String, u8> =
|
||||
serde_json::from_str(&fs::read_to_string("../assets/data_component.json").unwrap())
|
||||
.expect("Failed to parse data_component.json");
|
||||
|
||||
let mut package = Package::new(PackageName::new(
|
||||
"pumpkin",
|
||||
"plugin",
|
||||
Some(Version::new(0, 1, 0)),
|
||||
));
|
||||
let mut interface = Interface::new("data-components");
|
||||
|
||||
let mut component_enum = Enum::empty();
|
||||
let mut data_component_vec = data_components.iter().collect::<Vec<_>>();
|
||||
data_component_vec.sort_by_key(|(_, i)| **i);
|
||||
|
||||
for (raw_name, _) in data_component_vec {
|
||||
let name = raw_name
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(raw_name)
|
||||
.replace('_', "-")
|
||||
.replace('/', "-");
|
||||
component_enum.case(name);
|
||||
}
|
||||
|
||||
interface.type_def(TypeDef::new(
|
||||
"data-component",
|
||||
TypeDefKind::Enum(component_enum),
|
||||
));
|
||||
package.interface(interface);
|
||||
|
||||
package.to_string()
|
||||
}
|
||||
37
pumpkin-codegen/src/wit/enchantment.rs
Normal file
37
pumpkin-codegen/src/wit/enchantment.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use semver::Version;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||
|
||||
pub fn build() -> String {
|
||||
let enchantments: BTreeMap<String, serde_json::Value> =
|
||||
serde_json::from_str(&fs::read_to_string("../assets/enchantments.json").unwrap())
|
||||
.expect("Failed to parse enchantments.json");
|
||||
|
||||
let mut package = Package::new(PackageName::new(
|
||||
"pumpkin",
|
||||
"plugin",
|
||||
Some(Version::new(0, 1, 0)),
|
||||
));
|
||||
let mut interface = Interface::new("enchantments");
|
||||
|
||||
let mut enchantment_enum = Enum::empty();
|
||||
let mut enchantment_vec = enchantments.keys().collect::<Vec<_>>();
|
||||
enchantment_vec.sort();
|
||||
|
||||
for raw_name in enchantment_vec {
|
||||
let name = raw_name
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(raw_name)
|
||||
.replace('_', "-");
|
||||
enchantment_enum.case(name);
|
||||
}
|
||||
|
||||
interface.type_def(TypeDef::new(
|
||||
"enchantment",
|
||||
TypeDefKind::Enum(enchantment_enum),
|
||||
));
|
||||
package.interface(interface);
|
||||
|
||||
package.to_string()
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
pub mod bedrock_packet;
|
||||
pub mod data_component;
|
||||
pub mod enchantment;
|
||||
pub mod entity_type;
|
||||
pub mod java_packet;
|
||||
pub mod packet_mapping;
|
||||
@ -26,6 +28,8 @@ pub fn main() {
|
||||
(entity_type::build, "entity-types.wit"),
|
||||
(java_packet::build, "java-packets.wit"),
|
||||
(bedrock_packet::build, "bedrock-packets.wit"),
|
||||
(data_component::build, "data-components.wit"),
|
||||
(enchantment::build, "enchantments.wit"),
|
||||
];
|
||||
|
||||
for (build_fn, file) in build_functions {
|
||||
|
||||
@ -137,11 +137,6 @@ fn parse_packet_file(
|
||||
field_inits.push_str(&format!(" {}: data.{}.clone(),\n", field_name, wit_field));
|
||||
}
|
||||
},
|
||||
"VarInt" => {
|
||||
// WIT maps s32 to i32, so try_into is needed if WIT field is i32 and VarInt(i32)
|
||||
// Actually data.wit_field is i32.
|
||||
field_inits.push_str(&format!(" {}: VarInt(data.{}),\n", field_name, wit_field));
|
||||
},
|
||||
"VarUInt" => {
|
||||
field_inits.push_str(&format!(" {}: pumpkin_protocol::codec::var_uint::VarUInt(data.{}.try_into().unwrap()),\n", field_name, wit_field));
|
||||
},
|
||||
@ -174,8 +169,29 @@ fn parse_packet_file(
|
||||
}
|
||||
}
|
||||
},
|
||||
"i32" | "u32" | "i64" | "u64" | "bool" | "f32" | "f64" | "u8" | "i8" | "u16" | "i16" => {
|
||||
"i32" | "u32" | "i64" | "u64" | "bool" | "f32" | "f64" | "u8" | "i8" | "u16" | "i16" | "VarInt" => {
|
||||
if is_slice {
|
||||
if type_ident == "u8" {
|
||||
if is_ref {
|
||||
field_inits.push_str(&format!(" {}: &data.{},\n", field_name, wit_field));
|
||||
} else {
|
||||
field_inits.push_str(&format!(" {}: data.{}.clone(),\n", field_name, wit_field));
|
||||
}
|
||||
} else if type_ident == "VarInt" {
|
||||
prep_code.push_str(&format!(" let vec_{}: Vec<VarInt> = data.{}.iter().map(|v| VarInt(*v)).collect();\n", wit_field, wit_field));
|
||||
if is_ref {
|
||||
field_inits.push_str(&format!(" {}: &vec_{},\n", field_name, wit_field));
|
||||
} else {
|
||||
field_inits.push_str(&format!(" {}: vec_{},\n", field_name, wit_field));
|
||||
}
|
||||
} else {
|
||||
field_inits.push_str(&format!(" {}: data.{}.iter().map(|v| *v as _).collect(),\n", field_name, wit_field));
|
||||
}
|
||||
} else if type_ident == "VarInt" {
|
||||
field_inits.push_str(&format!(" {}: VarInt(data.{}),\n", field_name, wit_field));
|
||||
} else {
|
||||
field_inits.push_str(&format!(" {}: data.{}.try_into().unwrap(),\n", field_name, wit_field));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
possible = false;
|
||||
|
||||
@ -59,12 +59,32 @@ 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.
|
||||
|
||||
116
pumpkin-plugin-wit/v0.1/data-components.wit
Normal file
116
pumpkin-plugin-wit/v0.1/data-components.wit
Normal file
@ -0,0 +1,116 @@
|
||||
package pumpkin:plugin@0.1.0;
|
||||
|
||||
interface data-components {
|
||||
enum data-component {
|
||||
custom-data,
|
||||
max-stack-size,
|
||||
max-damage,
|
||||
damage,
|
||||
unbreakable,
|
||||
use-effects,
|
||||
custom-name,
|
||||
minimum-attack-charge,
|
||||
damage-type,
|
||||
item-name,
|
||||
item-model,
|
||||
lore,
|
||||
rarity,
|
||||
enchantments,
|
||||
can-place-on,
|
||||
can-break,
|
||||
attribute-modifiers,
|
||||
custom-model-data,
|
||||
tooltip-display,
|
||||
repair-cost,
|
||||
creative-slot-lock,
|
||||
enchantment-glint-override,
|
||||
intangible-projectile,
|
||||
food,
|
||||
consumable,
|
||||
use-remainder,
|
||||
use-cooldown,
|
||||
damage-resistant,
|
||||
tool,
|
||||
weapon,
|
||||
attack-range,
|
||||
enchantable,
|
||||
equippable,
|
||||
repairable,
|
||||
glider,
|
||||
tooltip-style,
|
||||
death-protection,
|
||||
blocks-attacks,
|
||||
piercing-weapon,
|
||||
kinetic-weapon,
|
||||
swing-animation,
|
||||
additional-trade-cost,
|
||||
stored-enchantments,
|
||||
dye,
|
||||
dyed-color,
|
||||
map-color,
|
||||
map-id,
|
||||
map-decorations,
|
||||
map-post-processing,
|
||||
charged-projectiles,
|
||||
bundle-contents,
|
||||
potion-contents,
|
||||
potion-duration-scale,
|
||||
suspicious-stew-effects,
|
||||
writable-book-content,
|
||||
written-book-content,
|
||||
trim,
|
||||
debug-stick-state,
|
||||
entity-data,
|
||||
bucket-entity-data,
|
||||
block-entity-data,
|
||||
instrument,
|
||||
provides-trim-material,
|
||||
ominous-bottle-amplifier,
|
||||
jukebox-playable,
|
||||
provides-banner-patterns,
|
||||
recipes,
|
||||
lodestone-tracker,
|
||||
firework-explosion,
|
||||
fireworks,
|
||||
profile,
|
||||
note-block-sound,
|
||||
banner-patterns,
|
||||
base-color,
|
||||
pot-decorations,
|
||||
container,
|
||||
block-state,
|
||||
bees,
|
||||
lock,
|
||||
container-loot,
|
||||
break-sound,
|
||||
villager-variant,
|
||||
wolf-variant,
|
||||
wolf-sound-variant,
|
||||
wolf-collar,
|
||||
fox-variant,
|
||||
salmon-size,
|
||||
parrot-variant,
|
||||
tropical-fish-pattern,
|
||||
tropical-fish-base-color,
|
||||
tropical-fish-pattern-color,
|
||||
mooshroom-variant,
|
||||
rabbit-variant,
|
||||
pig-variant,
|
||||
pig-sound-variant,
|
||||
cow-variant,
|
||||
cow-sound-variant,
|
||||
chicken-variant,
|
||||
chicken-sound-variant,
|
||||
zombie-nautilus-variant,
|
||||
frog-variant,
|
||||
horse-variant,
|
||||
painting-variant,
|
||||
llama-variant,
|
||||
axolotl-variant,
|
||||
cat-variant,
|
||||
cat-sound-variant,
|
||||
cat-collar,
|
||||
sheep-color,
|
||||
shulker-color,
|
||||
}
|
||||
}
|
||||
49
pumpkin-plugin-wit/v0.1/enchantments.wit
Normal file
49
pumpkin-plugin-wit/v0.1/enchantments.wit
Normal file
@ -0,0 +1,49 @@
|
||||
package pumpkin:plugin@0.1.0;
|
||||
|
||||
interface enchantments {
|
||||
enum enchantment {
|
||||
aqua-affinity,
|
||||
bane-of-arthropods,
|
||||
binding-curse,
|
||||
blast-protection,
|
||||
breach,
|
||||
channeling,
|
||||
density,
|
||||
depth-strider,
|
||||
efficiency,
|
||||
feather-falling,
|
||||
fire-aspect,
|
||||
fire-protection,
|
||||
flame,
|
||||
fortune,
|
||||
frost-walker,
|
||||
impaling,
|
||||
infinity,
|
||||
knockback,
|
||||
looting,
|
||||
loyalty,
|
||||
luck-of-the-sea,
|
||||
lunge,
|
||||
lure,
|
||||
mending,
|
||||
multishot,
|
||||
piercing,
|
||||
power,
|
||||
projectile-protection,
|
||||
protection,
|
||||
punch,
|
||||
quick-charge,
|
||||
respiration,
|
||||
riptide,
|
||||
sharpness,
|
||||
silk-touch,
|
||||
smite,
|
||||
soul-speed,
|
||||
sweeping-edge,
|
||||
swift-sneak,
|
||||
thorns,
|
||||
unbreaking,
|
||||
vanishing-curse,
|
||||
wind-burst,
|
||||
}
|
||||
}
|
||||
@ -644,6 +644,11 @@ interface player {
|
||||
/// Returns the item in the specified inventory slot.
|
||||
get-inventory-item: func (slot: u8) -> option<item-stack>;
|
||||
|
||||
/// Sets the item in the specified hand.
|
||||
set-item-in-hand: func (hand: hand, stack: option<item-stack>);
|
||||
/// Sets the item in the specified inventory slot.
|
||||
set-inventory-item: func (slot: u8, stack: option<item-stack>);
|
||||
|
||||
// Status
|
||||
|
||||
/// Returns the player's current health.
|
||||
|
||||
@ -17,11 +17,10 @@ pub struct PermissionCache {
|
||||
|
||||
impl PermissionCache {
|
||||
pub async fn load(path: &Path) -> Self {
|
||||
if let Ok(data) = fs::read_to_string(path).await {
|
||||
serde_json::from_str(&data).unwrap_or_default()
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
fs::read_to_string(path).await.map_or_else(
|
||||
|_| Self::default(),
|
||||
|data| serde_json::from_str(&data).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn save(&self, path: &Path) -> tokio::io::Result<()> {
|
||||
@ -31,13 +30,14 @@ impl PermissionCache {
|
||||
}
|
||||
|
||||
pub async fn calculate_hash(path: &Path) -> tokio::io::Result<String> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let bytes = fs::read(path).await?;
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&bytes);
|
||||
let result = hasher.finalize();
|
||||
Ok(result
|
||||
.iter()
|
||||
.map(|b| format!("{b:02x}"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(""))
|
||||
Ok(result.iter().fold(String::new(), |mut output, b| {
|
||||
let _ = write!(output, "{b:02x}");
|
||||
output
|
||||
}))
|
||||
}
|
||||
|
||||
@ -127,14 +127,13 @@ pub fn serialize_java_packet(
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CCustomPayload(data) => {
|
||||
todo!();
|
||||
// let p = pumpkin_protocol::java::client::play::CCustomPayload {
|
||||
// channel: &data.channel,
|
||||
// data: data.data,
|
||||
// };
|
||||
// let mut buf = Vec::new();
|
||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
// Some(buf.into())
|
||||
let p = pumpkin_protocol::java::client::play::CCustomPayload {
|
||||
channel: &data.channel,
|
||||
data: &data.data,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CPlayDisconnect(data) => {
|
||||
let component_reason = pumpkin_util::text::TextComponent::text(data.reason.clone());
|
||||
@ -294,7 +293,7 @@ pub fn serialize_java_packet(
|
||||
max_speed: data.max_speed.try_into().unwrap(),
|
||||
particle_count: data.particle_count.try_into().unwrap(),
|
||||
particle_id: VarInt(data.particle_id),
|
||||
data: data.data.as_slice(),
|
||||
data: &data.data,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
@ -370,13 +369,13 @@ pub fn serialize_java_packet(
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CRemoveEntities(data) => {
|
||||
todo!();
|
||||
// let p = pumpkin_protocol::java::client::play::CRemoveEntities {
|
||||
// entity_ids: VarInt(data.entity_ids),
|
||||
// };
|
||||
// let mut buf = Vec::new();
|
||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
// Some(buf.into())
|
||||
let vec_entity_ids: Vec<VarInt> = data.entity_ids.iter().map(|v| VarInt(*v)).collect();
|
||||
let p = pumpkin_protocol::java::client::play::CRemoveEntities {
|
||||
entity_ids: &vec_entity_ids,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CRemoveMobEffect(data) => {
|
||||
let p = pumpkin_protocol::java::client::play::CRemoveMobEffect {
|
||||
@ -451,14 +450,14 @@ pub fn serialize_java_packet(
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CSetPassengers(data) => {
|
||||
todo!();
|
||||
// let p = pumpkin_protocol::java::client::play::CSetPassengers {
|
||||
// entity_id: VarInt(data.entity_id),
|
||||
// passengers: VarInt(data.passengers),
|
||||
// };
|
||||
// let mut buf = Vec::new();
|
||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
// Some(buf.into())
|
||||
let vec_passengers: Vec<VarInt> = data.passengers.iter().map(|v| VarInt(*v)).collect();
|
||||
let p = pumpkin_protocol::java::client::play::CSetPassengers {
|
||||
entity_id: VarInt(data.entity_id),
|
||||
passengers: &vec_passengers,
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CTitleText(data) => {
|
||||
let component_title = pumpkin_util::text::TextComponent::text(data.title.clone());
|
||||
|
||||
@ -2,6 +2,7 @@ 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},
|
||||
@ -10,7 +11,6 @@ use crate::plugin::loader::wasm::wasm_host::{
|
||||
gui::{self, Gui, GuiType},
|
||||
},
|
||||
};
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
|
||||
impl PluginHostState {
|
||||
fn get_gui_res(&self, res: &Resource<Gui>) -> wasmtime::Result<&GuiResource> {
|
||||
@ -89,10 +89,7 @@ impl gui::HostGui for PluginHostState {
|
||||
) -> wasmtime::Result<()> {
|
||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||
if (slot as usize) < gui.inventory.slots.len() {
|
||||
let item_id = item.registry_key;
|
||||
let item_data = pumpkin_data::item::Item::from_registry_key(&item_id)
|
||||
.ok_or_else(|| wasmtime::Error::msg("Invalid item"))?;
|
||||
let item_stack = ItemStack::new(item.count, item_data);
|
||||
let item_stack = from_wit_item_stack(item);
|
||||
*gui.inventory.slots[slot as usize].lock().await = item_stack;
|
||||
}
|
||||
Ok(())
|
||||
@ -106,14 +103,7 @@ impl gui::HostGui for PluginHostState {
|
||||
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() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(WitItemStack {
|
||||
registry_key: stack.get_item().registry_key.to_string(),
|
||||
count: stack.item_count,
|
||||
}))
|
||||
}
|
||||
Ok(to_wit_item_stack(&stack))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@ -37,6 +37,8 @@ bindgen!({
|
||||
|
||||
impl pumpkin::plugin::java_packets::Host for PluginHostState {}
|
||||
impl pumpkin::plugin::bedrock_packets::Host for PluginHostState {}
|
||||
impl pumpkin::plugin::data_components::Host for PluginHostState {}
|
||||
impl pumpkin::plugin::enchantments::Host for PluginHostState {}
|
||||
|
||||
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
|
||||
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
||||
|
||||
@ -47,6 +47,22 @@ 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]>,
|
||||
}
|
||||
|
||||
impl<'de> SeqAccess<'de> for WitSeqAccess<'_> {
|
||||
type Error = ReadingError;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
||||
where
|
||||
T: serde::de::DeserializeSeed<'de>,
|
||||
{
|
||||
let val = seed.deserialize(&mut *self.deserializer)?;
|
||||
Ok(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
const fn to_wasm_java_version(
|
||||
version: JavaMinecraftVersion,
|
||||
) -> pumpkin::plugin::player::JavaMinecraftVersion {
|
||||
@ -478,19 +494,132 @@ 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.item_count == 0 {
|
||||
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 {
|
||||
@ -571,7 +700,65 @@ impl pumpkin::plugin::player::Host for PluginHostState {
|
||||
Ok(players)
|
||||
}
|
||||
}
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::events::from_wasm_hand;
|
||||
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
||||
use pumpkin_protocol::java::client::play::CSetContainerSlot;
|
||||
use pumpkin_world::inventory::Inventory;
|
||||
|
||||
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>,
|
||||
) -> 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 hand = from_wasm_hand(hand);
|
||||
let slot = match hand {
|
||||
pumpkin_util::Hand::Right => player.inventory().get_selected_slot() as usize,
|
||||
pumpkin_util::Hand::Left => PlayerInventory::OFF_HAND_SLOT,
|
||||
};
|
||||
|
||||
player.inventory().set_stack(slot, stack.clone()).await;
|
||||
|
||||
// Sync to client
|
||||
let stack_serializer = ItemStackSerializer::from(stack);
|
||||
let packet = CSetContainerSlot::new(0, 0, slot as i16, &stack_serializer);
|
||||
player.client.enqueue_packet(&packet).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_inventory_item(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
slot: u8,
|
||||
stack: Option<pumpkin::plugin::common::ItemStack>,
|
||||
) -> 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,
|
||||
);
|
||||
|
||||
player
|
||||
.inventory()
|
||||
.set_stack(slot as usize, stack.clone())
|
||||
.await;
|
||||
|
||||
// Sync to client
|
||||
let stack_serializer = ItemStackSerializer::from(stack);
|
||||
let packet = CSetContainerSlot::new(0, 0, slot as i16, &stack_serializer);
|
||||
player.client.enqueue_packet(&packet).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn as_entity(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
|
||||
@ -750,39 +750,9 @@ impl PluginManager {
|
||||
for name in sorted_names {
|
||||
if let Some((instance, metadata, loader_data, loader, path)) = plugins_map.remove(&name)
|
||||
{
|
||||
let hash = cache::calculate_hash(&path).await.unwrap_or_default();
|
||||
let allowed;
|
||||
let mut wait_time = std::time::Duration::ZERO;
|
||||
|
||||
if let Some(entry) = cache.entries.get(&hash) {
|
||||
if entry.permissions_requested == metadata.permissions {
|
||||
allowed = entry.approved;
|
||||
info!(
|
||||
"Found cached permission decision for plugin \"{}\" (approved: {})",
|
||||
metadata.name, allowed
|
||||
);
|
||||
} else {
|
||||
(allowed, wait_time) = Self::ask_permission_confirmation(&metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(&cache_path).await;
|
||||
}
|
||||
} else {
|
||||
(allowed, wait_time) = Self::ask_permission_confirmation(&metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(&cache_path).await;
|
||||
}
|
||||
let (allowed, wait_time) = self
|
||||
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path)
|
||||
.await;
|
||||
|
||||
total_wait_time += wait_time;
|
||||
|
||||
@ -812,6 +782,37 @@ impl PluginManager {
|
||||
Ok(total_wait_time)
|
||||
}
|
||||
|
||||
async fn check_permissions_cached(
|
||||
&self,
|
||||
path: &Path,
|
||||
metadata: &PluginMetadata,
|
||||
cache: &mut cache::PermissionCache,
|
||||
cache_path: &Path,
|
||||
) -> (bool, std::time::Duration) {
|
||||
let hash = cache::calculate_hash(path).await.unwrap_or_default();
|
||||
|
||||
if let Some(entry) = cache.entries.get(&hash)
|
||||
&& entry.permissions_requested == metadata.permissions
|
||||
{
|
||||
info!(
|
||||
"Found cached permission decision for plugin \"{}\" (approved: {})",
|
||||
metadata.name, entry.approved
|
||||
);
|
||||
return (entry.approved, std::time::Duration::ZERO);
|
||||
}
|
||||
|
||||
let (allowed, wait_time) = Self::ask_permission_confirmation(metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(cache_path).await;
|
||||
(allowed, wait_time)
|
||||
}
|
||||
|
||||
/// Start loading a plugin asynchronously
|
||||
async fn start_loading_plugin(
|
||||
&self,
|
||||
@ -823,39 +824,10 @@ impl PluginManager {
|
||||
|
||||
let cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
|
||||
let mut cache = cache::PermissionCache::load(&cache_path).await;
|
||||
let hash = cache::calculate_hash(path).await.unwrap_or_default();
|
||||
|
||||
let allowed;
|
||||
|
||||
if let Some(entry) = cache.entries.get(&hash) {
|
||||
if entry.permissions_requested == metadata.permissions {
|
||||
allowed = entry.approved;
|
||||
info!(
|
||||
"Found cached permission decision for plugin \"{}\" (approved: {})",
|
||||
metadata.name, allowed
|
||||
);
|
||||
} else {
|
||||
(allowed, _) = Self::ask_permission_confirmation(&metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(&cache_path).await;
|
||||
}
|
||||
} else {
|
||||
(allowed, _) = Self::ask_permission_confirmation(&metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(&cache_path).await;
|
||||
}
|
||||
let (allowed, _) = self
|
||||
.check_permissions_cached(path, &metadata, &mut cache, &cache_path)
|
||||
.await;
|
||||
|
||||
if !allowed {
|
||||
warn!(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user