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 bedrock_packet;
|
||||||
|
pub mod data_component;
|
||||||
|
pub mod enchantment;
|
||||||
pub mod entity_type;
|
pub mod entity_type;
|
||||||
pub mod java_packet;
|
pub mod java_packet;
|
||||||
pub mod packet_mapping;
|
pub mod packet_mapping;
|
||||||
@ -26,6 +28,8 @@ pub fn main() {
|
|||||||
(entity_type::build, "entity-types.wit"),
|
(entity_type::build, "entity-types.wit"),
|
||||||
(java_packet::build, "java-packets.wit"),
|
(java_packet::build, "java-packets.wit"),
|
||||||
(bedrock_packet::build, "bedrock-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 {
|
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));
|
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" => {
|
"VarUInt" => {
|
||||||
field_inits.push_str(&format!(" {}: pumpkin_protocol::codec::var_uint::VarUInt(data.{}.try_into().unwrap()),\n", field_name, wit_field));
|
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" => {
|
||||||
field_inits.push_str(&format!(" {}: data.{}.try_into().unwrap(),\n", field_name, wit_field));
|
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;
|
possible = false;
|
||||||
|
|||||||
@ -59,12 +59,32 @@ interface common {
|
|||||||
b: u8,
|
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.
|
/// Represents a stack of items with its type and count.
|
||||||
record item-stack {
|
record item-stack {
|
||||||
/// The unique registry key (e.g., "minecraft:diamond").
|
/// The unique registry key (e.g., "minecraft:diamond").
|
||||||
registry-key: string,
|
registry-key: string,
|
||||||
/// The number of items in the stack.
|
/// The number of items in the stack.
|
||||||
count: u8,
|
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.
|
/// 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.
|
/// Returns the item in the specified inventory slot.
|
||||||
get-inventory-item: func (slot: u8) -> option<item-stack>;
|
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
|
// Status
|
||||||
|
|
||||||
/// Returns the player's current health.
|
/// Returns the player's current health.
|
||||||
|
|||||||
@ -17,11 +17,10 @@ pub struct PermissionCache {
|
|||||||
|
|
||||||
impl PermissionCache {
|
impl PermissionCache {
|
||||||
pub async fn load(path: &Path) -> Self {
|
pub async fn load(path: &Path) -> Self {
|
||||||
if let Ok(data) = fs::read_to_string(path).await {
|
fs::read_to_string(path).await.map_or_else(
|
||||||
serde_json::from_str(&data).unwrap_or_default()
|
|_| Self::default(),
|
||||||
} else {
|
|data| serde_json::from_str(&data).unwrap_or_default(),
|
||||||
Self::default()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self, path: &Path) -> tokio::io::Result<()> {
|
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> {
|
pub async fn calculate_hash(path: &Path) -> tokio::io::Result<String> {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
let bytes = fs::read(path).await?;
|
let bytes = fs::read(path).await?;
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(&bytes);
|
hasher.update(&bytes);
|
||||||
let result = hasher.finalize();
|
let result = hasher.finalize();
|
||||||
Ok(result
|
Ok(result.iter().fold(String::new(), |mut output, b| {
|
||||||
.iter()
|
let _ = write!(output, "{b:02x}");
|
||||||
.map(|b| format!("{b:02x}"))
|
output
|
||||||
.collect::<Vec<String>>()
|
}))
|
||||||
.join(""))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,14 +127,13 @@ pub fn serialize_java_packet(
|
|||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
}
|
||||||
ClientboundPacket::CCustomPayload(data) => {
|
ClientboundPacket::CCustomPayload(data) => {
|
||||||
todo!();
|
let p = pumpkin_protocol::java::client::play::CCustomPayload {
|
||||||
// let p = pumpkin_protocol::java::client::play::CCustomPayload {
|
channel: &data.channel,
|
||||||
// channel: &data.channel,
|
data: &data.data,
|
||||||
// data: data.data,
|
};
|
||||||
// };
|
let mut buf = Vec::new();
|
||||||
// let mut buf = Vec::new();
|
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
Some(buf.into())
|
||||||
// Some(buf.into())
|
|
||||||
}
|
}
|
||||||
ClientboundPacket::CPlayDisconnect(data) => {
|
ClientboundPacket::CPlayDisconnect(data) => {
|
||||||
let component_reason = pumpkin_util::text::TextComponent::text(data.reason.clone());
|
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(),
|
max_speed: data.max_speed.try_into().unwrap(),
|
||||||
particle_count: data.particle_count.try_into().unwrap(),
|
particle_count: data.particle_count.try_into().unwrap(),
|
||||||
particle_id: VarInt(data.particle_id),
|
particle_id: VarInt(data.particle_id),
|
||||||
data: data.data.as_slice(),
|
data: &data.data,
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
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())
|
Some(buf.into())
|
||||||
}
|
}
|
||||||
ClientboundPacket::CRemoveEntities(data) => {
|
ClientboundPacket::CRemoveEntities(data) => {
|
||||||
todo!();
|
let vec_entity_ids: Vec<VarInt> = data.entity_ids.iter().map(|v| VarInt(*v)).collect();
|
||||||
// let p = pumpkin_protocol::java::client::play::CRemoveEntities {
|
let p = pumpkin_protocol::java::client::play::CRemoveEntities {
|
||||||
// entity_ids: VarInt(data.entity_ids),
|
entity_ids: &vec_entity_ids,
|
||||||
// };
|
};
|
||||||
// let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||||
// Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
}
|
||||||
ClientboundPacket::CRemoveMobEffect(data) => {
|
ClientboundPacket::CRemoveMobEffect(data) => {
|
||||||
let p = pumpkin_protocol::java::client::play::CRemoveMobEffect {
|
let p = pumpkin_protocol::java::client::play::CRemoveMobEffect {
|
||||||
@ -451,14 +450,14 @@ pub fn serialize_java_packet(
|
|||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
}
|
||||||
ClientboundPacket::CSetPassengers(data) => {
|
ClientboundPacket::CSetPassengers(data) => {
|
||||||
todo!();
|
let vec_passengers: Vec<VarInt> = data.passengers.iter().map(|v| VarInt(*v)).collect();
|
||||||
// let p = pumpkin_protocol::java::client::play::CSetPassengers {
|
let p = pumpkin_protocol::java::client::play::CSetPassengers {
|
||||||
// entity_id: VarInt(data.entity_id),
|
entity_id: VarInt(data.entity_id),
|
||||||
// passengers: VarInt(data.passengers),
|
passengers: &vec_passengers,
|
||||||
// };
|
};
|
||||||
// let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
// crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
crate::net::java::JavaClient::write_packet_for_version(&p, version, &mut buf).unwrap();
|
||||||
// Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
}
|
||||||
ClientboundPacket::CTitleText(data) => {
|
ClientboundPacket::CTitleText(data) => {
|
||||||
let component_title = pumpkin_util::text::TextComponent::text(data.title.clone());
|
let component_title = pumpkin_util::text::TextComponent::text(data.title.clone());
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use wasmtime::component::Resource;
|
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::api::gui::{PluginGui, PluginInventory};
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::{GuiResource, PluginHostState},
|
state::{GuiResource, PluginHostState},
|
||||||
@ -10,7 +11,6 @@ use crate::plugin::loader::wasm::wasm_host::{
|
|||||||
gui::{self, Gui, GuiType},
|
gui::{self, Gui, GuiType},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use pumpkin_data::item_stack::ItemStack;
|
|
||||||
|
|
||||||
impl PluginHostState {
|
impl PluginHostState {
|
||||||
fn get_gui_res(&self, res: &Resource<Gui>) -> wasmtime::Result<&GuiResource> {
|
fn get_gui_res(&self, res: &Resource<Gui>) -> wasmtime::Result<&GuiResource> {
|
||||||
@ -89,10 +89,7 @@ impl gui::HostGui for PluginHostState {
|
|||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||||
if (slot as usize) < gui.inventory.slots.len() {
|
if (slot as usize) < gui.inventory.slots.len() {
|
||||||
let item_id = item.registry_key;
|
let item_stack = from_wit_item_stack(item);
|
||||||
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);
|
|
||||||
*gui.inventory.slots[slot as usize].lock().await = item_stack;
|
*gui.inventory.slots[slot as usize].lock().await = item_stack;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -106,14 +103,7 @@ impl gui::HostGui for PluginHostState {
|
|||||||
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||||
if (slot as usize) < gui.inventory.slots.len() {
|
if (slot as usize) < gui.inventory.slots.len() {
|
||||||
let stack = gui.inventory.slots[slot as usize].lock().await;
|
let stack = gui.inventory.slots[slot as usize].lock().await;
|
||||||
if stack.is_empty() {
|
Ok(to_wit_item_stack(&stack))
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(WitItemStack {
|
|
||||||
registry_key: stack.get_item().registry_key.to_string(),
|
|
||||||
count: stack.item_count,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,8 @@ bindgen!({
|
|||||||
|
|
||||||
impl pumpkin::plugin::java_packets::Host for PluginHostState {}
|
impl pumpkin::plugin::java_packets::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::bedrock_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<()> {
|
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
|
||||||
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
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_protocol::codec::var_ulong::VarULong;
|
||||||
use pumpkin_util::version::{BedrockMinecraftVersion, JavaMinecraftVersion};
|
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(
|
const fn to_wasm_java_version(
|
||||||
version: JavaMinecraftVersion,
|
version: JavaMinecraftVersion,
|
||||||
) -> pumpkin::plugin::player::JavaMinecraftVersion {
|
) -> pumpkin::plugin::player::JavaMinecraftVersion {
|
||||||
@ -478,19 +494,132 @@ fn world_from_resource(
|
|||||||
.clone()
|
.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(
|
pub(super) fn to_wit_item_stack(
|
||||||
stack: &pumpkin_data::item_stack::ItemStack,
|
stack: &pumpkin_data::item_stack::ItemStack,
|
||||||
) -> Option<pumpkin::plugin::common::ItemStack> {
|
) -> Option<pumpkin::plugin::common::ItemStack> {
|
||||||
if stack.item_count == 0 {
|
if stack.is_empty() {
|
||||||
return None;
|
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 {
|
Some(pumpkin::plugin::common::ItemStack {
|
||||||
registry_key: stack.item.registry_key.to_string(),
|
registry_key: stack.item.registry_key.to_string(),
|
||||||
count: stack.item_count,
|
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(
|
const fn to_wit_permission_level(
|
||||||
level: PermissionLvl,
|
level: PermissionLvl,
|
||||||
) -> pumpkin::plugin::permission::PermissionLevel {
|
) -> pumpkin::plugin::permission::PermissionLevel {
|
||||||
@ -571,7 +700,65 @@ impl pumpkin::plugin::player::Host for PluginHostState {
|
|||||||
Ok(players)
|
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 {
|
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(
|
async fn as_entity(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: Resource<Player>,
|
player: Resource<Player>,
|
||||||
|
|||||||
@ -750,39 +750,9 @@ impl PluginManager {
|
|||||||
for name in sorted_names {
|
for name in sorted_names {
|
||||||
if let Some((instance, metadata, loader_data, loader, path)) = plugins_map.remove(&name)
|
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, wait_time) = self
|
||||||
let allowed;
|
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path)
|
||||||
let mut wait_time = std::time::Duration::ZERO;
|
.await;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
total_wait_time += wait_time;
|
total_wait_time += wait_time;
|
||||||
|
|
||||||
@ -812,6 +782,37 @@ impl PluginManager {
|
|||||||
Ok(total_wait_time)
|
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
|
/// Start loading a plugin asynchronously
|
||||||
async fn start_loading_plugin(
|
async fn start_loading_plugin(
|
||||||
&self,
|
&self,
|
||||||
@ -823,39 +824,10 @@ impl PluginManager {
|
|||||||
|
|
||||||
let cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
|
let cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
|
||||||
let mut cache = cache::PermissionCache::load(&cache_path).await;
|
let mut cache = cache::PermissionCache::load(&cache_path).await;
|
||||||
let hash = cache::calculate_hash(path).await.unwrap_or_default();
|
|
||||||
|
|
||||||
let allowed;
|
let (allowed, _) = self
|
||||||
|
.check_permissions_cached(path, &metadata, &mut cache, &cache_path)
|
||||||
if let Some(entry) = cache.entries.get(&hash) {
|
.await;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !allowed {
|
if !allowed {
|
||||||
warn!(
|
warn!(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user