feat: more plugin features

This commit is contained in:
Alexander Medvedev
2026-08-16 22:28:01 +02:00
parent 4ec5eb8a45
commit 8ce513ff89
85 changed files with 9579 additions and 570 deletions

2
Cargo.lock generated
View File

@@ -5434,6 +5434,7 @@ dependencies = [
"async-trait",
"bitflags 2.13.1",
"bumpalo",
"bytes",
"cc",
"cfg-if",
"encoding_rs",
@@ -5692,6 +5693,7 @@ dependencies = [
"rustls",
"tokio",
"tokio-rustls",
"tokio-util",
"tracing",
"wasmtime",
"wasmtime-wasi",

View File

@@ -214,8 +214,8 @@ ordered-float = { version = "5.3", default-features = false, features = ["std"]
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh64"] }
wasmtime = { version = "47.0", default-features = false, features = ["runtime", "component-model", "async", "cache", "cranelift", "gc", "gc-drc", "threads", "std"] }
wasmtime-wasi = { version = "47.0", default-features = false, features = ["p2"] }
wasmtime-wasi-http = { version = "47.0", default-features = false, features = ["p2", "default-send-request"] }
wasmtime-wasi = { version = "47.0", default-features = false, features = ["p2", "p3"] }
wasmtime-wasi-http = { version = "47.0", default-features = false, features = ["p2", "p3", "default-send-request"] }
# needed for interacting with wasmtime-wasi-http - keep in sync
hyper = { version = "^1.11", default-features = false }
axum = { version = "0.8.9", default-features = false, features = ["http1", "tokio"] }

View File

@@ -37,7 +37,7 @@ pub use networking::compression::CompressionConfig;
pub use networking::java::JavaConfig;
pub use networking::lan_broadcast::LANBroadcastConfig;
pub use networking::rcon::RCONConfig;
pub use plugins::PluginsConfig;
pub use plugins::{PluginOverride, PluginsConfig};
pub use pvp::PVPConfig;
pub use server_links::ServerLinksConfig;

View File

@@ -1,9 +1,167 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
/// Plugin system configuration.
#[derive(Deserialize, Serialize, Default)]
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(default)]
pub struct PluginsConfig {
/// Whether the plugin system is enabled. If false, no plugins will be loaded.
pub enabled: bool,
/// Whether to watch the plugins directory and automatically hot-reload modified plugins.
pub hot_reload: bool,
/// Whether the server asks for confirmation in the console when a plugin requests new permissions.
pub ask_permission_confirmation: bool,
/// Whether to allow loading unsigned WASM plugins.
pub allow_unsigned: bool,
/// List of permissions that are globally pre-approved for all plugins (bypassing confirmation).
pub allowed_permissions: Vec<String>,
/// List of permissions that are globally blocked for all plugins.
pub blocked_permissions: Vec<String>,
/// Whether host environment variables are inherited into WASI environments by default without explicit permissions.
pub inherit_env: bool,
/// Whether network sockets in plugins are restricted to localhost/loopback by default.
pub loopback_only: bool,
/// Optional global maximum memory limit in megabytes (MB) per plugin instance.
/// If not set, memory is only constrained by the host system's available memory.
pub max_memory_mb: Option<u64>,
/// Per-plugin configuration and overrides, keyed by plugin name.
///
/// Each entry is named after the plugin it applies to (for example `my_plugin`).
/// You only need to list the plugins you actually want to configure or override.
///
/// Example:
///
/// ```toml
/// [plugins.overrides.my_plugin]
/// enabled = true
/// allow_unsigned = true
/// max_memory_mb = 128
/// allowed_permissions = ["fs:read:data", "fs:write:data"]
/// blocked_permissions = ["network:outbound"]
/// loopback_only = true
///
/// [plugins.overrides.my_plugin.environment]
/// MY_API_KEY = "secret_key"
/// ```
pub overrides: HashMap<String, PluginOverride>,
}
impl Default for PluginsConfig {
fn default() -> Self {
Self {
enabled: true,
hot_reload: false,
ask_permission_confirmation: true,
allow_unsigned: true,
allowed_permissions: Vec::new(),
blocked_permissions: Vec::new(),
inherit_env: false,
loopback_only: false,
max_memory_mb: None,
overrides: HashMap::new(),
}
}
}
/// Settings for a single plugin, letting a server owner turn it off or change
/// its permissions, unsigned execution policy, memory limit, or environment variables.
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(default)]
pub struct PluginOverride {
/// Whether this specific plugin is enabled. If set to `false`, the plugin will be ignored during loading.
pub enabled: bool,
/// Override whether this plugin is allowed to run if unsigned.
pub allow_unsigned: Option<bool>,
/// Optional maximum memory limit in megabytes (MB) for this specific plugin.
/// Overrides the global `max_memory_mb` setting if specified.
pub max_memory_mb: Option<u64>,
/// Permissions pre-approved specifically for this plugin (skips interactive confirmation).
pub allowed_permissions: Vec<String>,
/// Additional permissions blocked specifically for this plugin.
pub blocked_permissions: Vec<String>,
/// Override whether network access is restricted to loopback for this plugin.
pub loopback_only: Option<bool>,
/// Custom environment variables passed directly to this plugin's WASI environment.
pub environment: HashMap<String, String>,
}
impl Default for PluginOverride {
fn default() -> Self {
Self {
enabled: true,
allow_unsigned: None,
max_memory_mb: None,
allowed_permissions: Vec::new(),
blocked_permissions: Vec::new(),
loopback_only: None,
environment: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config() {
let config = PluginsConfig::default();
assert!(config.enabled);
assert!(!config.hot_reload);
assert!(config.ask_permission_confirmation);
assert!(config.allow_unsigned);
assert!(config.allowed_permissions.is_empty());
assert!(config.blocked_permissions.is_empty());
assert!(!config.inherit_env);
assert!(!config.loopback_only);
assert_eq!(config.max_memory_mb, None);
assert!(config.overrides.is_empty());
}
#[test]
fn parse_toml_overrides() {
let toml_str = r#"
enabled = true
hot_reload = true
ask_permission_confirmation = false
allow_unsigned = false
allowed_permissions = ["fs:read:data"]
blocked_permissions = ["network:outbound"]
inherit_env = true
loopback_only = true
max_memory_mb = 256
[overrides.my_plugin]
enabled = false
allow_unsigned = true
max_memory_mb = 128
allowed_permissions = ["network:tcp"]
blocked_permissions = ["fs:write:data"]
loopback_only = false
[overrides.my_plugin.environment]
API_KEY = "12345"
"#;
let config: PluginsConfig = toml::from_str(toml_str).unwrap();
assert!(config.enabled);
assert!(config.hot_reload);
assert!(!config.ask_permission_confirmation);
assert!(!config.allow_unsigned);
assert_eq!(config.allowed_permissions, vec!["fs:read:data"]);
assert_eq!(config.blocked_permissions, vec!["network:outbound"]);
assert!(config.inherit_env);
assert!(config.loopback_only);
assert_eq!(config.max_memory_mb, Some(256));
let override_cfg = config.overrides.get("my_plugin").unwrap();
assert!(!override_cfg.enabled);
assert_eq!(override_cfg.allow_unsigned, Some(true));
assert_eq!(override_cfg.max_memory_mb, Some(128));
assert_eq!(override_cfg.allowed_permissions, vec!["network:tcp"]);
assert_eq!(override_cfg.blocked_permissions, vec!["fs:write:data"]);
assert_eq!(override_cfg.loopback_only, Some(false));
assert_eq!(override_cfg.environment.get("API_KEY").unwrap(), "12345");
}
}

View File

@@ -496,6 +496,63 @@ impl DamageType {
_ => None,
}
}
#[doc = r" Try to parse a damage type from a numeric registry id."]
pub const fn from_id(id: u8) -> Option<Self> {
match id {
0 => Some(Self::ARROW),
1 => Some(Self::BAD_RESPAWN_POINT),
2 => Some(Self::CACTUS),
3 => Some(Self::CAMPFIRE),
4 => Some(Self::CRAMMING),
5 => Some(Self::DRAGON_BREATH),
6 => Some(Self::DROWN),
7 => Some(Self::DRY_OUT),
8 => Some(Self::ENDER_PEARL),
9 => Some(Self::EXPLOSION),
10 => Some(Self::FALL),
11 => Some(Self::FALLING_ANVIL),
12 => Some(Self::FALLING_BLOCK),
13 => Some(Self::FALLING_STALACTITE),
14 => Some(Self::FIREBALL),
15 => Some(Self::FIREWORKS),
16 => Some(Self::FLY_INTO_WALL),
17 => Some(Self::FREEZE),
18 => Some(Self::GENERIC),
19 => Some(Self::GENERIC_KILL),
20 => Some(Self::HOT_FLOOR),
21 => Some(Self::IN_FIRE),
22 => Some(Self::IN_WALL),
23 => Some(Self::INDIRECT_MAGIC),
24 => Some(Self::LAVA),
25 => Some(Self::LIGHTNING_BOLT),
26 => Some(Self::MACE_SMASH),
27 => Some(Self::MAGIC),
28 => Some(Self::MOB_ATTACK),
29 => Some(Self::MOB_ATTACK_NO_AGGRO),
30 => Some(Self::MOB_PROJECTILE),
31 => Some(Self::ON_FIRE),
32 => Some(Self::OUT_OF_WORLD),
33 => Some(Self::OUTSIDE_BORDER),
34 => Some(Self::PLAYER_ATTACK),
35 => Some(Self::PLAYER_EXPLOSION),
36 => Some(Self::SONIC_BOOM),
37 => Some(Self::SPEAR),
38 => Some(Self::SPIT),
39 => Some(Self::STALAGMITE),
40 => Some(Self::STARVE),
41 => Some(Self::STING),
42 => Some(Self::SULFUR_CUBE_HOT),
43 => Some(Self::SWEET_BERRY_BUSH),
44 => Some(Self::THORNS),
45 => Some(Self::THROWN),
46 => Some(Self::TRIDENT),
47 => Some(Self::UNATTRIBUTED_FIREBALL),
48 => Some(Self::WIND_CHARGE),
49 => Some(Self::WITHER),
50 => Some(Self::WITHER_SKULL),
_ => None,
}
}
}
impl Taggable for DamageType {
#[inline]

View File

@@ -67639,7 +67639,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const APPLE : Self = Self { id : 878 , registry_key : "minecraft:apple" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x0Fitem.apple.name\0\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\tnutrition\x08\x05\x13saturation_modifier\x9A\x99\x99>\n\x11using_converts_to\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x02\x11minecraft:is_food\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\x0Eallow_off_hand\0\x03\ruse_animation\x02\x03\x0Cuse_duration@\x03\x11creative_category\x04\x05\x0Cmining_speed\0\0\x80?\x01\rhand_equipped\0\x03\x0Bframe_count\x02\x01\x04foil\0\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\0\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\x08\x10enchantable_slot\x04none\x03\x06damage\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x05apple\0\0\x01\x0Eshould_despawn\x01\x03\x0Emax_stack_size\x80\x01\0\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\x01\x05\x11movement_modifier33\xB3>\x05\x0Cuse_duration\xCD\xCC\xCC?\x08\x0Bstart_using\x06always\0\n\x17minecraft:use_animation\x08\x05value\x03eat\0\t\titem_tags\x08\x02\x11minecraft:is_food\0\0" } ;
pub const APPLE : Self = Self { id : 878 , registry_key : "minecraft:apple" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\x08\x02\x11minecraft:is_food\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\x01\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\xCD\xCC\xCC?\x05\x11movement_modifier33\xB3>\0\n\x0Fitem_properties\x03\x06damage\0\x03\x0Bframe_count\x02\x01\x0Eliquid_clipped\0\x05\x0Cmining_speed\0\0\x80?\x01\rhand_equipped\0\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x05apple\0\0\x01\x0Eallow_off_hand\0\x08\x0Ecreative_group\0\x03\x11enchantable_value\0\x01\x12hidden_in_commands\x02\x01\x04foil\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x80\x01\x01\x0Fstacked_by_data\0\x03\ruse_animation\x02\x03\x0Cuse_duration@\x03\x11creative_category\x04\x01\x0Eshould_despawn\x01\0\n\x17minecraft:use_animation\x08\x05value\x03eat\0\n\x0Eminecraft:tags\t\x04tags\x08\x02\x11minecraft:is_food\0\n\x16minecraft:display_name\x08\x05value\x0Fitem.apple.name\0\n\x0Eminecraft:food\x03\tnutrition\x08\x05\x13saturation_modifier\x9A\x99\x99>\x01\x0Ecan_always_eat\0\n\x11using_converts_to\0\0\0\0" } ;
pub const ARCHER_POTTERY_SHERD: Self = Self {
id: 671,
registry_key: "minecraft:archer_pottery_sherd",
@@ -67724,7 +67724,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BAKED_POTATO : Self = Self { id : 281 , registry_key : "minecraft:baked_potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\ron_use_action\x01\x03\tnutrition\n\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\0\0\0" } ;
pub const BAKED_POTATO : Self = Self { id : 281 , registry_key : "minecraft:baked_potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\ron_use_action\x01\x08\rcooldown_type\0\x08\x11using_converts_to\0\x03\tnutrition\n\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const BALLOON: Self = Self {
id: 612,
registry_key: "minecraft:balloon",
@@ -67977,7 +67977,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BEEF : Self = Self { id : 273 , registry_key : "minecraft:beef" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99>\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x03\tnutrition\x06\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\0\0" } ;
pub const BEEF : Self = Self { id : 273 , registry_key : "minecraft:beef" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x03\rcooldown_time\0\x08\x11using_converts_to\0\x03\tnutrition\x06\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x99>\0\0\0" } ;
pub const BEEHIVE: Self = Self {
id: -219,
registry_key: "minecraft:beehive",
@@ -67985,9 +67985,9 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BEETROOT : Self = Self { id : 285 , registry_key : "minecraft:beetroot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x19?\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x03\ron_use_action\x01\x03\tnutrition\x02\0\0\0" } ;
pub const BEETROOT_SEEDS : Self = Self { id : 295 , registry_key : "minecraft:beetroot_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x12minecraft:beetroot\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\0\0\0" } ;
pub const BEETROOT_SOUP : Self = Self { id : 286 , registry_key : "minecraft:beetroot_soup" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\tnutrition\x0C\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x03\ron_use_action\x01\x05\x13saturation_modifier\x9A\x99\x19?\x08\x11using_converts_to\x04bowl\0\x03\x18minecraft:max_stack_size\x02\0\0" } ;
pub const BEETROOT : Self = Self { id : 285 , registry_key : "minecraft:beetroot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x08\x11using_converts_to\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\x02\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const BEETROOT_SEEDS : Self = Self { id : 295 , registry_key : "minecraft:beetroot_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\x0Bcrop_result\x12minecraft:beetroot\x08\rplant_at_face\x02up\0\0\0" } ;
pub const BEETROOT_SOUP : Self = Self { id : 286 , registry_key : "minecraft:beetroot_soup" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x19?\x08\x11using_converts_to\x04bowl\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x03\tnutrition\x0C\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\0\x03\x18minecraft:max_stack_size\x02\x03\x16minecraft:use_duration@\0\0" } ;
pub const BELL: Self = Self {
id: -206,
registry_key: "minecraft:bell",
@@ -68149,7 +68149,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BLACK_BUNDLE : Self = Self { id : 857 , registry_key : "minecraft:black_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x01\rhand_equipped\0\x01\x04foil\0\x01\x0Fstacked_by_data\0\x01\x0Eallow_off_hand\0\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x17bundle_black_open_front\x08\x10bundle_open_back\x16bundle_black_open_back\x08\x07default\x0Cbundle_black\0\0\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\x0Eshould_despawn\x01\x03\x0Cuse_duration\0\x03\x11enchantable_value\0\x01\x12hidden_in_commands\x02\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x03\x06damage\0\x08\x0Ecreative_group\0\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\x01\x1Aallow_nested_storage_items\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\0\0" } ;
pub const BLACK_BUNDLE : Self = Self { id : 857 , registry_key : "minecraft:black_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x03\x0Bframe_count\x02\x08\x10enchantable_slot\x04none\x03\x0Cuse_duration\0\x01\x12hidden_in_commands\x02\x01\x04foil\0\x01\x0Fstacked_by_data\0\x05\x0Cmining_speed\0\0\x80?\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x01\rhand_equipped\0\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x16bundle_black_open_back\x08\x11bundle_open_front\x17bundle_black_open_front\x08\x07default\x0Cbundle_black\0\0\x01\x0Eshould_despawn\x01\x03\x06damage\0\x08\x0Ecreative_group\0\x03\x0Emax_stack_size\x02\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\t\titem_tags\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const BLACK_CANDLE: Self = Self {
id: -428,
registry_key: "minecraft:black_candle",
@@ -68325,7 +68325,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BLUE_BUNDLE : Self = Self { id : 858 , registry_key : "minecraft:blue_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x01\x04foil\0\x01\x0Eshould_despawn\x01\x01\x0Eallow_off_hand\0\x08\x10enchantable_slot\x04none\x03\x0Cuse_duration\0\x01\x12hidden_in_commands\x02\x03\x06damage\0\x03\x0Emax_stack_size\x02\x05\x0Cmining_speed\0\0\x80?\x03\x11creative_category\x06\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\x03\ruse_animation\0\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\0\x03\x0Bframe_count\x02\x01\rhand_equipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bbundle_blue\x08\x10bundle_open_back\x15bundle_blue_open_back\x08\x11bundle_open_front\x16bundle_blue_open_front\0\0\x08\x0Ecreative_group\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\t\titem_tags\0\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const BLUE_BUNDLE : Self = Self { id : 858 , registry_key : "minecraft:blue_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x03\x0Bframe_count\x02\x01\x12hidden_in_commands\x02\x05\x0Cmining_speed\0\0\x80?\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x16bundle_blue_open_front\x08\x07default\x0Bbundle_blue\x08\x10bundle_open_back\x15bundle_blue_open_back\0\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x01\x0Eliquid_clipped\0\x01\x0Eshould_despawn\x01\x03\x0Cuse_duration\0\x03\x11creative_category\x06\x03\ruse_animation\0\x03\x11enchantable_value\0\x03\x06damage\0\x08\x10enchantable_slot\x04none\x01\x0Eallow_off_hand\0\x01\rhand_equipped\0\x03\x0Emax_stack_size\x02\x01\x0Fstacked_by_data\0\x01\x04foil\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\t\titem_tags\0\0\0\0" } ;
pub const BLUE_CANDLE: Self = Self {
id: -424,
registry_key: "minecraft:blue_candle",
@@ -68564,8 +68564,8 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BREAD : Self = Self { id : 261 , registry_key : "minecraft:bread" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x08\x11using_converts_to\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\n\x03\ron_use_action\x01\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\0\0\0" } ;
pub const BREEZE_ROD : Self = Self { id : 874 , registry_key : "minecraft:breeze_rod" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x14item.breeze_rod.name\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x01\x0Eshould_despawn\x01\x01\x17can_destroy_in_creative\x01\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x03\x0Emax_stack_size\x80\x01\x03\x06damage\0\x05\x0Cmining_speed\0\0\x80?\x01\x04foil\0\x03\x0Cuse_duration\0\x03\x0Bframe_count\x02\x03\x11creative_category\x08\n\x0Eminecraft:icon\n\x08textures\x08\x07default\nbreeze_rod\0\0\x03\x11enchantable_value\0\x01\x12hidden_in_commands\x02\x01\rhand_equipped\x01\x01\x0Eallow_off_hand\0\x01\x0Eliquid_clipped\0\0\t\titem_tags\0\0\0\0" } ;
pub const BREAD : Self = Self { id : 261 , registry_key : "minecraft:bread" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x19?\x08\x11using_converts_to\0\x03\rcooldown_time\0\x03\ron_use_action\x01\x08\rcooldown_type\0\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\n\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const BREEZE_ROD : Self = Self { id : 874 , registry_key : "minecraft:breeze_rod" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x14item.breeze_rod.name\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x03\x0Cuse_duration\0\x03\x11creative_category\x08\x01\x12hidden_in_commands\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\nbreeze_rod\0\0\x01\x0Eliquid_clipped\0\x03\ruse_animation\0\x01\x04foil\0\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\x17can_destroy_in_creative\x01\x01\rhand_equipped\x01\x01\x0Eallow_off_hand\0\x08\x10enchantable_slot\x04none\x03\x11enchantable_value\0\x03\x06damage\0\x01\x0Eshould_despawn\x01\x03\x0Emax_stack_size\x80\x01\x01\x0Fstacked_by_data\0\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\t\titem_tags\0\0\0\0" } ;
pub const BREEZE_SPAWN_EGG: Self = Self {
id: 506,
registry_key: "minecraft:breeze_spawn_egg",
@@ -68629,7 +68629,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BROWN_BUNDLE : Self = Self { id : 859 , registry_key : "minecraft:brown_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x01\x17can_destroy_in_creative\x01\x01\x04foil\0\x03\x0Cuse_duration\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x16bundle_brown_open_back\x08\x07default\x0Cbundle_brown\x08\x11bundle_open_front\x17bundle_brown_open_front\0\0\x01\rhand_equipped\0\x03\x0Emax_stack_size\x02\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x01\x0Eshould_despawn\x01\x03\x06damage\0\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x03\x0Bframe_count\x02\x01\x12hidden_in_commands\x02\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\0\t\titem_tags\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\0\0\0" } ;
pub const BROWN_BUNDLE : Self = Self { id : 859 , registry_key : "minecraft:brown_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\x01\x0Eshould_despawn\x01\x05\x0Cmining_speed\0\0\x80?\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x17bundle_brown_open_front\x08\x07default\x0Cbundle_brown\x08\x10bundle_open_back\x16bundle_brown_open_back\0\0\x01\x04foil\0\x03\x11creative_category\x06\x01\rhand_equipped\0\x01\x0Eliquid_clipped\0\x08\x0Ecreative_group\0\x01\x0Eallow_off_hand\0\x03\x0Bframe_count\x02\x03\x06damage\0\x03\x0Cuse_duration\0\x03\x0Emax_stack_size\x02\x01\x12hidden_in_commands\x02\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\x03\x11enchantable_value\0\x08\x10enchantable_slot\x04none\x03\ruse_animation\0\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\0\t\titem_tags\0\0\0\0" } ;
pub const BROWN_CANDLE: Self = Self {
id: -425,
registry_key: "minecraft:brown_candle",
@@ -68805,7 +68805,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const BUNDLE : Self = Self { id : 860 , registry_key : "minecraft:bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\x03\x0Cuse_duration\0\x01\x0Eallow_off_hand\0\x01\x17can_destroy_in_creative\x01\x01\x0Fstacked_by_data\0\x01\x04foil\0\x03\x06damage\0\x08\x10enchantable_slot\x04none\x01\rhand_equipped\0\x05\x0Cmining_speed\0\0\x80?\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x06bundle\x08\x11bundle_open_front\x11bundle_open_front\x08\x10bundle_open_back\x10bundle_open_back\0\0\x01\x12hidden_in_commands\x02\x01\x0Eshould_despawn\x01\x03\x0Bframe_count\x02\x03\x11enchantable_value\0\x03\ruse_animation\0\x08\x0Ecreative_group\0\x03\x0Emax_stack_size\x02\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\0\0" } ;
pub const BUNDLE : Self = Self { id : 860 , registry_key : "minecraft:bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\t\titem_tags\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\x01\x0Fstacked_by_data\0\x01\x0Eshould_despawn\x01\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x10bundle_open_back\x08\x07default\x06bundle\x08\x11bundle_open_front\x11bundle_open_front\0\0\x01\x12hidden_in_commands\x02\x01\x0Eallow_off_hand\0\x01\x04foil\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\0\x03\x06damage\0\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\0\x05\x0Cmining_speed\0\0\x80?\x03\ruse_animation\0\x01\rhand_equipped\0\x03\x0Bframe_count\x02\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const BURN_POTTERY_SHERD: Self = Self {
id: 675,
registry_key: "minecraft:burn_pottery_sherd",
@@ -68869,7 +68869,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const CAMERA : Self = Self { id : 607 , registry_key : "minecraft:camera" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration\xC0\x9A\x0C\x08\x0Fminecraft:block\x10minecraft:camera\n\x10minecraft:camera\x05\x13black_bars_duration\xCD\xCCL>\x05\x10shutter_duration\xCD\xCCL>\x05\x17black_bars_screen_ratio\n\xD7\xA3=\x05\x13slide_away_duration\xCD\xCCL>\x05\x14shutter_screen_ratio\0\0\0?\x05\x10picture_duration\0\0\x80?\0\0\0" } ;
pub const CAMERA : Self = Self { id : 607 , registry_key : "minecraft:camera" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x08\x0Fminecraft:block\x10minecraft:camera\n\x10minecraft:camera\x05\x13black_bars_duration\xCD\xCCL>\x05\x10picture_duration\0\0\x80?\x05\x10shutter_duration\xCD\xCCL>\x05\x14shutter_screen_ratio\0\0\0?\x05\x17black_bars_screen_ratio\n\xD7\xA3=\x05\x13slide_away_duration\xCD\xCCL>\0\x03\x16minecraft:use_duration\xC0\x9A\x0C\0\0" } ;
pub const CAMPFIRE: Self = Self {
id: 601,
registry_key: "minecraft:campfire",
@@ -68898,7 +68898,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const CARROT : Self = Self { id : 279 , registry_key : "minecraft:carrot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x19?\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x06\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x03\ron_use_action\x01\0\n\x0Eminecraft:seed\x08\rplant_at_face\x02up\x08\x0Bcrop_result\x11minecraft:carrots\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\x12minecraft:farmland\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const CARROT : Self = Self { id : 279 , registry_key : "minecraft:carrot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x08\rcooldown_type\0\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x19?\x08\x11using_converts_to\0\x03\tnutrition\x06\0\x03\x16minecraft:use_duration@\n\x0Eminecraft:seed\x01\x1Aplant_at_any_solid_surface\0\x08\rplant_at_face\x02up\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\x0Bcrop_result\x11minecraft:carrots\0\0\0" } ;
pub const CARROT_ON_A_STICK: Self = Self {
id: 527,
registry_key: "minecraft:carrot_on_a_stick",
@@ -69200,7 +69200,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const CHICKEN : Self = Self { id : 275 , registry_key : "minecraft:chicken" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x03\tnutrition\x04\x08\rcooldown_type\0\x05\x13saturation_modifier\x9A\x99\x99>\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\x11using_converts_to\0\t\x07effects\n\x02\x03\tamplifier\0\x08\x04name\x06hunger\x05\x06chance\x9A\x99\x99>\x03\x08duration<\x08\rdescriptionId\rpotion.hunger\x03\x02id\"\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const CHICKEN : Self = Self { id : 275 , registry_key : "minecraft:chicken" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\t\x07effects\n\x02\x05\x06chance\x9A\x99\x99>\x03\tamplifier\0\x08\rdescriptionId\rpotion.hunger\x08\x04name\x06hunger\x03\x08duration<\x03\x02id\"\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x99>\x08\rcooldown_type\0\x08\x11using_converts_to\0\x03\ron_use_action\x01\x03\rcooldown_time\0\x03\tnutrition\x04\x01\x0Ecan_always_eat\0\0\0\0" } ;
pub const CHICKEN_SPAWN_EGG: Self = Self {
id: 439,
registry_key: "minecraft:chicken_spawn_egg",
@@ -69320,7 +69320,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const CHORUS_FRUIT : Self = Self { id : 568 , registry_key : "minecraft:chorus_fruit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x99>\x08\rcooldown_type\x0Bchorusfruit\x01\x0Ecan_always_eat\x01\x03\tnutrition\x08\x03\ron_use_action\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time(\x08\x11using_converts_to\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const CHORUS_FRUIT : Self = Self { id : 568 , registry_key : "minecraft:chorus_fruit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\ron_use_action\0\x03\rcooldown_time(\x03\tnutrition\x08\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\x0Bchorusfruit\x05\x13saturation_modifier\x9A\x99\x99>\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\x01\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const CHORUS_PLANT: Self = Self {
id: 240,
registry_key: "minecraft:chorus_plant",
@@ -69545,7 +69545,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const COD : Self = Self { id : 264 , registry_key : "minecraft:cod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x04\x05\x13saturation_modifier\xCD\xCC\xCC=\x08\x11using_converts_to\0\0\0\0" } ;
pub const COD : Self = Self { id : 264 , registry_key : "minecraft:cod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\xCD\xCC\xCC=\x03\rcooldown_time\0\x08\x11using_converts_to\0\x08\rcooldown_type\0\x03\tnutrition\x04\x03\ron_use_action\x01\0\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\0\0" } ;
pub const COD_BUCKET: Self = Self {
id: 367,
registry_key: "minecraft:cod_bucket",
@@ -69672,14 +69672,14 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const COOKED_BEEF : Self = Self { id : 274 , registry_key : "minecraft:cooked_beef" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\xCD\xCCL?\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x10\x08\rcooldown_type\0\x08\x11using_converts_to\0\x03\ron_use_action\x01\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_CHICKEN : Self = Self { id : 276 , registry_key : "minecraft:cooked_chicken" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\ron_use_action\x01\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\x0C\x08\x11using_converts_to\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_COD : Self = Self { id : 268 , registry_key : "minecraft:cooked_cod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x08\x11using_converts_to\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\n\x03\ron_use_action\x01\x08\rcooldown_type\0\0\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const COOKED_MUTTON : Self = Self { id : 561 , registry_key : "minecraft:cooked_mutton" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\ron_use_action\x01\x08\x11using_converts_to\0\x05\x13saturation_modifier\xCD\xCCL?\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\rcooldown_type\0\x03\tnutrition\x0C\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_PORKCHOP : Self = Self { id : 263 , registry_key : "minecraft:cooked_porkchop" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x03\ron_use_action\x01\x03\tnutrition\x10\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x05\x13saturation_modifier\xCD\xCCL?\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_RABBIT : Self = Self { id : 289 , registry_key : "minecraft:cooked_rabbit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\rcooldown_time\0\x08\rcooldown_type\0\x08\x11using_converts_to\0\x05\x13saturation_modifier\x9A\x99\x19?\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x03\tnutrition\n\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_SALMON : Self = Self { id : 269 , registry_key : "minecraft:cooked_salmon" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\tnutrition\x0C\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x05\x13saturation_modifier\xCD\xCCL?\x08\rcooldown_type\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\0\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKIE : Self = Self { id : 271 , registry_key : "minecraft:cookie" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCC\xCC=\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x08\x11using_converts_to\0\x03\tnutrition\x04\0\0\0" } ;
pub const COOKED_BEEF : Self = Self { id : 274 , registry_key : "minecraft:cooked_beef" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x05\x13saturation_modifier\xCD\xCCL?\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x03\tnutrition\x10\0\0\0" } ;
pub const COOKED_CHICKEN : Self = Self { id : 276 , registry_key : "minecraft:cooked_chicken" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\x03\tnutrition\x0C\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x19?\x08\x11using_converts_to\0\x03\ron_use_action\x01\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_COD : Self = Self { id : 268 , registry_key : "minecraft:cooked_cod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x08\rcooldown_type\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\n\0\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const COOKED_MUTTON : Self = Self { id : 561 , registry_key : "minecraft:cooked_mutton" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\ron_use_action\x01\x03\rcooldown_time\0\x03\tnutrition\x0C\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\xCD\xCCL?\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x08\x11using_converts_to\0\0\0\0" } ;
pub const COOKED_PORKCHOP : Self = Self { id : 263 , registry_key : "minecraft:cooked_porkchop" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\x11using_converts_to\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x03\rcooldown_time\0\x05\x13saturation_modifier\xCD\xCCL?\x03\tnutrition\x10\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_RABBIT : Self = Self { id : 289 , registry_key : "minecraft:cooked_rabbit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x19?\x03\rcooldown_time\0\x08\rcooldown_type\0\x03\ron_use_action\x01\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\tnutrition\n\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const COOKED_SALMON : Self = Self { id : 269 , registry_key : "minecraft:cooked_salmon" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x08\x11using_converts_to\0\x03\tnutrition\x0C\x05\x13saturation_modifier\xCD\xCCL?\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\0\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const COOKIE : Self = Self { id : 271 , registry_key : "minecraft:cookie" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\x04\x08\rcooldown_type\0\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCC\xCC=\x03\rcooldown_time\0\0\0\0" } ;
pub const COPPER_AXE: Self = Self {
id: 750,
registry_key: "minecraft:copper_axe",
@@ -69841,7 +69841,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const COPPER_SPEAR : Self = Self { id : 850 , registry_key : "minecraft:copper_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x11damage_conditions\x02\x0Cmax_duration\xFA\0\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\0\n\x13dismount_conditions\x02\x0Cmax_durationP\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed\0\0@A\0\x05\x11damage_multiplier\x85\xEBQ?\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\x02\x05delay\r\0\n\x14knockback_conditions\x02\x0Cmax_duration\xA5\0\x05\tmin_speed33\xA3@\x05\x12min_relative_speed\0\0\0\0\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\rhitbox_margin\0\0\x80>\x05\x0Fdamage_modifier\0\0\0\0\0\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\0\n\x10minecraft:damage\x02\x05value\x02\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x16minecraft:copper_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\t\x05items\n\x02\x08\x04name\x16minecraft:copper_ingot\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\n\x0Fitem_properties\x03\x0Emax_stack_size\x02\x05\x0Cmining_speed\0\0\x80?\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x03\ruse_animation\0\x01\x04foil\0\x01\x0Eallow_off_hand\0\x01\x0Fstacked_by_data\0\x01\x0Eshould_despawn\x01\x03\x06damage\x04\x08\x10enchantable_slot\x0Bmelee_spear\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x17can_destroy_in_creative\x01\x03\x11enchantable_value\x1A\x01\rhand_equipped\x01\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Ccopper_spear\0\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:copper_tier\x12minecraft:is_spear\0\n\x12minecraft:cooldown\x05\x08duration\x9A\x99Y?\x08\x04type\x06attack\x08\x08category\x05spear\0\t\titem_tags\x08\x04\x15minecraft:copper_tier\x12minecraft:is_spear\n\x18minecraft:swing_duration\x05\x05value\x9A\x99Y?\0\n\x16minecraft:display_name\x08\x05value\x16item.copper_spear.name\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x16minecraft:swing_sounds\x08\x0Battack_miss\x1Ditem.copper_spear.attack_miss\x08\nattack_hit\x1Citem.copper_spear.attack_hit\0\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\0\x08\x0Bstart_using\x06always\x08\x0Bstart_sound\x15item.copper_spear.use\x05\x0Cuse_duration\0\xA0\x8CG\x05\x11movement_modifier\0\0\x80?\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\r\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\x03\x0Emax_durability\xFC\x02\0\0\0" } ;
pub const COPPER_SPEAR : Self = Self { id : 850 , registry_key : "minecraft:copper_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\x08\x04\x15minecraft:copper_tier\x12minecraft:is_spear\n\x16minecraft:display_name\x08\x05value\x16item.copper_spear.name\0\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\0\x05\x11movement_modifier\0\0\x80?\x08\x0Bstart_sound\x15item.copper_spear.use\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\x03\x0Emax_durability\xFC\x02\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\x04foil\0\x01\x12hidden_in_commands\x02\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Ccopper_spear\0\0\x01\x17can_destroy_in_creative\x01\x03\x0Bframe_count\x02\x08\x10enchantable_slot\x0Bmelee_spear\x03\ruse_animation\0\x01\x0Eshould_despawn\x01\x03\x11enchantable_value\x1A\x03\x0Emax_stack_size\x02\x01\x0Fstacked_by_data\0\x03\x11creative_category\x06\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\x04\x01\rhand_equipped\x01\x01\x0Eallow_off_hand\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:copper_tier\x12minecraft:is_spear\0\n\x18minecraft:swing_duration\x05\x05value\x9A\x99Y?\0\n\x16minecraft:swing_sounds\x08\x0Battack_miss\x1Ditem.copper_spear.attack_miss\x08\nattack_hit\x1Citem.copper_spear.attack_hit\0\n\x12minecraft:cooldown\x05\x08duration\x9A\x99Y?\x08\x08category\x05spear\x08\x04type\x06attack\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\x05\x0Fdamage_modifier\0\0\0\0\n\x14knockback_conditions\x05\x12min_relative_speed\0\0\0\0\x02\x0Cmax_duration\xA5\0\x05\tmin_speed33\xA3@\0\x02\x05delay\r\0\x05\x11damage_multiplier\x85\xEBQ?\n\x13dismount_conditions\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed\0\0@A\x02\x0Cmax_durationP\0\0\n\x11damage_conditions\x02\x0Cmax_duration\xFA\0\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\0\x05\rhitbox_margin\0\0\x80>\0\0\n\x10minecraft:damage\x02\x05value\x02\0\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\r\0\n\x19minecraft:piercing_weapon\x05\rhitbox_margin\0\0\x80>\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x16minecraft:copper_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\t\x05items\n\x02\x08\x04name\x16minecraft:copper_ingot\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const COPPER_SWORD: Self = Self {
id: 747,
registry_key: "minecraft:copper_sword",
@@ -70206,7 +70206,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const CYAN_BUNDLE : Self = Self { id : 861 , registry_key : "minecraft:cyan_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\x03\x11enchantable_value\0\x03\x06damage\0\x01\x04foil\0\x03\x0Bframe_count\x02\x01\x0Fstacked_by_data\0\x01\x12hidden_in_commands\x02\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\0\x01\x0Eallow_off_hand\0\x01\x0Eshould_despawn\x01\x01\x0Eliquid_clipped\0\x03\ruse_animation\0\x08\x0Ecreative_group\0\x01\x17can_destroy_in_creative\x01\x03\x11creative_category\x06\x01\rhand_equipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x15bundle_cyan_open_back\x08\x07default\x0Bbundle_cyan\x08\x11bundle_open_front\x16bundle_cyan_open_front\0\0\x05\x0Cmining_speed\0\0\x80?\x08\x10enchantable_slot\x04none\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\0\0" } ;
pub const CYAN_BUNDLE : Self = Self { id : 861 , registry_key : "minecraft:cyan_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\0\n\x0Fitem_properties\x01\x12hidden_in_commands\x02\x03\x06damage\0\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x16bundle_cyan_open_front\x08\x07default\x0Bbundle_cyan\x08\x10bundle_open_back\x15bundle_cyan_open_back\0\0\x01\x04foil\0\x03\x0Cuse_duration\0\x01\x0Eshould_despawn\x01\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\rhand_equipped\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x03\x11creative_category\x06\x03\x0Emax_stack_size\x02\x08\x10enchantable_slot\x04none\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x01\x0Eliquid_clipped\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const CYAN_CANDLE: Self = Self {
id: -422,
registry_key: "minecraft:cyan_candle",
@@ -70928,7 +70928,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const DIAMOND_SPEAR : Self = Self { id : 851 , registry_key : "minecraft:diamond_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x19minecraft:piercing_weapon\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x02\x05delay\n\0\n\x13dismount_conditions\x05\tmin_speed\0\0 A\x02\x0Cmax_duration<\0\x05\x12min_relative_speed\0\0\0\0\0\x05\rhitbox_margin\0\0\x80>\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\x0Fdamage_modifier\0\0\0\0\n\x14knockback_conditions\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\x02\x0Cmax_duration\x82\0\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xC8\0\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\x11damage_multiplier\x9A\x99\x89?\0\0\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\0\x08\x0Bstart_sound\x16item.diamond_spear.use\x08\x0Bstart_using\x06always\x05\x11movement_modifier\0\0\x80?\x05\x0Cuse_duration\0\xA0\x8CG\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x0Eminecraft:tags\t\x04tags\x08\x06\x16minecraft:diamond_tier\x1Dminecraft:transformable_items\x12minecraft:is_spear\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\x03\x0Emax_durability\xB0\x18\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Ditem.diamond_spear.attack_hit\x08\x0Battack_miss\x1Eitem.diamond_spear.attack_miss\0\n\x12minecraft:cooldown\x05\x08durationff\x86?\x08\x08category\x05spear\x08\x04type\x06attack\0\n\x10minecraft:damage\x02\x05value\x04\0\0\n\x16minecraft:display_name\x08\x05value\x17item.diamond_spear.name\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\x08\rrepair_amount)context.other->query.remaining_durability\t\x05items\n\x02\x08\x04name\x17minecraft:diamond_spear\0\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x11minecraft:diamond\0\0\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\n\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x0Bmelee_spear\x03\x11enchantable_value\x14\x03\x0Emax_stack_size\x02\x03\x11creative_category\x06\x01\x04foil\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\rdiamond_spear\0\0\x01\x0Eshould_despawn\x01\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\ruse_animation\0\x03\x06damage\x08\x01\x17can_destroy_in_creative\x01\x01\rhand_equipped\x01\x01\x0Fstacked_by_data\0\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x03\x0Bframe_count\x02\0\n\x18minecraft:swing_duration\x05\x05valueff\x86?\0\t\titem_tags\x08\x06\x16minecraft:diamond_tier\x1Dminecraft:transformable_items\x12minecraft:is_spear\0\0" } ;
pub const DIAMOND_SPEAR : Self = Self { id : 851 , registry_key : "minecraft:diamond_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x17item.diamond_spear.name\0\t\titem_tags\x08\x06\x16minecraft:diamond_tier\x1Dminecraft:transformable_items\x12minecraft:is_spear\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\n\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x19minecraft:piercing_weapon\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\rhitbox_margin\0\0\x80>\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Ditem.diamond_spear.attack_hit\x08\x0Battack_miss\x1Eitem.diamond_spear.attack_miss\0\n\x0Eminecraft:tags\t\x04tags\x08\x06\x16minecraft:diamond_tier\x1Dminecraft:transformable_items\x12minecraft:is_spear\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x11damage_conditions\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xC8\0\x05\x12min_relative_speed33\x93@\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x14knockback_conditions\x02\x0Cmax_duration\x82\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\0\x05\x0Fdamage_modifier\0\0\0\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\rhitbox_margin\0\0\x80>\n\x13dismount_conditions\x05\tmin_speed\0\0 A\x02\x0Cmax_duration<\0\x05\x12min_relative_speed\0\0\0\0\0\x02\x05delay\n\0\x05\x11damage_multiplier\x9A\x99\x89?\0\0\n\x18minecraft:swing_duration\x05\x05valueff\x86?\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\x03\x0Emax_durability\xB0\x18\0\n\x10minecraft:damage\x02\x05value\x04\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x17minecraft:diamond_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x11minecraft:diamond\0\0\0\n\x17minecraft:use_modifiers\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\x01\x0Femit_vibrations\0\x05\x11movement_modifier\0\0\x80?\x08\x0Bstart_sound\x16item.diamond_spear.use\0\n\x12minecraft:cooldown\x05\x08durationff\x86?\x08\x04type\x06attack\x08\x08category\x05spear\0\n\x0Fitem_properties\x01\x0Eallow_off_hand\0\x01\x17can_destroy_in_creative\x01\x03\x06damage\x08\x01\rhand_equipped\x01\x01\x0Fstacked_by_data\0\x03\x0Emax_stack_size\x02\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x0Bframe_count\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\rdiamond_spear\0\0\x03\ruse_animation\0\x01\x04foil\0\x08\x0Ecreative_group\0\x01\x0Eshould_despawn\x01\x03\x0Cuse_duration\x80\xE4\xAF\x01\x08\x10enchantable_slot\x0Bmelee_spear\x03\x11enchantable_value\x14\x05\x0Cmining_speed\0\0\x80?\0\0\0" } ;
pub const DIAMOND_SWORD: Self = Self {
id: 318,
registry_key: "minecraft:diamond_sword",
@@ -71083,7 +71083,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const DRIED_KELP : Self = Self { id : 270 , registry_key : "minecraft:dried_kelp" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x03\tnutrition\x02\x03\rcooldown_time\0\x05\x13saturation_modifier\xCD\xCC\xCC=\0\x03\x16minecraft:use_duration \0\0" } ;
pub const DRIED_KELP : Self = Self { id : 270 , registry_key : "minecraft:dried_kelp" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration \n\x0Eminecraft:food\x05\x13saturation_modifier\xCD\xCC\xCC=\x03\tnutrition\x02\x08\x11using_converts_to\0\x03\rcooldown_time\0\x08\rcooldown_type\0\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\0\0" } ;
pub const DRIED_KELP_BLOCK: Self = Self {
id: -139,
registry_key: "minecraft:dried_kelp_block",
@@ -72029,7 +72029,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const ENCHANTED_GOLDEN_APPLE : Self = Self { id : 259 , registry_key : "minecraft:enchanted_golden_apple" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\n\x0Eminecraft:food\x03\rcooldown_time\0\x01\x0Ecan_always_eat\x01\x05\x13saturation_modifier\x9A\x99\x99?\x03\ron_use_action\x01\t\x07effects\n\x08\x03\x08duration<\x03\x02id\x14\x03\tamplifier\x02\x05\x06chance\0\0\x80?\x08\rdescriptionId\x13potion.regeneration\x08\x04name\x0Cregeneration\0\x05\x06chance\0\0\x80?\x03\tamplifier\x06\x08\rdescriptionId\x11potion.absorption\x03\x02id,\x03\x08duration\xF0\x01\x08\x04name\nabsorption\0\x03\x08duration\xD8\x04\x08\x04name\nresistance\x03\tamplifier\0\x03\x02id\x16\x08\rdescriptionId\x11potion.resistance\x05\x06chance\0\0\x80?\0\x03\x02id\x18\x08\x04name\x0Ffire_resistance\x08\rdescriptionId\x15potion.fireResistance\x03\tamplifier\0\x05\x06chance\0\0\x80?\x03\x08duration\xD8\x04\0\x03\tnutrition\x08\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\0\x03\x16minecraft:use_duration@\x01\x0Eminecraft:foil\x01\0\0" } ;
pub const ENCHANTED_GOLDEN_APPLE : Self = Self { id : 259 , registry_key : "minecraft:enchanted_golden_apple" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x0Eminecraft:foil\x01\n\x0Eminecraft:food\x01\x0Ecan_always_eat\x01\t\x07effects\n\x08\x05\x06chance\0\0\x80?\x03\tamplifier\x02\x03\x08duration<\x08\rdescriptionId\x13potion.regeneration\x03\x02id\x14\x08\x04name\x0Cregeneration\0\x08\rdescriptionId\x11potion.absorption\x03\tamplifier\x06\x03\x08duration\xF0\x01\x03\x02id,\x05\x06chance\0\0\x80?\x08\x04name\nabsorption\0\x03\tamplifier\0\x05\x06chance\0\0\x80?\x03\x08duration\xD8\x04\x08\x04name\nresistance\x08\rdescriptionId\x11potion.resistance\x03\x02id\x16\0\x08\rdescriptionId\x15potion.fireResistance\x05\x06chance\0\0\x80?\x03\x08duration\xD8\x04\x08\x04name\x0Ffire_resistance\x03\x02id\x18\x03\tamplifier\0\0\x03\tnutrition\x08\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99?\x08\rcooldown_type\0\x08\x11using_converts_to\0\x03\ron_use_action\x01\0\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\0\0" } ;
pub const ENCHANTING_TABLE: Self = Self {
id: 116,
registry_key: "minecraft:enchanting_table",
@@ -72604,7 +72604,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GLOW_BERRIES : Self = Self { id : 879 , registry_key : "minecraft:glow_berries" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x04\x05\x13saturation_modifier\x9A\x99\x99>\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\rcooldown_type\0\x03\ron_use_action\x01\0\x03\x16minecraft:use_duration@\n\x0Eminecraft:seed\t\x08plant_at\x08\x04\ncave_vines\x1Ccave_vines_head_with_berries\x08\x0Bcrop_result\x14minecraft:cave_vines\x08\rplant_at_face\x04down\x01\x1Aplant_at_any_solid_surface\x01\0\0\0" } ;
pub const GLOW_BERRIES : Self = Self { id : 879 , registry_key : "minecraft:glow_berries" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x04\ncave_vines\x1Ccave_vines_head_with_berries\x08\x0Bcrop_result\x14minecraft:cave_vines\x08\rplant_at_face\x04down\x01\x1Aplant_at_any_solid_surface\x01\0\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x03\ron_use_action\x01\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99>\x03\tnutrition\x04\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\0\0\0" } ;
pub const GLOW_FRAME: Self = Self {
id: 636,
registry_key: "minecraft:glow_frame",
@@ -72703,7 +72703,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GOLDEN_APPLE : Self = Self { id : 258 , registry_key : "minecraft:golden_apple" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x99?\x08\rcooldown_type\0\x03\rcooldown_time\0\x03\ron_use_action\x01\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x08\t\x07effects\n\x04\x03\x08duration\n\x08\rdescriptionId\x13potion.regeneration\x05\x06chance\0\0\x80?\x03\tamplifier\x02\x08\x04name\x0Cregeneration\x03\x02id\x14\0\x08\rdescriptionId\x11potion.absorption\x03\x02id,\x05\x06chance\0\0\x80?\x03\x08duration\xF0\x01\x08\x04name\nabsorption\x03\tamplifier\0\0\0\0\0" } ;
pub const GOLDEN_APPLE : Self = Self { id : 258 , registry_key : "minecraft:golden_apple" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99?\x03\ron_use_action\x01\x08\rcooldown_type\0\t\x07effects\n\x04\x03\tamplifier\x02\x05\x06chance\0\0\x80?\x08\rdescriptionId\x13potion.regeneration\x03\x02id\x14\x03\x08duration\n\x08\x04name\x0Cregeneration\0\x08\x04name\nabsorption\x05\x06chance\0\0\x80?\x08\rdescriptionId\x11potion.absorption\x03\x08duration\xF0\x01\x03\x02id,\x03\tamplifier\0\0\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\x01\x03\tnutrition\x08\0\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\0\0" } ;
pub const GOLDEN_AXE: Self = Self {
id: 328,
registry_key: "minecraft:golden_axe",
@@ -72718,7 +72718,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GOLDEN_CARROT : Self = Self { id : 283 , registry_key : "minecraft:golden_carrot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\ron_use_action\x01\x05\x13saturation_modifier\x9A\x99\x99?\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\rcooldown_type\0\x08\x11using_converts_to\0\x03\tnutrition\x0C\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const GOLDEN_CARROT : Self = Self { id : 283 , registry_key : "minecraft:golden_carrot" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x99?\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x03\tnutrition\x0C\x03\ron_use_action\x01\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const GOLDEN_CHESTPLATE: Self = Self {
id: 355,
registry_key: "minecraft:golden_chestplate",
@@ -72789,7 +72789,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GOLDEN_SPEAR : Self = Self { id : 852 , registry_key : "minecraft:golden_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x17minecraft:use_modifiers\x05\x11movement_modifier\0\0\x80?\x01\x0Femit_vibrations\0\x08\x0Bstart_sound\x15item.golden_spear.use\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\0\t\titem_tags\x08\x04\x15minecraft:golden_tier\x12minecraft:is_spear\n\x16minecraft:display_name\x08\x05value\x16item.golden_spear.name\0\n\x16minecraft:swing_sounds\x08\x0Battack_miss\x1Ditem.golden_spear.attack_miss\x08\nattack_hit\x1Citem.golden_spear.attack_hit\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\x03\x0Emax_durability<\0\n\x10minecraft:damage\x02\x05value\x01\0\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\x16\0\n\x12minecraft:cooldown\x08\x08category\x05spear\x05\x08duration33s?\x08\x04type\x06attack\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x0Fdamage_modifier\0\0\0\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\x11damage_multiplier333?\x02\x05delay\x0E\0\n\x14knockback_conditions\x05\tmin_speed33\xA3@\x02\x0Cmax_duration\xAA\0\x05\x12min_relative_speed\0\0\0\0\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x13dismount_conditions\x02\x0Cmax_durationF\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed\0\0PA\0\n\x11damage_conditions\x02\x0Cmax_duration\x13\x01\x05\tmin_speed\0\0\0\0\x05\x12min_relative_speed33\x93@\0\x05\rhitbox_margin\0\0\x80>\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x16minecraft:golden_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x14minecraft:gold_ingot\0\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:golden_tier\x12minecraft:is_spear\0\n\x18minecraft:swing_duration\x05\x05value33s?\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\x05\rhitbox_margin\0\0\x80>\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x0Fitem_properties\x05\x0Cmining_speed\0\0\x80?\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x0Bmelee_spear\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\ngold_spear\0\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x0Eliquid_clipped\0\x01\rhand_equipped\x01\x03\ruse_animation\0\x01\x0Eshould_despawn\x01\x03\x06damage\x02\x01\x12hidden_in_commands\x02\x03\x11enchantable_value,\x03\x0Emax_stack_size\x02\x01\x0Eallow_off_hand\0\x01\x04foil\0\x03\x0Bframe_count\x02\0\0\0" } ;
pub const GOLDEN_SPEAR : Self = Self { id : 852 , registry_key : "minecraft:golden_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\x08\x04\x15minecraft:golden_tier\x12minecraft:is_spear\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x16minecraft:golden_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\t\x05items\n\x02\x08\x04name\x14minecraft:gold_ingot\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Citem.golden_spear.attack_hit\x08\x0Battack_miss\x1Ditem.golden_spear.attack_miss\0\n\x16minecraft:display_name\x08\x05value\x16item.golden_spear.name\0\n\x15minecraft:enchantable\x01\x05value\x16\x08\x04slot\x0Bmelee_spear\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x10minecraft:damage\x02\x05value\x01\0\0\n\x18minecraft:swing_duration\x05\x05value33s?\0\n\x12minecraft:cooldown\x08\x04type\x06attack\x05\x08duration33s?\x08\x08category\x05spear\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x14minecraft:durability\x03\x0Emax_durability<\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:golden_tier\x12minecraft:is_spear\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x11damage_multiplier333?\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\x05\x0Fdamage_modifier\0\0\0\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x02\x05delay\x0E\0\n\x13dismount_conditions\x05\tmin_speed\0\0PA\x02\x0Cmax_durationF\0\x05\x12min_relative_speed\0\0\0\0\0\n\x11damage_conditions\x02\x0Cmax_duration\x13\x01\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\0\n\x14knockback_conditions\x02\x0Cmax_duration\xAA\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\0\x05\rhitbox_margin\0\0\x80>\0\0\n\x0Fitem_properties\x01\x0Eliquid_clipped\0\x03\x06damage\x02\x03\x11enchantable_value,\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x03\x0Bframe_count\x02\x08\x10enchantable_slot\x0Bmelee_spear\x01\x12hidden_in_commands\x02\x03\x0Emax_stack_size\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\ngold_spear\0\0\x03\x11creative_category\x06\x01\x0Eshould_despawn\x01\x01\rhand_equipped\x01\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x17can_destroy_in_creative\x01\x03\ruse_animation\0\x08\x0Ecreative_group\0\x01\x04foil\0\0\n\x17minecraft:use_modifiers\x01\x0Femit_vibrations\0\x05\x11movement_modifier\0\0\x80?\x05\x0Cuse_duration\0\xA0\x8CG\x08\x0Bstart_using\x06always\x08\x0Bstart_sound\x15item.golden_spear.use\0\0\0" } ;
pub const GOLDEN_SWORD: Self = Self {
id: 325,
registry_key: "minecraft:golden_sword",
@@ -72853,7 +72853,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GRAY_BUNDLE : Self = Self { id : 862 , registry_key : "minecraft:gray_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\0\x01\x12hidden_in_commands\x02\x01\x04foil\0\x08\x10enchantable_slot\x04none\x03\x0Bframe_count\x02\x01\rhand_equipped\0\x01\x0Eliquid_clipped\0\x03\ruse_animation\0\x03\x0Cuse_duration\0\x03\x0Emax_stack_size\x02\x03\x11creative_category\x06\x03\x11enchantable_value\0\x08\x0Ecreative_group\0\x01\x0Eshould_despawn\x01\x01\x0Eallow_off_hand\0\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bbundle_gray\x08\x10bundle_open_back\x15bundle_gray_open_back\x08\x11bundle_open_front\x16bundle_gray_open_front\0\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\t\titem_tags\0\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\t\rallowed_items\0\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const GRAY_BUNDLE : Self = Self { id : 862 , registry_key : "minecraft:gray_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\0\n\x0Fitem_properties\x03\x06damage\0\x01\x04foil\0\x01\x0Eallow_off_hand\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bbundle_gray\x08\x11bundle_open_front\x16bundle_gray_open_front\x08\x10bundle_open_back\x15bundle_gray_open_back\0\0\x01\x0Eliquid_clipped\0\x03\x11creative_category\x06\x01\rhand_equipped\0\x08\x10enchantable_slot\x04none\x03\x11enchantable_value\0\x01\x0Eshould_despawn\x01\x08\x0Ecreative_group\0\x03\x0Cuse_duration\0\x01\x17can_destroy_in_creative\x01\x03\x0Bframe_count\x02\x03\x0Emax_stack_size\x02\x05\x0Cmining_speed\0\0\x80?\x01\x12hidden_in_commands\x02\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const GRAY_CANDLE: Self = Self {
id: -420,
registry_key: "minecraft:gray_candle",
@@ -72952,7 +72952,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const GREEN_BUNDLE : Self = Self { id : 863 , registry_key : "minecraft:green_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\t\titem_tags\0\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n\x0Fitem_properties\x03\x0Cuse_duration\0\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x03\x0Bframe_count\x02\x03\ruse_animation\0\x01\x04foil\0\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x17bundle_green_open_front\x08\x07default\x0Cbundle_green\x08\x10bundle_open_back\x16bundle_green_open_back\0\0\x03\x11enchantable_value\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x03\x06damage\0\x01\x0Eliquid_clipped\0\x01\x0Eallow_off_hand\0\x03\x11creative_category\x06\x01\rhand_equipped\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x01\x12hidden_in_commands\x02\x05\x0Cmining_speed\0\0\x80?\0\0\0" } ;
pub const GREEN_BUNDLE : Self = Self { id : 863 , registry_key : "minecraft:green_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Cbundle_green\x08\x10bundle_open_back\x16bundle_green_open_back\x08\x11bundle_open_front\x17bundle_green_open_front\0\0\x03\x06damage\0\x03\x11enchantable_value\0\x05\x0Cmining_speed\0\0\x80?\x03\x0Cuse_duration\0\x01\rhand_equipped\0\x01\x0Fstacked_by_data\0\x01\x04foil\0\x01\x17can_destroy_in_creative\x01\x01\x0Eallow_off_hand\0\x01\x12hidden_in_commands\x02\x01\x0Eshould_despawn\x01\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x03\ruse_animation\0\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x08\x10enchantable_slot\x04none\x03\x0Bframe_count\x02\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\t\titem_tags\0\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const GREEN_CANDLE: Self = Self {
id: -426,
registry_key: "minecraft:green_candle",
@@ -73415,7 +73415,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const HONEY_BOTTLE : Self = Self { id : 604 , registry_key : "minecraft:honey_bottle" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_durationP\n\x0Eminecraft:food\x08\x11using_converts_to\x0Cglass_bottle\t\x0Eremove_effects\x03\x02&\x05\x13saturation_modifier\xCD\xCC\xCC=\x01\x0Ecan_always_eat\x01\x08\rcooldown_type\0\x03\rcooldown_time\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x0C\0\x03\x18minecraft:max_stack_size \0\0" } ;
pub const HONEY_BOTTLE : Self = Self { id : 604 , registry_key : "minecraft:honey_bottle" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\x01\x08\x11using_converts_to\x0Cglass_bottle\x05\x13saturation_modifier\xCD\xCC\xCC=\t\x0Eremove_effects\x03\x02&\x03\tnutrition\x0C\x03\rcooldown_time\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\0\x03\x18minecraft:max_stack_size \x03\x16minecraft:use_durationP\0\0" } ;
pub const HONEYCOMB: Self = Self {
id: 603,
registry_key: "minecraft:honeycomb",
@@ -73717,7 +73717,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const IRON_SPEAR : Self = Self { id : 853 , registry_key : "minecraft:iron_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x18minecraft:swing_duration\x05\x05value33s?\0\n\x16minecraft:display_name\x08\x05value\x14item.iron_spear.name\0\n\x10minecraft:damage\x02\x05value\x03\0\0\n\x14minecraft:durability\x03\x0Emax_durability\xF4\x03\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\0\n\x15minecraft:enchantable\x01\x05value\x0E\x08\x04slot\x0Bmelee_spear\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Aitem.iron_spear.attack_hit\x08\x0Battack_miss\x1Bitem.iron_spear.attack_miss\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x0Fdamage_modifier\0\0\0\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xE1\0\0\n\x13dismount_conditions\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed\0\x000A\x02\x0Cmax_duration2\0\0\x05\x11damage_multiplier33s?\n\x14knockback_conditions\x02\x0Cmax_duration\x87\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x02\x05delay\x0C\0\x05\rhitbox_margin\0\0\x80>\0\0\t\titem_tags\x08\x04\x13minecraft:iron_tier\x12minecraft:is_spear\n\x0Fitem_properties\x03\x0Bframe_count\x02\x03\x06damage\x06\x01\rhand_equipped\x01\x03\x0Cuse_duration\x80\xE4\xAF\x01\x03\x11enchantable_value\x1C\x01\x17can_destroy_in_creative\x01\x08\x10enchantable_slot\x0Bmelee_spear\x01\x12hidden_in_commands\x02\x03\x0Emax_stack_size\x02\x05\x0Cmining_speed\0\0\x80?\x03\ruse_animation\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\niron_spear\0\0\x08\x0Ecreative_group\0\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x01\x0Eallow_off_hand\0\x01\x04foil\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x14minecraft:iron_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x14minecraft:iron_ingot\0\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x13minecraft:iron_tier\x12minecraft:is_spear\0\n\x17minecraft:use_modifiers\x08\x0Bstart_sound\x13item.iron_spear.use\x05\x11movement_modifier\0\0\x80?\x05\x0Cuse_duration\0\xA0\x8CG\x08\x0Bstart_using\x06always\x01\x0Femit_vibrations\0\0\n\x12minecraft:cooldown\x05\x08duration33s?\x08\x08category\x05spear\x08\x04type\x06attack\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const IRON_SPEAR : Self = Self { id : 853 , registry_key : "minecraft:iron_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x14minecraft:iron_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\t\x05items\n\x02\x08\x04name\x14minecraft:iron_ingot\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x13minecraft:iron_tier\x12minecraft:is_spear\0\n\x17minecraft:use_modifiers\x05\x11movement_modifier\0\0\x80?\x08\x0Bstart_using\x06always\x08\x0Bstart_sound\x13item.iron_spear.use\x05\x0Cuse_duration\0\xA0\x8CG\x01\x0Femit_vibrations\0\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x03\x11creative_category\x06\x05\x0Cmining_speed\0\0\x80?\x01\rhand_equipped\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\niron_spear\0\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x08\x10enchantable_slot\x0Bmelee_spear\x03\x11enchantable_value\x1C\x01\x12hidden_in_commands\x02\x03\x06damage\x06\x03\x0Bframe_count\x02\x01\x0Eshould_despawn\x01\x01\x0Eallow_off_hand\0\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x01\x17can_destroy_in_creative\x01\x01\x04foil\0\0\n\x19minecraft:piercing_weapon\x05\rhitbox_margin\0\0\x80>\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\0\n\x15minecraft:enchantable\x01\x05value\x0E\x08\x04slot\x0Bmelee_spear\0\n\x14minecraft:durability\x03\x0Emax_durability\xF4\x03\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Aitem.iron_spear.attack_hit\x08\x0Battack_miss\x1Bitem.iron_spear.attack_miss\0\t\titem_tags\x08\x04\x13minecraft:iron_tier\x12minecraft:is_spear\n\x10minecraft:damage\x02\x05value\x03\0\0\n\x12minecraft:cooldown\x08\x04type\x06attack\x08\x08category\x05spear\x05\x08duration33s?\0\n\x16minecraft:display_name\x08\x05value\x14item.iron_spear.name\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x13dismount_conditions\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed\0\x000A\x02\x0Cmax_duration2\0\0\x02\x05delay\x0C\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x14knockback_conditions\x02\x0Cmax_duration\x87\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xE1\0\0\x05\x0Fdamage_modifier\0\0\0\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\x05\x11damage_multiplier33s?\x05\rhitbox_margin\0\0\x80>\0\0\n\x18minecraft:swing_duration\x05\x05value33s?\0\0\0" } ;
pub const IRON_SWORD: Self = Self {
id: 309,
registry_key: "minecraft:iron_sword",
@@ -74299,7 +74299,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const LIGHT_BLUE_BUNDLE : Self = Self { id : 864 , registry_key : "minecraft:light_blue_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x05\x0Cmining_speed\0\0\x80?\x08\x10enchantable_slot\x04none\x01\x12hidden_in_commands\x02\x03\x11enchantable_value\0\x01\x0Eallow_off_hand\0\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x01\rhand_equipped\0\x03\x0Cuse_duration\0\x01\x17can_destroy_in_creative\x01\x01\x04foil\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x1Bbundle_light_blue_open_back\x08\x07default\x11bundle_light_blue\x08\x11bundle_open_front\x1Cbundle_light_blue_open_front\0\0\x03\ruse_animation\0\x01\x0Fstacked_by_data\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x03\x06damage\0\x01\x0Eshould_despawn\x01\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const LIGHT_BLUE_BUNDLE : Self = Self { id : 864 , registry_key : "minecraft:light_blue_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x03\x06damage\0\x01\x0Fstacked_by_data\0\x01\x0Eshould_despawn\x01\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x03\ruse_animation\0\x01\rhand_equipped\0\x03\x11enchantable_value\0\x01\x04foil\0\x05\x0Cmining_speed\0\0\x80?\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x01\x0Eallow_off_hand\0\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x1Cbundle_light_blue_open_front\x08\x07default\x11bundle_light_blue\x08\x10bundle_open_back\x1Bbundle_light_blue_open_back\0\0\x01\x17can_destroy_in_creative\x01\x03\x0Cuse_duration\0\x03\x0Bframe_count\x02\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\t\titem_tags\0\0\0\0" } ;
pub const LIGHT_BLUE_CANDLE: Self = Self {
id: -416,
registry_key: "minecraft:light_blue_candle",
@@ -74398,7 +74398,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const LIGHT_GRAY_BUNDLE : Self = Self { id : 865 , registry_key : "minecraft:light_gray_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x0Fitem_properties\x01\x17can_destroy_in_creative\x01\x03\x11enchantable_value\0\x03\x0Emax_stack_size\x02\x05\x0Cmining_speed\0\0\x80?\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x01\x0Eallow_off_hand\0\x03\x0Cuse_duration\0\x03\x06damage\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x1Bbundle_light_gray_open_back\x08\x11bundle_open_front\x1Cbundle_light_gray_open_front\x08\x07default\x11bundle_light_gray\0\0\x01\rhand_equipped\0\x08\x10enchantable_slot\x04none\x03\x0Bframe_count\x02\x01\x04foil\0\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x01\x0Eshould_despawn\x01\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\0\t\titem_tags\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const LIGHT_GRAY_BUNDLE : Self = Self { id : 865 , registry_key : "minecraft:light_gray_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x01\x04foil\0\x08\x0Ecreative_group\0\x01\x17can_destroy_in_creative\x01\x08\x10enchantable_slot\x04none\x03\x06damage\0\x01\x12hidden_in_commands\x02\x03\x0Bframe_count\x02\x01\x0Eshould_despawn\x01\x03\ruse_animation\0\x03\x0Cuse_duration\0\x03\x0Emax_stack_size\x02\x01\x0Fstacked_by_data\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x01\rhand_equipped\0\x03\x11enchantable_value\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x11bundle_light_gray\x08\x11bundle_open_front\x1Cbundle_light_gray_open_front\x08\x10bundle_open_back\x1Bbundle_light_gray_open_back\0\0\x01\x0Eallow_off_hand\0\x05\x0Cmining_speed\0\0\x80?\0\0\0" } ;
pub const LIGHT_GRAY_CANDLE: Self = Self {
id: -421,
registry_key: "minecraft:light_gray_candle",
@@ -74518,7 +74518,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const LIME_BUNDLE : Self = Self { id : 866 , registry_key : "minecraft:lime_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x03\x11enchantable_value\0\x03\x0Emax_stack_size\x02\x03\ruse_animation\0\x01\x12hidden_in_commands\x02\x03\x06damage\0\x01\x04foil\0\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x16bundle_lime_open_front\x08\x10bundle_open_back\x15bundle_lime_open_back\x08\x07default\x0Bbundle_lime\0\0\x01\x0Eallow_off_hand\0\x03\x11creative_category\x06\x01\x0Eliquid_clipped\0\x01\rhand_equipped\0\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\0\0\t\titem_tags\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const LIME_BUNDLE : Self = Self { id : 866 , registry_key : "minecraft:lime_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x01\x0Eliquid_clipped\0\x03\ruse_animation\0\x03\x11enchantable_value\0\x03\x06damage\0\x03\x11creative_category\x06\x01\x04foil\0\x01\rhand_equipped\0\x03\x0Emax_stack_size\x02\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x01\x12hidden_in_commands\x02\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x16bundle_lime_open_front\x08\x10bundle_open_back\x15bundle_lime_open_back\x08\x07default\x0Bbundle_lime\0\0\x03\x0Cuse_duration\0\x03\x0Bframe_count\x02\x01\x17can_destroy_in_creative\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\t\titem_tags\0\0\0\0" } ;
pub const LIME_CANDLE: Self = Self {
id: -418,
registry_key: "minecraft:lime_candle",
@@ -74722,7 +74722,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const MAGENTA_BUNDLE : Self = Self { id : 867 , registry_key : "minecraft:magenta_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x01\x0Eallow_off_hand\0\x03\x0Bframe_count\x02\x03\x11creative_category\x06\x01\x04foil\0\x01\x12hidden_in_commands\x02\x01\rhand_equipped\0\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x08\x0Ecreative_group\0\x03\x0Cuse_duration\0\x05\x0Cmining_speed\0\0\x80?\x01\x17can_destroy_in_creative\x01\x08\x10enchantable_slot\x04none\x03\ruse_animation\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x18bundle_magenta_open_back\x08\x11bundle_open_front\x19bundle_magenta_open_front\x08\x07default\x0Ebundle_magenta\0\0\x03\x06damage\0\x01\x0Fstacked_by_data\0\x01\x0Eshould_despawn\x01\x03\x11enchantable_value\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\t\titem_tags\0\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const MAGENTA_BUNDLE : Self = Self { id : 867 , registry_key : "minecraft:magenta_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x01\x0Eliquid_clipped\0\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\0\x01\x0Eallow_off_hand\0\x03\x0Cuse_duration\0\x03\x11enchantable_value\0\x01\x04foil\0\x01\rhand_equipped\0\x03\x0Emax_stack_size\x02\x01\x0Eshould_despawn\x01\x08\x10enchantable_slot\x04none\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x19bundle_magenta_open_front\x08\x07default\x0Ebundle_magenta\x08\x10bundle_open_back\x18bundle_magenta_open_back\0\0\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x01\x12hidden_in_commands\x02\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\t\titem_tags\0\0\0\0" } ;
pub const MAGENTA_CANDLE: Self = Self {
id: -415,
registry_key: "minecraft:magenta_candle",
@@ -75024,8 +75024,8 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const MELON_SEEDS : Self = Self { id : 293 , registry_key : "minecraft:melon_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\x12minecraft:farmland\x01\x1Aplant_at_any_solid_surface\0\x08\rplant_at_face\x02up\x08\x0Bcrop_result\x14minecraft:melon_stem\0\0\0" } ;
pub const MELON_SLICE : Self = Self { id : 272 , registry_key : "minecraft:melon_slice" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x03\rcooldown_time\0\x03\tnutrition\x04\x05\x13saturation_modifier\x9A\x99\x99>\x08\x11using_converts_to\0\0\0\0" } ;
pub const MELON_SEEDS : Self = Self { id : 293 , registry_key : "minecraft:melon_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x14minecraft:melon_stem\0\0\0" } ;
pub const MELON_SLICE : Self = Self { id : 272 , registry_key : "minecraft:melon_slice" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\x03\tnutrition\x04\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x05\x13saturation_modifier\x9A\x99\x99>\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const MELON_STEM: Self = Self {
id: 105,
registry_key: "minecraft:melon_stem",
@@ -75243,7 +75243,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const MUSHROOM_STEW : Self = Self { id : 260 , registry_key : "minecraft:mushroom_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x19?\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\rcooldown_type\0\x03\tnutrition\x0C\x08\x11using_converts_to\x04bowl\0\x03\x18minecraft:max_stack_size\x02\0\0" } ;
pub const MUSHROOM_STEW : Self = Self { id : 260 , registry_key : "minecraft:mushroom_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x03\tnutrition\x0C\x08\x11using_converts_to\x04bowl\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\rcooldown_time\0\0\x03\x18minecraft:max_stack_size\x02\0\0" } ;
pub const MUSIC_DISC_11: Self = Self {
id: 554,
registry_key: "minecraft:music_disc_11",
@@ -75398,7 +75398,7 @@ impl BedrockItem {
component_based: true,
definition_components: b"\n\0\0",
};
pub const MUTTON : Self = Self { id : 560 , registry_key : "minecraft:mutton" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\tnutrition\x04\x03\rcooldown_time\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x99>\x08\rcooldown_type\0\x08\x11using_converts_to\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const MUTTON : Self = Self { id : 560 , registry_key : "minecraft:mutton" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\tnutrition\x04\x05\x13saturation_modifier\x9A\x99\x99>\x08\rcooldown_type\0\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x03\ron_use_action\x01\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const MYCELIUM: Self = Self {
id: 110,
registry_key: "minecraft:mycelium",
@@ -75490,7 +75490,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const NETHER_WART : Self = Self { id : 294 , registry_key : "minecraft:nether_wart" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\rplant_at_face\x02up\x08\x0Bcrop_result\x15minecraft:nether_wart\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\tsoul_sand\0\0\0" } ;
pub const NETHER_WART : Self = Self { id : 294 , registry_key : "minecraft:nether_wart" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\tsoul_sand\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x15minecraft:nether_wart\0\0\0" } ;
pub const NETHER_WART_BLOCK: Self = Self {
id: 214,
registry_key: "minecraft:nether_wart_block",
@@ -75596,7 +75596,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const NETHERITE_SPEAR : Self = Self { id : 854 , registry_key : "minecraft:netherite_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x11damage_multiplier\x9A\x99\x99?\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\rhitbox_margin\0\0\x80>\x02\x05delay\x08\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xAF\0\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\n\x13dismount_conditions\x05\tmin_speed\0\0\x10A\x02\x0Cmax_duration2\0\x05\x12min_relative_speed\0\0\0\0\0\n\x14knockback_conditions\x02\x0Cmax_durationn\0\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\0\x05\x0Fdamage_modifier\0\0\0\0\0\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x19minecraft:netherite_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x19minecraft:netherite_ingot\0\0\0\n\x0Fitem_properties\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\x1E\x05\x0Cmining_speed\0\0\x80?\x01\rhand_equipped\x01\x03\x06damage\n\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Fnetherite_spear\0\0\x03\x0Emax_stack_size\x02\x03\ruse_animation\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x01\x04foil\0\x08\x10enchantable_slot\x0Bmelee_spear\x01\x0Eshould_despawn\x01\x01\x0Eallow_off_hand\0\x01\x0Fstacked_by_data\0\x01\x12hidden_in_commands\x02\x03\x11creative_category\x06\0\n\x18minecraft:fire_resistant\x01\x05value\x01\0\n\x12minecraft:cooldown\x08\x08category\x05spear\x05\x08duration33\x93?\x08\x04type\x06attack\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Fitem.netherite_spear.attack_hit\x08\x0Battack_miss item.netherite_spear.attack_miss\0\t\titem_tags\x08\x04\x18minecraft:netherite_tier\x12minecraft:is_spear\n\x10minecraft:damage\x02\x05value\x05\0\0\n\x17minecraft:use_modifiers\x05\x11movement_modifier\0\0\x80?\x01\x0Femit_vibrations\0\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\x08\x0Bstart_sound\x18item.netherite_spear.use\0\n\x19minecraft:piercing_weapon\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\x05\rhitbox_margin\0\0\x80>\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\x0F\0\n\x16minecraft:display_name\x08\x05value\x19item.netherite_spear.name\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x18minecraft:swing_duration\x05\x05value33\x93?\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x18minecraft:netherite_tier\x12minecraft:is_spear\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\x03\x0Emax_durability\xDC\x1F\0\0\0" } ;
pub const NETHERITE_SPEAR : Self = Self { id : 854 , registry_key : "minecraft:netherite_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x15minecraft:enchantable\x01\x05value\x0F\x08\x04slot\x0Bmelee_spear\0\n\x18minecraft:fire_resistant\x01\x05value\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x0Fitem_properties\x01\x0Fstacked_by_data\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Fnetherite_spear\0\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\n\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x03\x0Emax_stack_size\x02\x03\ruse_animation\0\x01\x04foil\0\x01\x0Eliquid_clipped\0\x01\x0Eallow_off_hand\0\x08\x10enchantable_slot\x0Bmelee_spear\x01\rhand_equipped\x01\x08\x0Ecreative_group\0\x03\x11enchantable_value\x1E\x03\x0Bframe_count\x02\x01\x17can_destroy_in_creative\x01\x01\x0Eshould_despawn\x01\0\n\x12minecraft:cooldown\x08\x04type\x06attack\x08\x08category\x05spear\x05\x08duration33\x93?\0\n\x19minecraft:piercing_weapon\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\0\n\x10minecraft:damage\x02\x05value\x05\0\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x16minecraft:display_name\x08\x05value\x19item.netherite_spear.name\0\n\x17minecraft:use_modifiers\x08\x0Bstart_using\x06always\x05\x11movement_modifier\0\0\x80?\x05\x0Cuse_duration\0\xA0\x8CG\x01\x0Femit_vibrations\0\x08\x0Bstart_sound\x18item.netherite_spear.use\0\t\titem_tags\x08\x04\x18minecraft:netherite_tier\x12minecraft:is_spear\n\x16minecraft:swing_sounds\x08\x0Battack_miss item.netherite_spear.attack_miss\x08\nattack_hit\x1Fitem.netherite_spear.attack_hit\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x13dismount_conditions\x05\x12min_relative_speed\0\0\0\0\x02\x0Cmax_duration2\0\x05\tmin_speed\0\0\x10A\0\n\x14knockback_conditions\x05\tmin_speed33\xA3@\x02\x0Cmax_durationn\0\x05\x12min_relative_speed\0\0\0\0\0\x05\rhitbox_margin\0\0\x80>\x05\x11damage_multiplier\x9A\x99\x99?\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\xAF\0\0\x05\x0Fdamage_modifier\0\0\0\0\x02\x05delay\x08\0\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\x08\rrepair_amount)context.other->query.remaining_durability\t\x05items\n\x02\x08\x04name\x19minecraft:netherite_spear\0\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04name\x19minecraft:netherite_ingot\0\0\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x18minecraft:netherite_tier\x12minecraft:is_spear\0\n\x18minecraft:swing_duration\x05\x05value33\x93?\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\x03\x0Emax_durability\xDC\x1F\0\0\0" } ;
pub const NETHERITE_SWORD: Self = Self {
id: 617,
registry_key: "minecraft:netherite_sword",
@@ -75793,7 +75793,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const OMINOUS_TRIAL_KEY : Self = Self { id : 875 , registry_key : "minecraft:ominous_trial_key" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x1Bitem.ominous_trial_key.name\0\n\x0Fitem_properties\x03\x11enchantable_value\0\x01\rhand_equipped\0\x01\x0Eshould_despawn\x01\x01\x0Eallow_off_hand\0\x05\x0Cmining_speed\0\0\x80?\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x11ominous_trial_key\0\0\x01\x0Fstacked_by_data\0\x03\x11creative_category\x08\x01\x0Eliquid_clipped\0\x01\x12hidden_in_commands\x02\x08\x0Ecreative_group\0\x03\x0Emax_stack_size\x80\x01\x08\x10enchantable_slot\x04none\x03\x0Bframe_count\x02\x01\x17can_destroy_in_creative\x01\x03\x06damage\0\x03\x0Cuse_duration\0\x01\x04foil\0\x03\ruse_animation\0\0\t\titem_tags\0\0\0\0" } ;
pub const OMINOUS_TRIAL_KEY : Self = Self { id : 875 , registry_key : "minecraft:ominous_trial_key" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n\x0Fitem_properties\x01\x0Eshould_despawn\x01\x03\ruse_animation\0\x08\x10enchantable_slot\x04none\x01\rhand_equipped\0\x03\x0Emax_stack_size\x80\x01\x01\x12hidden_in_commands\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x11ominous_trial_key\0\0\x01\x04foil\0\x01\x0Eliquid_clipped\0\x03\x11creative_category\x08\x03\x11enchantable_value\0\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\0\x01\x0Eallow_off_hand\0\x03\x06damage\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\0\n\x16minecraft:display_name\x08\x05value\x1Bitem.ominous_trial_key.name\0\0\0" } ;
pub const OPEN_EYEBLOSSOM: Self = Self {
id: -1018,
registry_key: "minecraft:open_eyeblossom",
@@ -75801,7 +75801,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const ORANGE_BUNDLE : Self = Self { id : 868 , registry_key : "minecraft:orange_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\0\x01\x0Fstacked_by_data\0\x01\rhand_equipped\0\x01\x0Eshould_despawn\x01\x03\x0Bframe_count\x02\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\0\x01\x04foil\0\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x03\x11creative_category\x06\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x18bundle_orange_open_front\x08\x07default\rbundle_orange\x08\x10bundle_open_back\x17bundle_orange_open_back\0\0\x01\x0Eallow_off_hand\0\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const ORANGE_BUNDLE : Self = Self { id : 868 , registry_key : "minecraft:orange_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\0\t\titem_tags\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\x03\x0Bframe_count\x02\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x01\x0Eshould_despawn\x01\x08\x10enchantable_slot\x04none\x01\x04foil\0\x01\rhand_equipped\0\x03\x06damage\0\x03\x0Cuse_duration\0\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x03\x11enchantable_value\0\x03\x0Emax_stack_size\x02\x01\x0Eallow_off_hand\0\x05\x0Cmining_speed\0\0\x80?\x01\x0Fstacked_by_data\0\x08\x0Ecreative_group\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x17bundle_orange_open_back\x08\x11bundle_open_front\x18bundle_orange_open_front\x08\x07default\rbundle_orange\0\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const ORANGE_CANDLE: Self = Self {
id: -414,
registry_key: "minecraft:orange_candle",
@@ -76334,7 +76334,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PINK_BUNDLE : Self = Self { id : 869 , registry_key : "minecraft:pink_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x03\x11enchantable_value\0\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x03\x11creative_category\x06\x01\x17can_destroy_in_creative\x01\x03\ruse_animation\0\x01\rhand_equipped\0\x01\x0Fstacked_by_data\0\x01\x12hidden_in_commands\x02\x08\x10enchantable_slot\x04none\n\x0Eminecraft:icon\n\x08textures\x08\x11bundle_open_front\x16bundle_pink_open_front\x08\x07default\x0Bbundle_pink\x08\x10bundle_open_back\x15bundle_pink_open_back\0\0\x01\x0Eshould_despawn\x01\x01\x04foil\0\x08\x0Ecreative_group\0\x03\x06damage\0\x03\x0Cuse_duration\0\x03\x0Bframe_count\x02\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const PINK_BUNDLE : Self = Self { id : 869 , registry_key : "minecraft:pink_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\0\n\x0Fitem_properties\x05\x0Cmining_speed\0\0\x80?\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x01\x0Eliquid_clipped\0\x03\x06damage\0\x01\x0Fstacked_by_data\0\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x01\x0Eshould_despawn\x01\x03\x0Cuse_duration\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x15bundle_pink_open_back\x08\x11bundle_open_front\x16bundle_pink_open_front\x08\x07default\x0Bbundle_pink\0\0\x01\x17can_destroy_in_creative\x01\x01\x12hidden_in_commands\x02\x03\ruse_animation\0\x01\rhand_equipped\0\x01\x04foil\0\0\t\titem_tags\0\0\0\0" } ;
pub const PINK_CANDLE: Self = Self {
id: -419,
registry_key: "minecraft:pink_candle",
@@ -76475,7 +76475,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PITCHER_POD : Self = Self { id : 297 , registry_key : "minecraft:pitcher_pod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x16minecraft:pitcher_crop\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\0\0\0" } ;
pub const PITCHER_POD : Self = Self { id : 297 , registry_key : "minecraft:pitcher_pod" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\rplant_at_face\x02up\t\x08plant_at\x08\x02\x12minecraft:farmland\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x16minecraft:pitcher_crop\0\0\0" } ;
pub const PLANKS: Self = Self {
id: 814,
registry_key: "minecraft:planks",
@@ -76511,7 +76511,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const POISONOUS_POTATO : Self = Self { id : 282 , registry_key : "minecraft:poisonous_potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x08\x11using_converts_to\0\t\x07effects\n\x02\x08\x04name\x06poison\x08\rdescriptionId\rpotion.poison\x03\tamplifier\0\x05\x06chance\x9A\x99\x19?\x03\x08duration\n\x03\x02id&\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\rcooldown_type\0\x03\rcooldown_time\0\x03\tnutrition\x04\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x05\x13saturation_modifier\x9A\x99\x99>\0\0\0" } ;
pub const POISONOUS_POTATO : Self = Self { id : 282 , registry_key : "minecraft:poisonous_potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\x03\ron_use_action\x01\x05\x13saturation_modifier\x9A\x99\x99>\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\t\x07effects\n\x02\x08\rdescriptionId\rpotion.poison\x03\tamplifier\0\x03\x08duration\n\x03\x02id&\x08\x04name\x06poison\x05\x06chance\x9A\x99\x19?\0\x03\tnutrition\x04\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const POLAR_BEAR_SPAWN_EGG: Self = Self {
id: 477,
registry_key: "minecraft:polar_bear_spawn_egg",
@@ -76988,7 +76988,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PORKCHOP : Self = Self { id : 262 , registry_key : "minecraft:porkchop" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\x11using_converts_to\0\x05\x13saturation_modifier\x9A\x99\x99>\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x03\ron_use_action\x01\x03\tnutrition\x06\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const PORKCHOP : Self = Self { id : 262 , registry_key : "minecraft:porkchop" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\x9A\x99\x99>\x03\ron_use_action\x01\x03\rcooldown_time\0\x08\x11using_converts_to\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x06\x08\rcooldown_type\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const PORTAL: Self = Self {
id: 90,
registry_key: "minecraft:portal",
@@ -76996,7 +76996,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const POTATO : Self = Self { id : 280 , registry_key : "minecraft:potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\x12minecraft:farmland\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x12minecraft:potatoes\x08\rplant_at_face\x02up\0\n\x0Eminecraft:food\x08\rcooldown_type\0\x03\ron_use_action\x01\x03\tnutrition\x02\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99>\0\0\0" } ;
pub const POTATO : Self = Self { id : 280 , registry_key : "minecraft:potato" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x03\ron_use_action\x01\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\x9A\x99\x99>\x03\tnutrition\x02\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\n\x0Eminecraft:seed\x08\x0Bcrop_result\x12minecraft:potatoes\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\x12minecraft:farmland\0\0\0" } ;
pub const POTATOES: Self = Self {
id: 142,
registry_key: "minecraft:potatoes",
@@ -77130,7 +77130,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PUFFERFISH : Self = Self { id : 267 , registry_key : "minecraft:pufferfish" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x05\x13saturation_modifier\xCD\xCC\xCC=\t\x07effects\n\x06\x08\x04name\x06poison\x03\tamplifier\x02\x05\x06chance\0\0\x80?\x03\x08durationx\x08\rdescriptionId\rpotion.poison\x03\x02id&\0\x05\x06chance\0\0\x80?\x08\rdescriptionId\x10potion.confusion\x08\x04name\x06nausea\x03\tamplifier\0\x03\x08duration\x1E\x03\x02id\x12\0\x08\x04name\x06hunger\x03\x02id\"\x08\rdescriptionId\rpotion.hunger\x03\tamplifier\x04\x05\x06chance\0\0\x80?\x03\x08duration\x1E\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x08\rcooldown_type\0\x03\ron_use_action\x01\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x03\tnutrition\x02\0\x03\x16minecraft:use_duration@\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const PUFFERFISH : Self = Self { id : 267 , registry_key : "minecraft:pufferfish" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x07effects\n\x06\x08\x04name\x06poison\x03\tamplifier\x02\x03\x08durationx\x05\x06chance\0\0\x80?\x03\x02id&\x08\rdescriptionId\rpotion.poison\0\x03\x08duration\x1E\x03\x02id\x12\x05\x06chance\0\0\x80?\x08\x04name\x06nausea\x03\tamplifier\0\x08\rdescriptionId\x10potion.confusion\0\x08\rdescriptionId\rpotion.hunger\x03\tamplifier\x04\x05\x06chance\0\0\x80?\x03\x08duration\x1E\x08\x04name\x06hunger\x03\x02id\"\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x03\tnutrition\x02\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCC\xCC=\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\0\x03\x16minecraft:use_duration@\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const PUFFERFISH_BUCKET: Self = Self {
id: 370,
registry_key: "minecraft:pufferfish_bucket",
@@ -77152,8 +77152,8 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PUMPKIN_PIE : Self = Self { id : 284 , registry_key : "minecraft:pumpkin_pie" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\x10\x05\x13saturation_modifier\x9A\x99\x99>\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const PUMPKIN_SEEDS : Self = Self { id : 292 , registry_key : "minecraft:pumpkin_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\rplant_at_face\x02up\x08\x0Bcrop_result\x16minecraft:pumpkin_stem\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\x12minecraft:farmland\0\0\0" } ;
pub const PUMPKIN_PIE : Self = Self { id : 284 , registry_key : "minecraft:pumpkin_pie" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\tnutrition\x10\x05\x13saturation_modifier\x9A\x99\x99>\x03\rcooldown_time\0\x08\x11using_converts_to\0\x03\ron_use_action\x01\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const PUMPKIN_SEEDS : Self = Self { id : 292 , registry_key : "minecraft:pumpkin_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\x08\x0Bcrop_result\x16minecraft:pumpkin_stem\x01\x1Aplant_at_any_solid_surface\0\0\0\0" } ;
pub const PUMPKIN_STEM: Self = Self {
id: 104,
registry_key: "minecraft:pumpkin_stem",
@@ -77161,7 +77161,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const PURPLE_BUNDLE : Self = Self { id : 870 , registry_key : "minecraft:purple_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x17bundle_purple_open_back\x08\x11bundle_open_front\x18bundle_purple_open_front\x08\x07default\rbundle_purple\0\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x03\x11enchantable_value\0\x03\x11creative_category\x06\x03\x0Emax_stack_size\x02\x01\x04foil\0\x01\x0Eliquid_clipped\0\x03\x0Bframe_count\x02\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\0\x01\x12hidden_in_commands\x02\x05\x0Cmining_speed\0\0\x80?\x08\x10enchantable_slot\x04none\x01\x0Eallow_off_hand\0\x01\x0Eshould_despawn\x01\x03\x06damage\0\x01\rhand_equipped\0\x03\ruse_animation\0\0\t\titem_tags\0\0\0\0" } ;
pub const PURPLE_BUNDLE : Self = Self { id : 870 , registry_key : "minecraft:purple_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\t\titem_tags\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\x03\x11creative_category\x06\x03\x06damage\0\x05\x0Cmining_speed\0\0\x80?\x03\x11enchantable_value\0\x01\x0Eliquid_clipped\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x03\x0Bframe_count\x02\x01\x04foil\0\x01\x0Eshould_despawn\x01\x03\ruse_animation\0\x03\x0Cuse_duration\0\x01\rhand_equipped\0\x01\x0Eallow_off_hand\0\x01\x17can_destroy_in_creative\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\rbundle_purple\x08\x10bundle_open_back\x17bundle_purple_open_back\x08\x11bundle_open_front\x18bundle_purple_open_front\0\0\x01\x0Fstacked_by_data\0\x01\x12hidden_in_commands\x02\x08\x0Ecreative_group\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const PURPLE_CANDLE: Self = Self {
id: -423,
registry_key: "minecraft:purple_candle",
@@ -77351,7 +77351,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const RABBIT : Self = Self { id : 288 , registry_key : "minecraft:rabbit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x99>\x08\rcooldown_type\0\x03\tnutrition\x06\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\0\0\0" } ;
pub const RABBIT : Self = Self { id : 288 , registry_key : "minecraft:rabbit" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x03\tnutrition\x06\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x08\x11using_converts_to\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x05\x13saturation_modifier\x9A\x99\x99>\x03\rcooldown_time\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const RABBIT_FOOT: Self = Self {
id: 538,
registry_key: "minecraft:rabbit_foot",
@@ -77373,7 +77373,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const RABBIT_STEW : Self = Self { id : 290 , registry_key : "minecraft:rabbit_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x18minecraft:max_stack_size\x02\n\x0Eminecraft:food\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\x14\x08\rcooldown_type\0\x03\ron_use_action\x01\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\x04bowl\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const RABBIT_STEW : Self = Self { id : 290 , registry_key : "minecraft:rabbit_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\x9A\x99\x19?\x03\tnutrition\x14\x03\ron_use_action\x01\x08\x11using_converts_to\x04bowl\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x08\rcooldown_type\0\0\x03\x16minecraft:use_duration@\x03\x18minecraft:max_stack_size\x02\0\0" } ;
pub const RAIL: Self = Self {
id: 66,
registry_key: "minecraft:rail",
@@ -77451,7 +77451,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const RED_BUNDLE : Self = Self { id : 871 , registry_key : "minecraft:red_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n\x16minecraft:storage_item\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\0\n\x0Fitem_properties\x01\x0Eshould_despawn\x01\x03\x06damage\0\x05\x0Cmining_speed\0\0\x80?\x01\x04foil\0\x03\ruse_animation\0\x01\x0Eallow_off_hand\0\x01\x0Fstacked_by_data\0\x01\rhand_equipped\0\x08\x10enchantable_slot\x04none\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x14bundle_red_open_back\x08\x07default\nbundle_red\x08\x11bundle_open_front\x15bundle_red_open_front\0\0\x03\x11creative_category\x06\x08\x0Ecreative_group\0\x03\x0Bframe_count\x02\x01\x12hidden_in_commands\x02\x03\x0Cuse_duration\0\x01\x17can_destroy_in_creative\x01\x03\x0Emax_stack_size\x02\x01\x0Eliquid_clipped\0\x03\x11enchantable_value\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\0\0" } ;
pub const RED_BUNDLE : Self = Self { id : 871 , registry_key : "minecraft:red_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x0Fitem_properties\x01\x17can_destroy_in_creative\x01\x01\x12hidden_in_commands\x02\x01\x0Eallow_off_hand\0\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x14bundle_red_open_back\x08\x07default\nbundle_red\x08\x11bundle_open_front\x15bundle_red_open_front\0\0\x03\x0Bframe_count\x02\x03\ruse_animation\0\x03\x06damage\0\x01\x04foil\0\x01\x0Eliquid_clipped\0\x08\x10enchantable_slot\x04none\x03\x0Emax_stack_size\x02\x01\x0Eshould_despawn\x01\x05\x0Cmining_speed\0\0\x80?\x03\x11enchantable_value\0\x03\x11creative_category\x06\x01\x0Fstacked_by_data\0\x01\rhand_equipped\0\x03\x0Cuse_duration\0\x08\x0Ecreative_group\0\0\t\titem_tags\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\x03\tmax_slots\x80\x01\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\t\rallowed_items\0\0\0\0\0" } ;
pub const RED_CANDLE: Self = Self {
id: -427,
registry_key: "minecraft:red_candle",
@@ -77809,7 +77809,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const ROTTEN_FLESH : Self = Self { id : 277 , registry_key : "minecraft:rotten_flesh" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x05\x13saturation_modifier\xCD\xCC\xCC=\t\x07effects\n\x02\x08\x04name\x06hunger\x03\x08duration<\x08\rdescriptionId\rpotion.hunger\x03\tamplifier\0\x03\x02id\"\x05\x06chance\xCD\xCCL?\0\x03\tnutrition\x08\x08\x11using_converts_to\0\x03\ron_use_action\x01\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const ROTTEN_FLESH : Self = Self { id : 277 , registry_key : "minecraft:rotten_flesh" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x07effects\n\x02\x05\x06chance\xCD\xCCL?\x03\tamplifier\0\x08\rdescriptionId\rpotion.hunger\x03\x08duration<\x08\x04name\x06hunger\x03\x02id\"\0\x08\rcooldown_type\0\x01\x0Ecan_always_eat\0\x08\x11using_converts_to\0\x03\tnutrition\x08\x03\rcooldown_time\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCC\xCC=\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const SADDLE: Self = Self {
id: 374,
registry_key: "minecraft:saddle",
@@ -77817,7 +77817,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const SALMON : Self = Self { id : 265 , registry_key : "minecraft:salmon" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\n\x0Eminecraft:food\x03\ron_use_action\x01\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\x08\x11using_converts_to\0\x05\x13saturation_modifier\xCD\xCC\xCC=\x03\tnutrition\x04\x01\x0Ecan_always_eat\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const SALMON : Self = Self { id : 265 , registry_key : "minecraft:salmon" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\rcooldown_time\0\x08\x11using_converts_to\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x04\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCC\xCC=\x08\rcooldown_type\0\0\x03\x16minecraft:use_duration@\x01\x19minecraft:stacked_by_data\x01\0\0" } ;
pub const SALMON_BUCKET: Self = Self {
id: 368,
registry_key: "minecraft:salmon_bucket",
@@ -78378,7 +78378,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const SPIDER_EYE : Self = Self { id : 278 , registry_key : "minecraft:spider_eye" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x08\rcooldown_type\0\t\x07effects\n\x02\x05\x06chance\0\0\x80?\x03\x08duration\n\x08\x04name\x06poison\x03\x02id&\x08\rdescriptionId\rpotion.poison\x03\tamplifier\0\0\x05\x13saturation_modifier\xCD\xCCL?\x03\rcooldown_time\0\x03\tnutrition\x04\x03\ron_use_action\x01\x08\x11using_converts_to\0\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\0\0" } ;
pub const SPIDER_EYE : Self = Self { id : 278 , registry_key : "minecraft:spider_eye" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x05\x13saturation_modifier\xCD\xCCL?\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\x04\t\x07effects\n\x02\x03\x08duration\n\x03\x02id&\x08\rdescriptionId\rpotion.poison\x08\x04name\x06poison\x03\tamplifier\0\x05\x06chance\0\0\x80?\0\x08\rcooldown_type\0\x03\rcooldown_time\0\0\0\0" } ;
pub const SPIDER_SPAWN_EGG: Self = Self {
id: 450,
registry_key: "minecraft:spider_spawn_egg",
@@ -78743,7 +78743,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const STONE_SPEAR : Self = Self { id : 855 , registry_key : "minecraft:stone_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Bitem.stone_spear.attack_hit\x08\x0Battack_miss\x1Citem.stone_spear.attack_miss\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x15minecraft:enchantable\x08\x04slot\x0Bmelee_spear\x01\x05value\x05\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x14minecraft:stone_tier\x12minecraft:is_spear\0\n\x18minecraft:swing_duration\x05\x05value\0\0@?\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\x08\rrepair_amount)context.other->query.remaining_durability\t\x05items\n\x02\x08\x04name\x15minecraft:stone_spear\0\0\t\x05items\n\x02\x08\x04tags,q.all_tags('minecraft:stone_tool_materials')\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\n\x10minecraft:damage\x02\x05value\x02\0\0\n\x17minecraft:use_modifiers\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\x01\x0Femit_vibrations\0\x08\x0Bstart_sound\x14item.stone_spear.use\x05\x11movement_modifier\0\0\x80?\0\n\x19minecraft:piercing_weapon\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\rhitbox_margin\0\0\x80>\0\t\titem_tags\x08\x04\x14minecraft:stone_tier\x12minecraft:is_spear\n\x12minecraft:cooldown\x08\x08category\x05spear\x05\x08duration\0\0@?\x08\x04type\x06attack\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x14knockback_conditions\x05\x12min_relative_speed\0\0\0\0\x02\x0Cmax_duration\xB4\0\x05\tmin_speed33\xA3@\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x13dismount_conditions\x05\tmin_speed\0\0PA\x02\x0Cmax_durationZ\0\x05\x12min_relative_speed\0\0\0\0\0\x05\x0Fdamage_modifier\0\0\0\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration\x13\x01\0\x05\x11damage_multiplier\x85\xEBQ?\x02\x05delay\x0E\0\x05\rhitbox_margin\0\0\x80>\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\0\0\n\x0Fitem_properties\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x01\x0Eliquid_clipped\0\x01\rhand_equipped\x01\x01\x12hidden_in_commands\x02\x01\x04foil\0\x03\x11enchantable_value\n\x03\ruse_animation\0\x01\x0Eallow_off_hand\0\x03\x11creative_category\x06\x08\x10enchantable_slot\x0Bmelee_spear\x03\x06damage\x04\x05\x0Cmining_speed\0\0\x80?\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bstone_spear\0\0\x03\x0Bframe_count\x02\0\n\x16minecraft:display_name\x08\x05value\x15item.stone_spear.name\0\n\x14minecraft:durability\x03\x0Emax_durability\x84\x02\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\0\0\0" } ;
pub const STONE_SPEAR : Self = Self { id : 855 , registry_key : "minecraft:stone_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x15item.stone_spear.name\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x0Fdamage_modifier\0\0\0\0\x02\x05delay\x0E\0\n\x13dismount_conditions\x05\tmin_speed\0\0PA\x02\x0Cmax_durationZ\0\x05\x12min_relative_speed\0\0\0\0\0\x05\rhitbox_margin\0\0\x80>\x05\x11damage_multiplier\x85\xEBQ?\n\x14knockback_conditions\x02\x0Cmax_duration\xB4\0\x05\tmin_speed33\xA3@\x05\x12min_relative_speed\0\0\0\0\0\n\x11damage_conditions\x02\x0Cmax_duration\x13\x01\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\0\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\0\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\x0Eallow_off_hand\0\x08\x10enchantable_slot\x0Bmelee_spear\x03\x0Bframe_count\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bstone_spear\0\0\x01\x12hidden_in_commands\x02\x01\x04foil\0\x03\x11creative_category\x06\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x03\x06damage\x04\x01\x17can_destroy_in_creative\x01\x01\rhand_equipped\x01\x05\x0Cmining_speed\0\0\x80?\x03\x11enchantable_value\n\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x01\x0Eshould_despawn\x01\x03\ruse_animation\0\0\n\x12minecraft:cooldown\x08\x04type\x06attack\x05\x08duration\0\0@?\x08\x08category\x05spear\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\rhitbox_margin\0\0\x80>\0\n\x18minecraft:swing_duration\x05\x05value\0\0@?\0\n\x16minecraft:swing_sounds\x08\x0Battack_miss\x1Citem.stone_spear.attack_miss\x08\nattack_hit\x1Bitem.stone_spear.attack_hit\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x14minecraft:stone_tier\x12minecraft:is_spear\0\n\x15minecraft:enchantable\x01\x05value\x05\x08\x04slot\x0Bmelee_spear\0\n\x14minecraft:durability\n\rdamage_chance\x03\x03max\xC8\x01\x03\x03min\0\0\x03\x0Emax_durability\x84\x02\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\t\titem_tags\x08\x04\x14minecraft:stone_tier\x12minecraft:is_spear\n\x17minecraft:use_modifiers\x05\x11movement_modifier\0\0\x80?\x01\x0Femit_vibrations\0\x08\x0Bstart_sound\x14item.stone_spear.use\x08\x0Bstart_using\x06always\x05\x0Cuse_duration\0\xA0\x8CG\0\n\x10minecraft:damage\x02\x05value\x02\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\x08\rrepair_amount)context.other->query.remaining_durability\t\x05items\n\x02\x08\x04name\x15minecraft:stone_spear\0\0\t\x05items\n\x02\x08\x04tags,q.all_tags('minecraft:stone_tool_materials')\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\0\0\0\0" } ;
pub const STONE_STAIRS: Self = Self {
id: 67,
registry_key: "minecraft:stone_stairs",
@@ -79122,8 +79122,8 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const SUSPICIOUS_STEW : Self = Self { id : 602 , registry_key : "minecraft:suspicious_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\ron_use_action\x02\x03\tnutrition\x0C\x03\rcooldown_time\0\x05\x13saturation_modifier\x9A\x99\x19?\x08\rcooldown_type\0\x01\x0Ecan_always_eat\x01\x08\x11using_converts_to\x04bowl\0\x03\x18minecraft:max_stack_size\x02\x03\x16minecraft:use_duration@\0\0" } ;
pub const SWEET_BERRIES : Self = Self { id : 287 , registry_key : "minecraft:sweet_berries" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x1Aminecraft:sweet_berry_bush\t\x08plant_at\x08\x14\x08farmland\x05grass\x04dirt\x0Bcoarse_dirt\x06podzol\nmoss_block\x08mycelium\x03mud\x14muddy_mangrove_roots\x0Fdirt_with_roots\0\n\x0Eminecraft:food\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x08\rcooldown_type\0\x03\tnutrition\x04\x05\x13saturation_modifier\x9A\x99\x99>\x08\x11using_converts_to\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\rcooldown_time\0\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const SUSPICIOUS_STEW : Self = Self { id : 602 , registry_key : "minecraft:suspicious_stew" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x03\x16minecraft:use_duration@\x03\x18minecraft:max_stack_size\x02\n\x0Eminecraft:food\x01\x0Ecan_always_eat\x01\x03\ron_use_action\x02\x08\x11using_converts_to\x04bowl\x03\tnutrition\x0C\x05\x13saturation_modifier\x9A\x99\x19?\x03\rcooldown_time\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\0\0\0" } ;
pub const SWEET_BERRIES : Self = Self { id : 287 , registry_key : "minecraft:sweet_berries" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:food\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\x04\x08\rcooldown_type\0\x05\x13saturation_modifier\x9A\x99\x99>\x01\x0Ecan_always_eat\0\x03\ron_use_action\x01\x03\rcooldown_time\0\0\x03\x16minecraft:use_duration@\n\x0Eminecraft:seed\x08\x0Bcrop_result\x1Aminecraft:sweet_berry_bush\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x14\x08farmland\x05grass\x04dirt\x0Bcoarse_dirt\x06podzol\nmoss_block\x08mycelium\x03mud\x14muddy_mangrove_roots\x0Fdirt_with_roots\0\0\0" } ;
pub const SWEET_BERRY_BUSH: Self = Self {
id: -207,
registry_key: "minecraft:sweet_berry_bush",
@@ -79222,7 +79222,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const TORCHFLOWER_SEEDS : Self = Self { id : 296 , registry_key : "minecraft:torchflower_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\x0Bcrop_result\x1Aminecraft:torchflower_crop\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\0\0\0" } ;
pub const TORCHFLOWER_SEEDS : Self = Self { id : 296 , registry_key : "minecraft:torchflower_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x01\x1Aplant_at_any_solid_surface\0\x08\x0Bcrop_result\x1Aminecraft:torchflower_crop\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\0\0\0" } ;
pub const TOTEM_OF_UNDYING: Self = Self {
id: 578,
registry_key: "minecraft:totem_of_undying",
@@ -79251,7 +79251,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const TRIAL_KEY : Self = Self { id : 876 , registry_key : "minecraft:trial_key" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x01\x0Eshould_despawn\x01\x01\x12hidden_in_commands\x02\x03\x11creative_category\x08\x01\rhand_equipped\0\x03\x0Cuse_duration\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\ttrial_key\0\0\x01\x0Fstacked_by_data\0\x03\x0Bframe_count\x02\x08\x10enchantable_slot\x04none\x01\x17can_destroy_in_creative\x01\x03\x11enchantable_value\0\x01\x0Eallow_off_hand\0\x01\x0Eliquid_clipped\0\x05\x0Cmining_speed\0\0\x80?\x08\x0Ecreative_group\0\x03\x06damage\0\x03\ruse_animation\0\x03\x0Emax_stack_size\x80\x01\x01\x04foil\0\0\t\titem_tags\0\0\n\x16minecraft:display_name\x08\x05value\x13item.trial_key.name\0\0\0" } ;
pub const TRIAL_KEY : Self = Self { id : 876 , registry_key : "minecraft:trial_key" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\n\x0Eminecraft:icon\n\x08textures\x08\x07default\ttrial_key\0\0\x03\x06damage\0\x08\x10enchantable_slot\x04none\x01\x0Eshould_despawn\x01\x03\x0Emax_stack_size\x80\x01\x03\x0Bframe_count\x02\x01\x04foil\0\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x03\x11enchantable_value\0\x01\x0Eallow_off_hand\0\x01\rhand_equipped\0\x01\x17can_destroy_in_creative\x01\x08\x0Ecreative_group\0\x03\x0Cuse_duration\0\x01\x12hidden_in_commands\x02\x01\x0Eliquid_clipped\0\x03\x11creative_category\x08\x05\x0Cmining_speed\0\0\x80?\0\t\titem_tags\0\0\n\x16minecraft:display_name\x08\x05value\x13item.trial_key.name\0\0\0" } ;
pub const TRIAL_SPAWNER: Self = Self {
id: -315,
registry_key: "minecraft:trial_spawner",
@@ -79280,7 +79280,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const TROPICAL_FISH : Self = Self { id : 266 , registry_key : "minecraft:tropical_fish" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\n\x0Eminecraft:food\x03\rcooldown_time\0\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x08\x11using_converts_to\0\x03\tnutrition\x02\x08\rcooldown_type\0\x05\x13saturation_modifier\xCD\xCC\xCC=\0\x03\x16minecraft:use_duration@\0\0" } ;
pub const TROPICAL_FISH : Self = Self { id : 266 , registry_key : "minecraft:tropical_fish" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\x01\x19minecraft:stacked_by_data\x01\x03\x16minecraft:use_duration@\n\x0Eminecraft:food\x03\rcooldown_time\0\x08\x11using_converts_to\0\x08\rcooldown_type\0\t\x0Con_use_range\x05\x06\0\0\0A\0\0\0A\0\0\0A\x03\tnutrition\x02\x03\ron_use_action\x01\x01\x0Ecan_always_eat\0\x05\x13saturation_modifier\xCD\xCC\xCC=\0\0\0" } ;
pub const TROPICAL_FISH_BUCKET: Self = Self {
id: 369,
registry_key: "minecraft:tropical_fish_bucket",
@@ -80331,8 +80331,8 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const WHEAT_SEEDS : Self = Self { id : 291 , registry_key : "minecraft:wheat_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\x08\x0Bcrop_result\x0Fminecraft:wheat\x08\rplant_at_face\x02up\x01\x1Aplant_at_any_solid_surface\0\t\x08plant_at\x08\x02\x12minecraft:farmland\0\0\0" } ;
pub const WHITE_BUNDLE : Self = Self { id : 872 , registry_key : "minecraft:white_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\t\titem_tags\0\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x01\x1Aallow_nested_storage_items\x01\x03\tmax_slots\x80\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\rhand_equipped\0\x01\x0Eliquid_clipped\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Cbundle_white\x08\x10bundle_open_back\x16bundle_white_open_back\x08\x11bundle_open_front\x17bundle_white_open_front\0\0\x03\x11creative_category\x06\x03\ruse_animation\0\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x05\x0Cmining_speed\0\0\x80?\x01\x12hidden_in_commands\x02\x01\x0Eshould_despawn\x01\x01\x17can_destroy_in_creative\x01\x01\x04foil\0\x03\x0Emax_stack_size\x02\x03\x06damage\0\x08\x10enchantable_slot\x04none\x03\x0Bframe_count\x02\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\0\0\0\0" } ;
pub const WHEAT_SEEDS : Self = Self { id : 291 , registry_key : "minecraft:wheat_seeds" , version : BedrockItemVersion :: Legacy , component_based : false , definition_components : b"\n\0\n\ncomponents\n\x0Eminecraft:seed\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\x0Bcrop_result\x0Fminecraft:wheat\x01\x1Aplant_at_any_solid_surface\0\x08\rplant_at_face\x02up\0\0\0" } ;
pub const WHITE_BUNDLE : Self = Self { id : 872 , registry_key : "minecraft:white_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\x01\x1Aallow_nested_storage_items\x01\0\t\titem_tags\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x01\x12hidden_in_commands\x02\x03\x11creative_category\x06\x01\x0Fstacked_by_data\0\x08\x10enchantable_slot\x04none\x05\x0Cmining_speed\0\0\x80?\x01\x04foil\0\x01\rhand_equipped\0\x01\x0Eshould_despawn\x01\x03\x06damage\0\x03\ruse_animation\0\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x01\x0Eliquid_clipped\0\x01\x17can_destroy_in_creative\x01\x03\x0Emax_stack_size\x02\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x16bundle_white_open_back\x08\x11bundle_open_front\x17bundle_white_open_front\x08\x07default\x0Cbundle_white\0\0\x03\x0Cuse_duration\0\x03\x0Bframe_count\x02\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\0\0" } ;
pub const WHITE_CANDLE: Self = Self {
id: -413,
registry_key: "minecraft:white_candle",
@@ -80452,7 +80452,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const WIND_CHARGE : Self = Self { id : 877 , registry_key : "minecraft:wind_charge" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x15item.wind_charge.name\0\n\x12minecraft:cooldown\x05\x08duration\0\0\0?\x08\x08category\x0Bwind_charge\x08\x04type\x03use\0\n\x0Fitem_properties\x08\x10enchantable_slot\x04none\x03\ruse_animation\0\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bwind_charge\0\0\x03\x06damage\0\x03\x0Bframe_count\x02\x01\x0Eallow_off_hand\0\x01\x17can_destroy_in_creative\x01\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x01\rhand_equipped\0\x05\x0Cmining_speed\0\0\x80?\x08\x0Ecreative_group\0\x01\x0Eshould_despawn\x01\x01\x0Fstacked_by_data\0\x03\x0Cuse_duration\0\x03\x11enchantable_value\0\x01\x04foil\0\x03\x0Emax_stack_size\x80\x01\x01\x0Eliquid_clipped\0\0\n\x14minecraft:projectile\x08\x11projectile_entity\"minecraft:wind_charge_projectile<>\x05\x16minimum_critical_power\0\0\0\0\0\n\x13minecraft:throwable\x05\x12launch_power_scale\0\0\xC0?\x05\x10max_launch_power\0\0\xC0?\x05\x11min_draw_duration\0\0\0\0\x01\x1Cscale_power_by_draw_duration\0\x05\x11max_draw_duration\0\0\0\0\x01\x12do_swing_animation\x01\0\t\titem_tags\0\0\0\0" } ;
pub const WIND_CHARGE : Self = Self { id : 877 , registry_key : "minecraft:wind_charge" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x0Fitem_properties\x01\x12hidden_in_commands\x02\x08\x0Ecreative_group\0\x01\x0Eliquid_clipped\0\x03\x11creative_category\x06\x03\x0Bframe_count\x02\x03\x0Cuse_duration\0\x03\x0Emax_stack_size\x80\x01\n\x0Eminecraft:icon\n\x08textures\x08\x07default\x0Bwind_charge\0\0\x05\x0Cmining_speed\0\0\x80?\x03\x06damage\0\x01\x0Eallow_off_hand\0\x03\x11enchantable_value\0\x01\rhand_equipped\0\x01\x0Eshould_despawn\x01\x01\x04foil\0\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x08\x10enchantable_slot\x04none\0\n\x16minecraft:display_name\x08\x05value\x15item.wind_charge.name\0\n\x14minecraft:projectile\x05\x16minimum_critical_power\0\0\0\0\x08\x11projectile_entity\"minecraft:wind_charge_projectile<>\0\n\x13minecraft:throwable\x05\x12launch_power_scale\0\0\xC0?\x05\x11min_draw_duration\0\0\0\0\x05\x10max_launch_power\0\0\xC0?\x05\x11max_draw_duration\0\0\0\0\x01\x12do_swing_animation\x01\x01\x1Cscale_power_by_draw_duration\0\0\n\x12minecraft:cooldown\x08\x04type\x03use\x08\x08category\x0Bwind_charge\x05\x08duration\0\0\0?\0\t\titem_tags\0\0\0\0" } ;
pub const WITCH_SPAWN_EGG: Self = Self {
id: 456,
registry_key: "minecraft:witch_spawn_egg",
@@ -80565,7 +80565,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const WOODEN_SPEAR : Self = Self { id : 856 , registry_key : "minecraft:wooden_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x16minecraft:display_name\x08\x05value\x16item.wooden_spear.name\0\n\x17minecraft:use_modifiers\x05\x0Cuse_duration\0\xA0\x8CG\x08\x0Bstart_sound\x15item.wooden_spear.use\x01\x0Femit_vibrations\0\x08\x0Bstart_using\x06always\x05\x11movement_modifier\0\0\x80?\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x14minecraft:durability\x03\x0Emax_durabilityx\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Citem.wooden_spear.attack_hit\x08\x0Battack_miss\x1Ditem.wooden_spear.attack_miss\0\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:wooden_tier\x12minecraft:is_spear\0\n\x15minecraft:enchantable\x01\x05value\x0F\x08\x04slot\x0Bmelee_spear\0\n\x18minecraft:swing_duration\x05\x05valueff&?\0\t\titem_tags\x08\x04\x15minecraft:wooden_tier\x12minecraft:is_spear\n\x12minecraft:cooldown\x08\x04type\x06attack\x08\x08category\x05spear\x05\x08durationff&?\0\n\x10minecraft:damage\x02\x05value\x01\0\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03min\0\0\0@\x05\x03max\0\0\xF0@\0\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\0\n\x0Eminecraft:fuel\x05\x08duration\0\0 A\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x0Fitem_properties\x01\x0Eallow_off_hand\0\x05\x0Cmining_speed\0\0\x80?\n\x0Eminecraft:icon\n\x08textures\x08\x07default\nwood_spear\0\0\x03\ruse_animation\0\x08\x10enchantable_slot\x0Bmelee_spear\x08\x0Ecreative_group\0\x03\x11creative_category\x06\x03\x0Bframe_count\x02\x01\x0Fstacked_by_data\0\x03\x06damage\x02\x01\x0Eshould_despawn\x01\x01\x0Eliquid_clipped\0\x01\x17can_destroy_in_creative\x01\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\x80\xE4\xAF\x01\x03\x11enchantable_value\x1E\x01\x12hidden_in_commands\x02\x01\rhand_equipped\x01\x01\x04foil\0\0\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\x05\rhitbox_margin\0\0\x80>\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration,\x01\0\x05\x0Fdamage_modifier\0\0\0\0\x05\x11damage_multiplier333?\n\x14knockback_conditions\x05\x12min_relative_speed\0\0\0\0\x05\tmin_speed33\xA3@\x02\x0Cmax_duration\xC8\0\0\x02\x05delay\x0F\0\n\x13dismount_conditions\x05\tmin_speed\0\0`A\x02\x0Cmax_durationd\0\x05\x12min_relative_speed\0\0\0\0\0\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\0\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\x08\rrepair_amount)context.other->query.remaining_durability\t\x05items\n\x02\x08\x04name\x16minecraft:wooden_spear\0\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04tags\x1Eq.all_tags('minecraft:planks')\0\0\0\0\0" } ;
pub const WOODEN_SPEAR : Self = Self { id : 856 , registry_key : "minecraft:wooden_spear" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x18minecraft:kinetic_weapon\n\x18minecraft:kinetic_weapon\x05\x11damage_multiplier333?\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\n\x05reach\x05\x03min\0\0\0@\x05\x03max\0\0\x90@\0\n\x13dismount_conditions\x05\tmin_speed\0\0`A\x05\x12min_relative_speed\0\0\0\0\x02\x0Cmax_durationd\0\0\n\x11damage_conditions\x05\x12min_relative_speed33\x93@\x05\tmin_speed\0\0\0\0\x02\x0Cmax_duration,\x01\0\x02\x05delay\x0F\0\n\x14knockback_conditions\x05\tmin_speed33\xA3@\x05\x12min_relative_speed\0\0\0\0\x02\x0Cmax_duration\xC8\0\0\x05\rhitbox_margin\0\0\x80>\x05\x0Fdamage_modifier\0\0\0\0\0\0\n\x17minecraft:hand_equipped\x01\x05value\x01\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x18minecraft:swing_duration\x05\x05valueff&?\0\n\x14minecraft:repairable\t\x0Crepair_items\n\x04\t\x05items\n\x02\x08\x04name\x16minecraft:wooden_spear\0\x08\rrepair_amount)context.other->query.remaining_durability\0\x08\rrepair_amount\x1Bquery.max_durability * 0.25\t\x05items\n\x02\x08\x04tags\x1Eq.all_tags('minecraft:planks')\0\0\0\n\x12minecraft:cooldown\x08\x08category\x05spear\x08\x04type\x06attack\x05\x08durationff&?\0\n\x0Fitem_properties\x08\x0Ecreative_group\0\x03\x06damage\x02\x03\ruse_animation\0\x03\x0Cuse_duration\x80\xE4\xAF\x01\x01\x0Eallow_off_hand\0\x01\x17can_destroy_in_creative\x01\x01\rhand_equipped\x01\x01\x0Fstacked_by_data\0\x03\x11creative_category\x06\x03\x11enchantable_value\x1E\x03\x0Emax_stack_size\x02\x01\x0Eshould_despawn\x01\x08\x10enchantable_slot\x0Bmelee_spear\x03\x0Bframe_count\x02\x05\x0Cmining_speed\0\0\x80?\x01\x04foil\0\x01\x12hidden_in_commands\x02\n\x0Eminecraft:icon\n\x08textures\x08\x07default\nwood_spear\0\0\x01\x0Eliquid_clipped\0\0\n\x15minecraft:enchantable\x01\x05value\x0F\x08\x04slot\x0Bmelee_spear\0\n\x0Eminecraft:fuel\x05\x08duration\0\0 A\0\n\x16minecraft:swing_sounds\x08\nattack_hit\x1Citem.wooden_spear.attack_hit\x08\x0Battack_miss\x1Ditem.wooden_spear.attack_miss\0\t\titem_tags\x08\x04\x15minecraft:wooden_tier\x12minecraft:is_spear\n\x0Eminecraft:tags\t\x04tags\x08\x04\x15minecraft:wooden_tier\x12minecraft:is_spear\0\n\x17minecraft:use_modifiers\x05\x0Cuse_duration\0\xA0\x8CG\x01\x0Femit_vibrations\0\x08\x0Bstart_using\x06always\x08\x0Bstart_sound\x15item.wooden_spear.use\x05\x11movement_modifier\0\0\x80?\0\n\x10minecraft:damage\x02\x05value\x01\0\0\n\x14minecraft:durability\x03\x0Emax_durabilityx\n\rdamage_chance\x03\x03min\0\x03\x03max\xC8\x01\0\0\n\x19minecraft:piercing_weapon\n\x0Ecreative_reach\x05\x03max\0\0\xF0@\x05\x03min\0\0\0@\0\x05\rhitbox_margin\0\0\x80>\n\x05reach\x05\x03max\0\0\x90@\x05\x03min\0\0\0@\0\0\n\x16minecraft:display_name\x08\x05value\x16item.wooden_spear.name\0\0\0" } ;
pub const WOODEN_SWORD: Self = Self {
id: 310,
registry_key: "minecraft:wooden_sword",
@@ -80594,7 +80594,7 @@ impl BedrockItem {
component_based: false,
definition_components: b"\n\0\0",
};
pub const YELLOW_BUNDLE : Self = Self { id : 873 , registry_key : "minecraft:yellow_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x16minecraft:storage_item\x01\x1Aallow_nested_storage_items\x01\t\rallowed_items\0\0\x03\tmax_slots\x80\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n\x0Fitem_properties\x03\x06damage\0\x03\x0Bframe_count\x02\n\x0Eminecraft:icon\n\x08textures\x08\x10bundle_open_back\x17bundle_yellow_open_back\x08\x07default\rbundle_yellow\x08\x11bundle_open_front\x18bundle_yellow_open_front\0\0\x08\x0Ecreative_group\0\x08\x10enchantable_slot\x04none\x01\x12hidden_in_commands\x02\x01\x0Eshould_despawn\x01\x01\x04foil\0\x03\ruse_animation\0\x01\rhand_equipped\0\x03\x11creative_category\x06\x01\x0Eallow_off_hand\0\x01\x0Eliquid_clipped\0\x03\x0Emax_stack_size\x02\x03\x0Cuse_duration\0\x01\x0Fstacked_by_data\0\x01\x17can_destroy_in_creative\x01\x05\x0Cmining_speed\0\0\x80?\x03\x11enchantable_value\0\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\t\titem_tags\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\0\0" } ;
pub const YELLOW_BUNDLE : Self = Self { id : 873 , registry_key : "minecraft:yellow_bundle" , version : BedrockItemVersion :: DataDriven , component_based : true , definition_components : b"\n\0\n\ncomponents\n\x1Cminecraft:bundle_interaction\x03\x12num_viewable_slots\x18\0\n\x0Fitem_properties\x03\x06damage\0\x01\x0Eshould_despawn\x01\x01\x04foil\0\x03\x0Cuse_duration\0\x03\x0Emax_stack_size\x02\x08\x10enchantable_slot\x04none\n\x0Eminecraft:icon\n\x08textures\x08\x07default\rbundle_yellow\x08\x11bundle_open_front\x18bundle_yellow_open_front\x08\x10bundle_open_back\x17bundle_yellow_open_back\0\0\x03\x11enchantable_value\0\x03\x11creative_category\x06\x01\x12hidden_in_commands\x02\x05\x0Cmining_speed\0\0\x80?\x08\x0Ecreative_group\0\x01\x0Fstacked_by_data\0\x03\ruse_animation\0\x01\x17can_destroy_in_creative\x01\x01\x0Eallow_off_hand\0\x03\x0Bframe_count\x02\x01\rhand_equipped\0\x01\x0Eliquid_clipped\0\0\n\x18minecraft:max_stack_size\x01\x05value\x01\0\n\x16minecraft:storage_item\t\rallowed_items\0\0\x01\x1Aallow_nested_storage_items\x01\t\x0Cbanned_items\n\x04\x08\x04name\x15minecraft:shulker_box\0\x08\x04name\x1Cminecraft:undyed_shulker_box\0\x03\tmax_slots\x80\x01\0\n\x1Eminecraft:storage_weight_limit\x03\x10max_weight_limit\x80\x01\0\n!minecraft:storage_weight_modifier\x03\x16weight_in_storage_item\x08\0\t\titem_tags\0\0\0\0" } ;
pub const YELLOW_CANDLE: Self = Self {
id: -417,
registry_key: "minecraft:yellow_candle",

File diff suppressed because one or more lines are too long

View File

@@ -415,7 +415,8 @@ impl ItemStack {
}
}
fn custom_data_compound(&self) -> Option<&NbtCompound> {
#[must_use]
pub fn custom_data_compound(&self) -> Option<&NbtCompound> {
self.get_data_component::<CustomDataImpl>()
.map(|custom_data| &custom_data.data)
}

View File

@@ -181,7 +181,7 @@ pub mod dimension;
#[cfg(feature = "enchantment")]
#[rustfmt::skip]
#[path = "generated/enchantment.rs"]
mod enchantment;
pub mod enchantment;
#[cfg(feature = "enchantment")]
pub use enchantment::*;

View File

@@ -56,7 +56,10 @@ impl EnderChestInventory {
///
/// Used to animate the ender chest lid based on viewers.
pub async fn set_tracker(&self, tracker: Arc<ViewerCountTracker>) {
self.tracker.lock().await.replace(tracker);
let old = self.tracker.lock().await.replace(tracker);
if let Some(old_tracker) = old {
old_tracker.close_container();
}
}
/// Checks if this inventory has a tracker set.
@@ -88,21 +91,28 @@ impl Inventory for EnderChestInventory {
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let items = self.items.read().await;
items[slot].clone()
items
.get(slot)
.cloned()
.unwrap_or_else(|| ItemStack::EMPTY.clone())
})
}
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut items = self.items.write().await;
std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone())
if slot < Self::INVENTORY_SIZE {
std::mem::replace(&mut items[slot], ItemStack::EMPTY.clone())
} else {
ItemStack::EMPTY.clone()
}
})
}
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
let mut items = self.items.write().await;
if !items[slot].is_empty() && amount > 0 {
if slot < Self::INVENTORY_SIZE && !items[slot].is_empty() && amount > 0 {
items[slot].split(amount)
} else {
ItemStack::EMPTY.clone()
@@ -113,7 +123,9 @@ impl Inventory for EnderChestInventory {
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
let mut items = self.items.write().await;
items[slot] = stack;
if slot < Self::INVENTORY_SIZE {
items[slot] = stack;
}
})
}
@@ -127,7 +139,8 @@ impl Inventory for EnderChestInventory {
fn on_close(&self) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if let Some(tracker) = self.tracker.lock().await.as_ref() {
let tracker = self.tracker.lock().await.take();
if let Some(tracker) = tracker {
tracker.close_container();
}
})
@@ -148,3 +161,85 @@ impl Clearable for EnderChestInventory {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use pumpkin_data::item::Item;
#[tokio::test]
async fn new_inventory() {
let ec = EnderChestInventory::new();
assert_eq!(ec.size(), 27);
assert!(ec.is_empty().await);
assert!(!ec.has_tracker().await);
}
#[tokio::test]
async fn set_and_get_stack() {
let ec = EnderChestInventory::new();
let stack = ItemStack::new(1, &Item::DIRT);
ec.set_stack(0, stack.clone()).await;
assert_eq!(ec.get_stack(0).await.item.id, Item::DIRT.id);
assert_eq!(ec.get_stack(0).await.item_count, 1);
assert!(!ec.is_empty().await);
// Out of bounds shouldn't panic
ec.set_stack(100, stack).await;
assert!(ec.get_stack(100).await.is_empty());
}
#[tokio::test]
async fn remove_stack() {
let ec = EnderChestInventory::new();
let stack = ItemStack::new(5, &Item::DIAMOND);
ec.set_stack(10, stack).await;
let removed_specific = ec.remove_stack_specific(10, 2).await;
assert_eq!(removed_specific.item_count, 2);
assert_eq!(ec.get_stack(10).await.item_count, 3);
let removed_all = ec.remove_stack(10).await;
assert_eq!(removed_all.item_count, 3);
assert!(ec.get_stack(10).await.is_empty());
assert!(ec.is_empty().await);
}
#[tokio::test]
async fn clear_inventory() {
let ec = EnderChestInventory::new();
ec.set_stack(0, ItemStack::new(1, &Item::STONE)).await;
ec.set_stack(26, ItemStack::new(1, &Item::OAK_LOG)).await;
assert!(!ec.is_empty().await);
ec.clear().await;
assert!(ec.is_empty().await);
}
#[tokio::test]
async fn tracker_lifecycle() {
let ec = EnderChestInventory::new();
let tracker1 = Arc::new(ViewerCountTracker::new());
let tracker2 = Arc::new(ViewerCountTracker::new());
ec.set_tracker(tracker1.clone()).await;
assert!(ec.has_tracker().await);
assert!(ec.is_tracker(&tracker1).await);
assert!(!ec.is_tracker(&tracker2).await);
ec.on_open().await;
assert_eq!(tracker1.get_viewer_count(), 1);
// Setting a new tracker while one is open should close the old tracker
ec.set_tracker(tracker2.clone()).await;
assert_eq!(tracker1.get_viewer_count(), 0);
assert!(ec.is_tracker(&tracker2).await);
ec.on_open().await;
assert_eq!(tracker2.get_viewer_count(), 1);
ec.on_close().await;
assert_eq!(tracker2.get_viewer_count(), 0);
assert!(!ec.has_tracker().await);
}
}

View File

@@ -0,0 +1,207 @@
pub use crate::wit::pumpkin::plugin::display::{
BillboardMode, BlockDisplayEntity, DisplayEntity, DisplayTransformation, InteractionEntity,
ItemDisplayEntity, ItemDisplayMode, Quaternionf, TextAlignment, TextDisplayEntity, Vector3f,
};
use crate::wit::pumpkin::plugin::item_stack::ItemStack;
use crate::wit::pumpkin::plugin::text::TextComponent;
use crate::wit::pumpkin::plugin::world::Entity;
/// Builder for constructing [`DisplayTransformation`].
#[derive(Clone, Copy, Debug)]
pub struct TransformationBuilder {
translation: Vector3f,
scale: Vector3f,
left_rotation: Quaternionf,
right_rotation: Quaternionf,
}
impl Default for TransformationBuilder {
fn default() -> Self {
Self::new()
}
}
impl TransformationBuilder {
/// Creates a new identity `TransformationBuilder`.
#[must_use]
pub const fn new() -> Self {
Self {
translation: Vector3f {
x: 0.0,
y: 0.0,
z: 0.0,
},
scale: Vector3f {
x: 1.0,
y: 1.0,
z: 1.0,
},
left_rotation: Quaternionf {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
},
right_rotation: Quaternionf {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
},
}
}
/// Sets the translation vector.
#[must_use]
pub const fn translation(mut self, x: f32, y: f32, z: f32) -> Self {
self.translation = Vector3f { x, y, z };
self
}
/// Sets the scale vector.
#[must_use]
pub const fn scale(mut self, x: f32, y: f32, z: f32) -> Self {
self.scale = Vector3f { x, y, z };
self
}
/// Sets uniform scale across all axes.
#[must_use]
pub const fn uniform_scale(mut self, scale: f32) -> Self {
self.scale = Vector3f {
x: scale,
y: scale,
z: scale,
};
self
}
/// Sets the left rotation quaternion.
#[must_use]
pub const fn left_rotation(mut self, x: f32, y: f32, z: f32, w: f32) -> Self {
self.left_rotation = Quaternionf { x, y, z, w };
self
}
/// Sets the right rotation quaternion.
#[must_use]
pub const fn right_rotation(mut self, x: f32, y: f32, z: f32, w: f32) -> Self {
self.right_rotation = Quaternionf { x, y, z, w };
self
}
/// Builds the [`DisplayTransformation`].
#[must_use]
pub const fn build(self) -> DisplayTransformation {
DisplayTransformation {
translation: self.translation,
scale: self.scale,
left_rotation: self.left_rotation,
right_rotation: self.right_rotation,
}
}
}
/// Extension trait on generic [`Entity`] for downcasting to specialized Display / Interaction resources.
pub trait EntityDisplayExt {
/// Attempts to view this entity as a base [`DisplayEntity`].
fn as_display(&self) -> Option<DisplayEntity>;
/// Attempts to view this entity as a [`BlockDisplayEntity`].
fn as_block_display(&self) -> Option<BlockDisplayEntity>;
/// Attempts to view this entity as an [`ItemDisplayEntity`].
fn as_item_display(&self) -> Option<ItemDisplayEntity>;
/// Attempts to view this entity as a [`TextDisplayEntity`].
fn as_text_display(&self) -> Option<TextDisplayEntity>;
/// Attempts to view this entity as an [`InteractionEntity`].
fn as_interaction(&self) -> Option<InteractionEntity>;
}
impl EntityDisplayExt for Entity {
fn as_display(&self) -> Option<DisplayEntity> {
DisplayEntity::from_entity(self)
}
fn as_block_display(&self) -> Option<BlockDisplayEntity> {
BlockDisplayEntity::from_entity(self)
}
fn as_item_display(&self) -> Option<ItemDisplayEntity> {
ItemDisplayEntity::from_entity(self)
}
fn as_text_display(&self) -> Option<TextDisplayEntity> {
TextDisplayEntity::from_entity(self)
}
fn as_interaction(&self) -> Option<InteractionEntity> {
InteractionEntity::from_entity(self)
}
}
/// Extension trait for [`DisplayEntity`] providing convenience mutation helpers.
pub trait DisplayEntityExt {
/// Sets translation directly without manually constructing a `DisplayTransformation`.
fn set_translation(&self, x: f32, y: f32, z: f32);
/// Sets scale directly without manually constructing a `DisplayTransformation`.
fn set_scale(&self, x: f32, y: f32, z: f32);
}
impl DisplayEntityExt for DisplayEntity {
fn set_translation(&self, x: f32, y: f32, z: f32) {
let mut transform = self.get_transformation();
transform.translation = Vector3f { x, y, z };
self.set_transformation(transform);
}
fn set_scale(&self, x: f32, y: f32, z: f32) {
let mut transform = self.get_transformation();
transform.scale = Vector3f { x, y, z };
self.set_transformation(transform);
}
}
/// Extension trait for [`ItemDisplayEntity`] providing convenience methods.
pub trait ItemDisplayEntityExt {
/// Sets the displayed item stack.
fn set_item_stack(&self, item: Option<ItemStack>);
}
impl ItemDisplayEntityExt for ItemDisplayEntity {
fn set_item_stack(&self, item: Option<ItemStack>) {
self.set_item(item);
}
}
/// Extension trait for [`TextDisplayEntity`] providing convenience methods.
pub trait TextDisplayEntityExt {
/// Sets text from a plain string.
fn set_plain_text(&self, text: &str);
}
impl TextDisplayEntityExt for TextDisplayEntity {
fn set_plain_text(&self, text: &str) {
self.set_text(TextComponent::text(text));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transformation_builder() {
let transform = TransformationBuilder::new()
.translation(1.0, 2.0, 3.0)
.scale(2.0, 2.0, 2.0)
.left_rotation(0.0, 0.7071, 0.0, 0.7071)
.build();
assert_eq!(transform.translation.x, 1.0);
assert_eq!(transform.translation.y, 2.0);
assert_eq!(transform.translation.z, 3.0);
assert_eq!(transform.scale.x, 2.0);
assert_eq!(transform.scale.y, 2.0);
assert_eq!(transform.scale.z, 2.0);
assert_eq!(transform.left_rotation.y, 0.7071);
}
}

View File

@@ -0,0 +1,377 @@
//! Plugin custom enchantment registration and builder utilities.
//!
//! This module provides a fluent, type-safe API for defining, registering, and querying
//! custom enchantments as well as applying custom enchantments to [`ItemStack`](crate::ItemStack)s.
//!
//! # Examples
//!
//! ## Defining and Registering a Custom Enchantment
//! ```rust,ignore
//! use pumpkin_plugin_api::{
//! enchantment::{AttributeModifierSlot, EnchantmentBuilder},
//! text::TextComponent,
//! Server,
//! };
//!
//! fn register_enchantments(server: &Server) {
//! let manager = server.get_enchantment_manager();
//!
//! manager.register(
//! EnchantmentBuilder::new("my_plugin:lifesteal", TextComponent::text("Life Steal"))
//! .max_level(3)
//! .anvil_cost(4)
//! .supported_items("#minecraft:enchantable/weapon")
//! .weight(2)
//! .slots([AttributeModifierSlot::MainHand])
//! .exclusive_with("custom:poison_touch")
//! ).expect("failed to register custom enchantment");
//! }
//! ```
//!
//! ## Applying Custom Enchantments to an [`ItemStack`](crate::ItemStack)
//! ```rust,ignore
//! use pumpkin_plugin_api::ItemStack;
//!
//! fn give_sword() -> ItemStack {
//! let mut sword = ItemStack::new("minecraft:diamond_sword", 1);
//! sword.add_custom_enchantment("my_plugin:lifesteal", 2);
//! assert!(sword.has_custom_enchantment("my_plugin:lifesteal"));
//! assert_eq!(sword.get_custom_enchantment_level("my_plugin:lifesteal"), Some(2));
//! sword
//! }
//! ```
pub use crate::wit::pumpkin::plugin::enchantments::{
AttributeModifierSlot, CustomEnchantment, Enchantment, EnchantmentManager,
};
pub use crate::wit::pumpkin::plugin::item_stack::CustomEnchantmentValue;
use crate::{Context, Server, TextComponent};
use std::fmt;
/// Errors that can occur when building or registering a custom enchantment.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EnchantmentError {
/// The enchantment identifier was empty.
EmptyId,
/// The enchantment maximum level was invalid (must be >= 1).
InvalidMaxLevel,
/// Registration with the server enchantment manager failed.
RegistrationFailed(String),
}
impl fmt::Display for EnchantmentError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyId => write!(f, "enchantment identifier cannot be empty"),
Self::InvalidMaxLevel => write!(f, "enchantment max level must be at least 1"),
Self::RegistrationFailed(msg) => {
write!(f, "failed to register enchantment: {msg}")
}
}
}
}
impl std::error::Error for EnchantmentError {}
/// Fluent builder for constructing and validating a [`CustomEnchantment`].
pub struct EnchantmentBuilder {
id: String,
description: TextComponent,
max_level: u32,
anvil_cost: u32,
supported_items: String,
weight: u32,
slots: Vec<AttributeModifierSlot>,
exclusive_set: Vec<String>,
}
impl EnchantmentBuilder {
/// Creates a new enchantment builder with the given unique identifier and description.
///
/// # Example
/// ```rust,ignore
/// let builder = EnchantmentBuilder::new("my_plugin:lifesteal", TextComponent::text("Life Steal"));
/// ```
#[must_use]
pub fn new(id: impl Into<String>, description: impl Into<TextComponent>) -> Self {
Self {
id: id.into(),
description: description.into(),
max_level: 1,
anvil_cost: 4,
supported_items: "#minecraft:enchantable/weapon".into(),
weight: 5,
slots: vec![AttributeModifierSlot::MainHand],
exclusive_set: Vec::new(),
}
}
/// Sets the maximum level of the enchantment (default: 1).
#[must_use]
pub const fn max_level(mut self, max_level: u32) -> Self {
self.max_level = max_level;
self
}
/// Sets the base anvil cost multiplier (default: 4).
#[must_use]
pub const fn anvil_cost(mut self, anvil_cost: u32) -> Self {
self.anvil_cost = anvil_cost;
self
}
/// Sets the supported items pattern or tag (e.g. `"#minecraft:enchantable/weapon"`).
#[must_use]
pub fn supported_items(mut self, items: impl Into<String>) -> Self {
self.supported_items = items.into();
self
}
/// Sets the weight / rarity of the enchantment (1..=10, higher = more common, default: 5).
#[must_use]
pub const fn weight(mut self, weight: u32) -> Self {
self.weight = weight;
self
}
/// Adds a single active equipment slot for this enchantment.
#[must_use]
pub fn slot(mut self, slot: AttributeModifierSlot) -> Self {
self.slots.push(slot);
self
}
/// Replaces the active equipment slots for this enchantment.
#[must_use]
pub fn slots(mut self, slots: impl IntoIterator<Item = AttributeModifierSlot>) -> Self {
self.slots = slots.into_iter().collect();
self
}
/// Adds an exclusive / conflicting enchantment ID.
#[must_use]
pub fn exclusive_with(mut self, id: impl Into<String>) -> Self {
self.exclusive_set.push(id.into());
self
}
/// Replaces the exclusive / conflicting enchantment list.
#[must_use]
pub fn exclusive_set(mut self, set: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.exclusive_set = set.into_iter().map(Into::into).collect();
self
}
/// Validates the enchantment parameters.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation fails.
pub fn validate(&self) -> Result<(), EnchantmentError> {
if self.id.trim().is_empty() {
return Err(EnchantmentError::EmptyId);
}
if self.max_level == 0 {
return Err(EnchantmentError::InvalidMaxLevel);
}
Ok(())
}
/// Builds the validated [`CustomEnchantment`] record.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation fails.
pub fn build(self) -> Result<CustomEnchantment, EnchantmentError> {
self.validate()?;
Ok(CustomEnchantment {
id: self.id,
description: self.description,
max_level: self.max_level,
anvil_cost: self.anvil_cost,
supported_items: self.supported_items,
weight: self.weight,
slots: self.slots,
exclusive_set: self.exclusive_set,
})
}
/// Registers this custom enchantment directly with the provided [`EnchantmentManager`].
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
pub fn register(self, manager: &EnchantmentManager) -> Result<(), EnchantmentError> {
manager.register(self)
}
/// Registers this custom enchantment with the server.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
pub fn register_to_server(self, server: &Server) -> Result<(), EnchantmentError> {
let manager = server.get_enchantment_manager();
manager.register(self)
}
/// Registers this custom enchantment with the plugin context.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
pub fn register_to_context(self, context: &Context) -> Result<(), EnchantmentError> {
let manager = context.get_enchantment_manager();
manager.register(self)
}
}
/// Trait for enchantment types that can be registered with an [`EnchantmentManager`].
pub trait RegistrableEnchantment {
/// Registers this enchantment with the provided enchantment manager.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
fn register(self, manager: &EnchantmentManager) -> Result<(), EnchantmentError>;
}
impl RegistrableEnchantment for EnchantmentBuilder {
fn register(self, manager: &EnchantmentManager) -> Result<(), EnchantmentError> {
let enchantment = self.build()?;
manager
.register_enchantment(enchantment)
.map_err(EnchantmentError::RegistrationFailed)
}
}
impl RegistrableEnchantment for CustomEnchantment {
fn register(self, manager: &EnchantmentManager) -> Result<(), EnchantmentError> {
manager
.register_enchantment(self)
.map_err(EnchantmentError::RegistrationFailed)
}
}
impl EnchantmentManager {
/// Registers a custom enchantment with the server.
///
/// Accepts [`EnchantmentBuilder`] or a [`CustomEnchantment`].
///
/// # Errors
/// Returns [`EnchantmentError`] if registration or validation fails.
pub fn register(
&self,
enchantment: impl RegistrableEnchantment,
) -> Result<(), EnchantmentError> {
enchantment.register(self)
}
/// Gets an enchantment definition by its ID (custom or vanilla).
#[must_use]
pub fn get(&self, id: &str) -> Option<CustomEnchantment> {
self.get_enchantment(id)
}
/// Checks if an enchantment ID is registered on the server.
#[must_use]
pub fn has(&self, id: &str) -> bool {
self.has_enchantment(id)
}
/// Returns all registered enchantment IDs (custom and vanilla).
#[must_use]
pub fn get_all_ids(&self) -> Vec<String> {
self.get_all_enchantment_ids()
}
}
impl Context {
/// Returns the global enchantment manager for registering and querying custom enchantments.
#[must_use]
pub fn get_enchantment_manager(&self) -> EnchantmentManager {
self.get_server().get_enchantment_manager()
}
/// Registers a custom enchantment with the server.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
pub fn register_enchantment(
&self,
enchantment: impl RegistrableEnchantment,
) -> Result<(), EnchantmentError> {
self.get_enchantment_manager().register(enchantment)
}
/// Gets an enchantment definition by its ID.
#[must_use]
pub fn get_enchantment(&self, id: &str) -> Option<CustomEnchantment> {
self.get_server().get_enchantment(id)
}
}
impl Server {
/// Registers a custom enchantment with the server.
///
/// # Errors
/// Returns [`EnchantmentError`] if validation or registration fails.
pub fn register_enchantment(
&self,
enchantment: impl RegistrableEnchantment,
) -> Result<(), EnchantmentError> {
self.get_enchantment_manager().register(enchantment)
}
}
/// Converts a positive integer to a standard Roman numeral representation (e.g. `1 -> "I"`, `3 -> "III"`, `5 -> "V"`).
#[must_use]
pub fn to_roman_numeral(level: u32) -> String {
match level {
1 => "I".into(),
2 => "II".into(),
3 => "III".into(),
4 => "IV".into(),
5 => "V".into(),
6 => "VI".into(),
7 => "VII".into(),
8 => "VIII".into(),
9 => "IX".into(),
10 => "X".into(),
_ => level.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enchantment_builder_defaults() {
let dummy_text: TextComponent = unsafe { std::mem::zeroed() };
let builder = EnchantmentBuilder::new("custom:freeze", dummy_text);
assert_eq!(builder.max_level, 1);
assert_eq!(builder.anvil_cost, 4);
assert_eq!(builder.weight, 5);
assert_eq!(builder.slots, vec![AttributeModifierSlot::MainHand]);
std::mem::forget(builder);
}
#[test]
fn enchantment_builder_validation() {
let dummy_text: TextComponent = unsafe { std::mem::zeroed() };
let builder = EnchantmentBuilder::new("", dummy_text);
assert_eq!(builder.validate(), Err(EnchantmentError::EmptyId));
std::mem::forget(builder);
let dummy_text: TextComponent = unsafe { std::mem::zeroed() };
let builder = EnchantmentBuilder::new("custom:poison", dummy_text).max_level(0);
assert_eq!(builder.validate(), Err(EnchantmentError::InvalidMaxLevel));
std::mem::forget(builder);
}
#[test]
fn roman_numerals() {
assert_eq!(to_roman_numeral(1), "I");
assert_eq!(to_roman_numeral(2), "II");
assert_eq!(to_roman_numeral(3), "III");
assert_eq!(to_roman_numeral(4), "IV");
assert_eq!(to_roman_numeral(5), "V");
assert_eq!(to_roman_numeral(10), "X");
assert_eq!(to_roman_numeral(255), "255");
}
}

View File

@@ -0,0 +1,39 @@
use crate::wit::pumpkin::plugin::advancement::{AdvancementProgress, FrameType};
impl AdvancementProgress {
/// Returns `true` if the advancement is fully completed.
#[must_use]
pub const fn is_done(&self) -> bool {
self.done
}
/// Checks if a specific criterion has been awarded.
#[must_use]
pub fn is_criterion_done(&self, criterion: &str) -> bool {
self.awarded_criteria.iter().any(|c| c == criterion)
}
/// Returns a slice of the awarded criteria names.
#[must_use]
pub fn get_awarded_criteria(&self) -> &[String] {
&self.awarded_criteria
}
/// Returns a slice of the remaining criteria names.
#[must_use]
pub fn get_remaining_criteria(&self) -> &[String] {
&self.remaining_criteria
}
}
impl FrameType {
/// Returns the vanilla translation key suffix or identifier for this frame type.
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Task => "task",
Self::Challenge => "challenge",
Self::Goal => "goal",
}
}
}

View File

@@ -0,0 +1,30 @@
use crate::wit::pumpkin::plugin::game_rules::{GameRule, GameRuleValue};
use crate::wit::pumpkin::plugin::world::World;
impl World {
/// Gets a boolean game rule value from this world. Returns `None` if the rule is an integer rule.
pub fn get_game_rule_bool(&self, rule: GameRule) -> Option<bool> {
match self.get_game_rule(rule) {
GameRuleValue::Bool(v) => Some(v),
GameRuleValue::Int(_) => None,
}
}
/// Gets an integer game rule value from this world. Returns `None` if the rule is a boolean rule.
pub fn get_game_rule_int(&self, rule: GameRule) -> Option<i32> {
match self.get_game_rule(rule) {
GameRuleValue::Int(v) => Some(v),
GameRuleValue::Bool(_) => None,
}
}
/// Sets a boolean game rule value for this world.
pub fn set_game_rule_bool(&self, rule: GameRule, value: bool) {
self.set_game_rule(rule, GameRuleValue::Bool(value));
}
/// Sets an integer game rule value for this world.
pub fn set_game_rule_int(&self, rule: GameRule, value: i32) {
self.set_game_rule(rule, GameRuleValue::Int(value));
}
}

View File

@@ -1,6 +1,8 @@
//! WIT-generated plugin type extensions belong in here. For example [Display](std::fmt::Display) implementations.
mod advancement;
mod attributes;
mod player;
mod game_rules;
pub mod player;
mod server_list_ping;
mod uuid;

View File

@@ -1,9 +1,33 @@
use crate::wit::pumpkin::plugin::item_stack::ItemStack;
use crate::wit::pumpkin::plugin::player::{
BanIpOptions, BanPlayerOptions, BedrockDisconnectReason, BedrockKickOptions, JavaKickOptions,
SocketTeardownPolicy,
Player, SocketTeardownPolicy,
};
use crate::wit::pumpkin::plugin::text::TextComponent;
/// Extension trait providing batch access and helper utilities for player ender chest inventories.
pub trait PlayerEnderChestExt {
/// Returns all 27 slots of the player's ender chest.
fn get_all_ender_chest_items(&self) -> Vec<Option<ItemStack>>;
/// Sets all 27 slots of the player's ender chest from an iterator.
fn set_all_ender_chest_items(&self, items: impl IntoIterator<Item = Option<ItemStack>>);
}
impl PlayerEnderChestExt for Player {
fn get_all_ender_chest_items(&self) -> Vec<Option<ItemStack>> {
(0..27)
.map(|slot| self.get_ender_chest_item(slot))
.collect()
}
fn set_all_ender_chest_items(&self, items: impl IntoIterator<Item = Option<ItemStack>>) {
for (slot, item) in items.into_iter().take(27).enumerate() {
self.set_ender_chest_item(slot as u8, item);
}
}
}
impl JavaKickOptions {
/// Creates a new `JavaKickOptions` with the given reason and default settings.
#[must_use]
@@ -108,3 +132,20 @@ impl BanIpOptions {
}
}
}
#[cfg(test)]
mod tests {
use crate::{CustomStatistic, StatisticCategory};
#[test]
fn statistic_types() {
assert_eq!(StatisticCategory::Mined as u8, 0);
assert_eq!(StatisticCategory::Crafted as u8, 1);
assert_eq!(StatisticCategory::Custom as u8, 8);
assert_eq!(CustomStatistic::LeaveGame as u8, 0);
assert_eq!(CustomStatistic::PlayTime as u8, 1);
assert_eq!(CustomStatistic::Deaths as u8, 32);
assert_eq!(CustomStatistic::PlayerKills as u8, 35);
}
}

View File

@@ -73,6 +73,10 @@ use crate::{
/// Plugin command registration and handling utilities.
pub mod commands;
/// Display and interaction entity utilities and builders.
pub mod display;
/// Custom enchantment registration and builder utilities.
pub mod enchantment;
/// Event system and event handlers.
pub mod events;
mod ext;
@@ -82,8 +86,14 @@ pub mod forms;
///
/// Use these in your `PluginMetadata` to request access to specific host features.
pub mod permissions;
/// Custom recipe registration and builder utilities.
pub mod recipe;
/// Scheduler utilities.
pub mod scheduler;
/// Scoreboard team management and builder utilities.
pub mod team;
/// Custom world and chunk generation utilities and traits.
pub mod worldgen;
/// Command WIT API re-exports.
pub mod command {
@@ -94,19 +104,53 @@ pub mod command {
}
pub use wit::pumpkin::plugin::{
bedrock_packets, block_entity, boss_bar, command as command_wit, common,
advancement as advancement_wit, bedrock_packets, block_entity, boss_bar,
command as command_wit, common,
context::{Context, Server},
data_components, entity,
damage_types as damage_types_wit, data_components, display as display_wit,
enchantments as enchantments_wit, entity,
entity_types::EntityType,
event::{self as events_wit, EventType},
gui, i18n, ipc, item_stack, java_dialogs, java_packets, particles, permission, player,
scoreboard, server, text, uuid, world,
recipe as recipe_wit, scoreboard, screens as screens_wit, server, statistics as statistics_wit,
text, uuid, world,
};
// Convenience re-exports of commonly-used plugin types so plugin authors can
// name them directly (e.g. build an `ItemStack` for a GUI or `/give`).
pub use damage_types_wit::DamageType;
pub use display::{
BillboardMode, BlockDisplayEntity, DisplayEntity, DisplayEntityExt, DisplayTransformation,
EntityDisplayExt, InteractionEntity, ItemDisplayEntity, ItemDisplayEntityExt, ItemDisplayMode,
Quaternionf, TextAlignment, TextDisplayEntity, TextDisplayEntityExt, TransformationBuilder,
Vector3f,
};
pub use enchantment::{
AttributeModifierSlot, CustomEnchantment, CustomEnchantmentValue, Enchantment,
EnchantmentBuilder, EnchantmentError, EnchantmentManager, RegistrableEnchantment,
};
pub use events::{EventHandler, FromIntoEvent};
pub use ext::player::PlayerEnderChestExt;
pub use recipe::{
CookingRecipeBuilder, Ingredient, RecipeCategory, RecipeError, RecipeManager,
RegistrableRecipe, ShapedRecipeBuilder, ShapelessRecipeBuilder,
};
pub use screens_wit::Screen;
pub use statistics_wit::{CustomStatistic, StatisticCategory};
pub use team::{PlayerTeamExt, ScoreboardTeamExt, Team, TeamSettingsBuilder};
pub use wit::pumpkin::plugin::item_stack::ItemStack;
pub use wit::pumpkin::plugin::player::Player;
pub use wit::pumpkin::plugin::scoreboard::{CollisionRule, NametagVisibility, TeamSettings};
pub use wit::pumpkin::plugin::server::Dimension;
pub use wit::pumpkin::plugin::world::World;
pub use worldgen::{ChunkBuffer, ChunkGenerator, GenerationPhase, GeneratorManager};
/// Advancement WIT API re-exports.
pub mod advancement {
pub use crate::wit::pumpkin::plugin::advancement::{
AdvancementDisplay, AdvancementInfo, AdvancementProgress, FrameType,
};
}
/// Java dialog WIT API re-exports.
pub mod java_dialog {
@@ -270,6 +314,33 @@ impl wit::Guest for Component {
) -> Result<wit::IpcMessage, String> {
plugin().handle_ipc_message(sender, message)
}
fn handle_generate_phase(
generator_id: u32,
phase: wit::pumpkin::plugin::world::GenerationPhase,
chunk: wit::pumpkin::plugin::world::ChunkBuffer,
) {
let handlers = crate::worldgen::GENERATOR_HANDLERS
.lock()
.unwrap_or_else(|e| e.into_inner());
if let Some(generator) = handlers.get(&generator_id) {
let mut buffer = crate::worldgen::ChunkBuffer::new(chunk);
match phase {
wit::pumpkin::plugin::world::GenerationPhase::Biomes => {
generator.generate_biomes(&mut buffer);
}
wit::pumpkin::plugin::world::GenerationPhase::Noise => {
generator.generate_noise(&mut buffer);
}
wit::pumpkin::plugin::world::GenerationPhase::Surface => {
generator.generate_surface(&mut buffer);
}
wit::pumpkin::plugin::world::GenerationPhase::Features => {
generator.generate_features(&mut buffer);
}
}
}
}
}
/// Convenience alias for `core::result::Result<T, String>` used throughout the plugin API.
@@ -354,3 +425,8 @@ macro_rules! register_plugin {
}
/// AI and mob goal utilities.
pub mod ai;
/// Persistent custom data containers (Bukkit-style `PersistentDataHolder`).
pub mod persistent_data;
pub use persistent_data::PersistentDataHolder;
/// Game rules definitions and values.
pub use wit::pumpkin::plugin::game_rules::{GameRule, GameRuleValue};

View File

@@ -0,0 +1,477 @@
//! Persistent custom data container API (Bukkit-style `PersistentDataHolder`).
//!
//! Provides namespaced persistent data storage on entities, block entities, chunks,
//! worlds, and item stacks that is automatically persisted to disk in NBT format.
use crate::wit::pumpkin::plugin::block_entity::BlockEntity;
use crate::wit::pumpkin::plugin::common::{NbtTag, NbtTree};
use crate::wit::pumpkin::plugin::item_stack::ItemStack;
use crate::wit::pumpkin::plugin::player::Player;
use crate::wit::pumpkin::plugin::world::{Chunk, Entity, World};
/// Constructs an `NbtTree` containing a single string value.
pub fn string_tree(val: &str) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::StringTag(val.to_string())],
}
}
/// Constructs an `NbtTree` containing a single 32-bit integer value.
pub fn int_tree(val: i32) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Int(val)],
}
}
/// Constructs an `NbtTree` containing a single 64-bit integer value.
pub fn long_tree(val: i64) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Long(val)],
}
}
/// Constructs an `NbtTree` containing a single 16-bit short value.
pub fn short_tree(val: i16) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Short(val)],
}
}
/// Constructs an `NbtTree` containing a single byte (8-bit) value.
pub fn byte_tree(val: i8) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Byte(val)],
}
}
/// Constructs an `NbtTree` containing a single boolean value.
pub fn bool_tree(val: bool) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Byte(if val { 1 } else { 0 })],
}
}
/// Constructs an `NbtTree` containing a single 32-bit float value.
pub fn float_tree(val: f32) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Float(val)],
}
}
/// Constructs an `NbtTree` containing a single 64-bit double value.
pub fn double_tree(val: f64) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::Double(val)],
}
}
/// Constructs an `NbtTree` containing a byte array value.
pub fn byte_array_tree(val: Vec<i8>) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::ByteArray(val)],
}
}
/// Constructs an `NbtTree` containing an integer array value.
pub fn int_array_tree(val: Vec<i32>) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::IntArray(val)],
}
}
/// Constructs an `NbtTree` containing a long array value.
pub fn long_array_tree(val: Vec<i64>) -> NbtTree {
NbtTree {
root: 0,
tags: vec![NbtTag::LongArray(val)],
}
}
/// Trait for objects that can hold persistent, namespaced custom NBT data.
///
/// Analogous to Bukkit's `PersistentDataHolder` interface.
pub trait PersistentDataHolder {
/// Sets a raw NBT tree under the specified namespace and key.
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree);
/// Gets a raw NBT tree under the specified namespace and key, if present.
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree>;
/// Removes custom data stored under the specified namespace and key.
fn remove_custom_data(&self, namespace: &str, key: &str);
/// Returns `true` if custom data is stored under the specified namespace and key.
fn has_custom_data(&self, namespace: &str, key: &str) -> bool;
/// Sets a string value.
fn set_string(&self, namespace: &str, key: &str, value: &str) {
self.set_custom_data(namespace, key, &string_tree(value));
}
/// Gets a string value.
fn get_string(&self, namespace: &str, key: &str) -> Option<String> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::StringTag(s) => Some(s.clone()),
_ => None,
}
}
/// Sets a 32-bit integer value.
fn set_int(&self, namespace: &str, key: &str, value: i32) {
self.set_custom_data(namespace, key, &int_tree(value));
}
/// Gets a 32-bit integer value.
fn get_int(&self, namespace: &str, key: &str) -> Option<i32> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Int(v) => Some(*v),
NbtTag::Byte(v) => Some(i32::from(*v)),
NbtTag::Short(v) => Some(i32::from(*v)),
_ => None,
}
}
/// Sets a 64-bit integer value.
fn set_long(&self, namespace: &str, key: &str, value: i64) {
self.set_custom_data(namespace, key, &long_tree(value));
}
/// Gets a 64-bit integer value.
fn get_long(&self, namespace: &str, key: &str) -> Option<i64> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Long(v) => Some(*v),
NbtTag::Int(v) => Some(i64::from(*v)),
NbtTag::Byte(v) => Some(i64::from(*v)),
NbtTag::Short(v) => Some(i64::from(*v)),
_ => None,
}
}
/// Sets a 16-bit integer value.
fn set_short(&self, namespace: &str, key: &str, value: i16) {
self.set_custom_data(namespace, key, &short_tree(value));
}
/// Gets a 16-bit integer value.
fn get_short(&self, namespace: &str, key: &str) -> Option<i16> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Short(v) => Some(*v),
NbtTag::Byte(v) => Some(i16::from(*v)),
_ => None,
}
}
/// Sets an 8-bit byte value.
fn set_byte(&self, namespace: &str, key: &str, value: i8) {
self.set_custom_data(namespace, key, &byte_tree(value));
}
/// Gets an 8-bit byte value.
fn get_byte(&self, namespace: &str, key: &str) -> Option<i8> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Byte(v) => Some(*v),
_ => None,
}
}
/// Sets a boolean value (stored as an NBT byte: 1 or 0).
fn set_bool(&self, namespace: &str, key: &str, value: bool) {
self.set_custom_data(namespace, key, &bool_tree(value));
}
/// Gets a boolean value.
fn get_bool(&self, namespace: &str, key: &str) -> Option<bool> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Byte(v) => Some(*v != 0),
_ => None,
}
}
/// Sets a 32-bit float value.
fn set_float(&self, namespace: &str, key: &str, value: f32) {
self.set_custom_data(namespace, key, &float_tree(value));
}
/// Gets a 32-bit float value.
fn get_float(&self, namespace: &str, key: &str) -> Option<f32> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Float(v) => Some(*v),
_ => None,
}
}
/// Sets a 64-bit double value.
fn set_double(&self, namespace: &str, key: &str, value: f64) {
self.set_custom_data(namespace, key, &double_tree(value));
}
/// Gets a 64-bit double value.
fn get_double(&self, namespace: &str, key: &str) -> Option<f64> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::Double(v) => Some(*v),
NbtTag::Float(v) => Some(f64::from(*v)),
_ => None,
}
}
/// Sets a byte array value.
fn set_byte_array(&self, namespace: &str, key: &str, value: Vec<i8>) {
self.set_custom_data(namespace, key, &byte_array_tree(value));
}
/// Gets a byte array value.
fn get_byte_array(&self, namespace: &str, key: &str) -> Option<Vec<i8>> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::ByteArray(v) => Some(v.clone()),
_ => None,
}
}
/// Sets an integer array value.
fn set_int_array(&self, namespace: &str, key: &str, value: Vec<i32>) {
self.set_custom_data(namespace, key, &int_array_tree(value));
}
/// Gets an integer array value.
fn get_int_array(&self, namespace: &str, key: &str) -> Option<Vec<i32>> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::IntArray(v) => Some(v.clone()),
_ => None,
}
}
/// Sets a long array value.
fn set_long_array(&self, namespace: &str, key: &str, value: Vec<i64>) {
self.set_custom_data(namespace, key, &long_array_tree(value));
}
/// Gets a long array value.
fn get_long_array(&self, namespace: &str, key: &str) -> Option<Vec<i64>> {
let tree = self.get_custom_data(namespace, key)?;
match tree.tags.get(tree.root as usize)? {
NbtTag::LongArray(v) => Some(v.clone()),
_ => None,
}
}
}
impl PersistentDataHolder for ItemStack {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.has_custom_data(namespace, key)
}
}
impl PersistentDataHolder for Entity {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.has_custom_data(namespace, key)
}
}
impl PersistentDataHolder for BlockEntity {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.has_custom_data(namespace, key)
}
}
impl PersistentDataHolder for Chunk {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.has_custom_data(namespace, key)
}
}
impl PersistentDataHolder for World {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.has_custom_data(namespace, key)
}
}
impl PersistentDataHolder for Player {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.as_entity().set_custom_data(namespace, key, value);
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.as_entity().get_custom_data(namespace, key)
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.as_entity().remove_custom_data(namespace, key);
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.as_entity().has_custom_data(namespace, key)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::collections::HashMap;
struct MockHolder {
data: RefCell<HashMap<(String, String), NbtTree>>,
}
impl MockHolder {
fn new() -> Self {
Self {
data: RefCell::new(HashMap::new()),
}
}
}
impl PersistentDataHolder for MockHolder {
fn set_custom_data(&self, namespace: &str, key: &str, value: &NbtTree) {
self.data
.borrow_mut()
.insert((namespace.to_string(), key.to_string()), value.clone());
}
fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTree> {
self.data
.borrow()
.get(&(namespace.to_string(), key.to_string()))
.cloned()
}
fn remove_custom_data(&self, namespace: &str, key: &str) {
self.data
.borrow_mut()
.remove(&(namespace.to_string(), key.to_string()));
}
fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.data
.borrow()
.contains_key(&(namespace.to_string(), key.to_string()))
}
}
#[test]
fn persistent_data_typed_methods() {
let holder = MockHolder::new();
// String
holder.set_string("my_mod", "greeting", "hello world");
assert!(holder.has_custom_data("my_mod", "greeting"));
assert_eq!(
holder.get_string("my_mod", "greeting"),
Some("hello world".to_string())
);
// Int
holder.set_int("my_mod", "score", 9001);
assert_eq!(holder.get_int("my_mod", "score"), Some(9001));
// Long
holder.set_long("my_mod", "large_id", 123_456_789_012);
assert_eq!(holder.get_long("my_mod", "large_id"), Some(123_456_789_012));
// Bool
holder.set_bool("my_mod", "is_admin", true);
assert_eq!(holder.get_bool("my_mod", "is_admin"), Some(true));
// Float & Double
holder.set_float("my_mod", "multiplier", 1.5);
assert_eq!(holder.get_float("my_mod", "multiplier"), Some(1.5));
holder.set_double("my_mod", "precise", std::f64::consts::PI);
assert_eq!(
holder.get_double("my_mod", "precise"),
Some(std::f64::consts::PI)
);
// Byte array
holder.set_byte_array("my_mod", "raw_bytes", vec![1, 2, 3, 4]);
assert_eq!(
holder.get_byte_array("my_mod", "raw_bytes"),
Some(vec![1, 2, 3, 4])
);
// Remove
holder.remove_custom_data("my_mod", "greeting");
assert!(!holder.has_custom_data("my_mod", "greeting"));
assert_eq!(holder.get_string("my_mod", "greeting"), None);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,371 @@
use crate::wit::pumpkin::plugin::common::NamedColor;
use crate::wit::pumpkin::plugin::player::Player;
use crate::wit::pumpkin::plugin::scoreboard::{
CollisionRule, NametagVisibility, Scoreboard, TeamSettings,
};
use crate::wit::pumpkin::plugin::text::TextComponent;
/// Builder for constructing [`TeamSettings`].
pub struct TeamSettingsBuilder {
display_name: Option<TextComponent>,
friendly_fire: bool,
see_friendly_invisibles: bool,
nametag_visibility: NametagVisibility,
collision_rule: CollisionRule,
color: NamedColor,
prefix: Option<TextComponent>,
suffix: Option<TextComponent>,
}
impl Default for TeamSettingsBuilder {
fn default() -> Self {
Self::new()
}
}
impl TeamSettingsBuilder {
/// Creates a new `TeamSettingsBuilder` with default settings.
#[must_use]
pub fn new() -> Self {
Self {
display_name: None,
friendly_fire: true,
see_friendly_invisibles: false,
nametag_visibility: NametagVisibility::Always,
collision_rule: CollisionRule::Always,
color: NamedColor::White,
prefix: None,
suffix: None,
}
}
/// Sets the team's display name.
#[must_use]
pub fn display_name(mut self, name: impl Into<TextComponent>) -> Self {
self.display_name = Some(name.into());
self
}
/// Sets whether friendly fire is enabled for members of this team.
#[must_use]
pub fn friendly_fire(mut self, allow: bool) -> Self {
self.friendly_fire = allow;
self
}
/// Sets whether teammates can see invisible friendly players.
#[must_use]
pub fn see_friendly_invisibles(mut self, see: bool) -> Self {
self.see_friendly_invisibles = see;
self
}
/// Sets nametag visibility for this team.
#[must_use]
pub fn nametag_visibility(mut self, vis: NametagVisibility) -> Self {
self.nametag_visibility = vis;
self
}
/// Sets the collision rule for members of this team.
#[must_use]
pub fn collision_rule(mut self, rule: CollisionRule) -> Self {
self.collision_rule = rule;
self
}
/// Sets the display and glowing color for this team.
#[must_use]
pub fn color(mut self, color: NamedColor) -> Self {
self.color = color;
self
}
/// Sets the player prefix shown before member names.
#[must_use]
pub fn prefix(mut self, prefix: impl Into<TextComponent>) -> Self {
self.prefix = Some(prefix.into());
self
}
/// Sets the player suffix shown after member names.
#[must_use]
pub fn suffix(mut self, suffix: impl Into<TextComponent>) -> Self {
self.suffix = Some(suffix.into());
self
}
/// Builds the [`TeamSettings`].
#[must_use]
pub fn build(self) -> TeamSettings {
TeamSettings {
display_name: self.display_name.unwrap_or_else(|| TextComponent::text("")),
friendly_fire: self.friendly_fire,
see_friendly_invisibles: self.see_friendly_invisibles,
nametag_visibility: self.nametag_visibility,
collision_rule: self.collision_rule,
color: self.color,
prefix: self.prefix.unwrap_or_else(|| TextComponent::text("")),
suffix: self.suffix.unwrap_or_else(|| TextComponent::text("")),
}
}
}
/// A high-level representation of a scoreboard team.
pub struct Team<'a> {
scoreboard: &'a Scoreboard,
name: String,
}
impl<'a> Team<'a> {
/// Creates a handle to a team on the given scoreboard.
#[must_use]
pub fn new(scoreboard: &'a Scoreboard, name: impl Into<String>) -> Self {
Self {
scoreboard,
name: name.into(),
}
}
/// Returns the team's internal identifier name.
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
/// Gets the current settings for this team, if it exists on the scoreboard.
#[must_use]
pub fn get_settings(&self) -> Option<TeamSettings> {
self.scoreboard.get_team(&self.name)
}
/// Updates the settings for this team on the scoreboard.
pub fn update_settings(&self, settings: TeamSettings) {
self.scoreboard.update_team(&self.name, settings);
}
/// Gets the display name of this team.
#[must_use]
pub fn display_name(&self) -> Option<TextComponent> {
self.get_settings().map(|s| s.display_name)
}
/// Sets the display name of this team.
pub fn set_display_name(&self, name: TextComponent) {
if let Some(mut s) = self.get_settings() {
s.display_name = name;
self.update_settings(s);
}
}
/// Gets the prefix of this team.
#[must_use]
pub fn prefix(&self) -> Option<TextComponent> {
self.get_settings().map(|s| s.prefix)
}
/// Sets the prefix of this team.
pub fn set_prefix(&self, prefix: TextComponent) {
if let Some(mut s) = self.get_settings() {
s.prefix = prefix;
self.update_settings(s);
}
}
/// Gets the suffix of this team.
#[must_use]
pub fn suffix(&self) -> Option<TextComponent> {
self.get_settings().map(|s| s.suffix)
}
/// Sets the suffix of this team.
pub fn set_suffix(&self, suffix: TextComponent) {
if let Some(mut s) = self.get_settings() {
s.suffix = suffix;
self.update_settings(s);
}
}
/// Gets the team color.
#[must_use]
pub fn color(&self) -> Option<NamedColor> {
self.get_settings().map(|s| s.color)
}
/// Sets the team color.
pub fn set_color(&self, color: NamedColor) {
if let Some(mut s) = self.get_settings() {
s.color = color;
self.update_settings(s);
}
}
/// Gets whether friendly fire is enabled.
#[must_use]
pub fn allow_friendly_fire(&self) -> bool {
self.get_settings().map_or(true, |s| s.friendly_fire)
}
/// Sets whether friendly fire is enabled.
pub fn set_allow_friendly_fire(&self, allow: bool) {
if let Some(mut s) = self.get_settings() {
s.friendly_fire = allow;
self.update_settings(s);
}
}
/// Gets whether teammates can see friendly invisible players.
#[must_use]
pub fn can_see_friendly_invisibles(&self) -> bool {
self.get_settings()
.map_or(false, |s| s.see_friendly_invisibles)
}
/// Sets whether teammates can see friendly invisible players.
pub fn set_can_see_friendly_invisibles(&self, see: bool) {
if let Some(mut s) = self.get_settings() {
s.see_friendly_invisibles = see;
self.update_settings(s);
}
}
/// Gets nametag visibility for this team.
#[must_use]
pub fn nametag_visibility(&self) -> Option<NametagVisibility> {
self.get_settings().map(|s| s.nametag_visibility)
}
/// Sets nametag visibility for this team.
pub fn set_nametag_visibility(&self, vis: NametagVisibility) {
if let Some(mut s) = self.get_settings() {
s.nametag_visibility = vis;
self.update_settings(s);
}
}
/// Gets the collision rule for this team.
#[must_use]
pub fn collision_rule(&self) -> Option<CollisionRule> {
self.get_settings().map(|s| s.collision_rule)
}
/// Sets the collision rule for this team.
pub fn set_collision_rule(&self, rule: CollisionRule) {
if let Some(mut s) = self.get_settings() {
s.collision_rule = rule;
self.update_settings(s);
}
}
/// Returns a list of all player / entity names in this team.
#[must_use]
pub fn get_players(&self) -> Vec<String> {
self.scoreboard.get_team_players(&self.name)
}
/// Adds a player or entity name to this team.
pub fn add_player(&self, player_name: &str) {
self.scoreboard.add_player_to_team(&self.name, player_name);
}
/// Removes a player or entity name from this team.
pub fn remove_player(&self, player_name: &str) {
self.scoreboard
.remove_player_from_team(&self.name, player_name);
}
/// Checks if a player or entity name is in this team.
#[must_use]
pub fn has_player(&self, player_name: &str) -> bool {
self.get_players().iter().any(|p| p == player_name)
}
/// Removes all members from this team.
pub fn clear_players(&self) {
self.scoreboard.clear_team_players(&self.name);
}
/// Removes this team from the scoreboard.
pub fn unregister(self) {
self.scoreboard.remove_team(&self.name);
}
}
/// Extension trait for [`Scoreboard`] providing team operations.
pub trait ScoreboardTeamExt {
/// Registers and creates a new team on the scoreboard.
fn register_new_team(&self, name: &str, settings: TeamSettings) -> Team<'_>;
/// Gets a team by name, if it exists on the scoreboard.
fn get_team_handle(&self, name: &str) -> Option<Team<'_>>;
/// Returns all teams on the scoreboard.
fn get_all_teams(&self) -> Vec<Team<'_>>;
/// Gets the team that a player belongs to, if any.
fn get_player_team_handle(&self, player_name: &str) -> Option<Team<'_>>;
}
impl ScoreboardTeamExt for Scoreboard {
fn register_new_team(&self, name: &str, settings: TeamSettings) -> Team<'_> {
self.create_team(name, settings);
Team::new(self, name)
}
fn get_team_handle(&self, name: &str) -> Option<Team<'_>> {
self.get_team(name).is_some().then(|| Team::new(self, name))
}
fn get_all_teams(&self) -> Vec<Team<'_>> {
self.get_teams()
.into_iter()
.map(|name| Team::new(self, name))
.collect()
}
fn get_player_team_handle(&self, player_name: &str) -> Option<Team<'_>> {
self.get_player_team(player_name)
.map(|name| Team::new(self, name))
}
}
/// Extension trait for [`Player`] team operations.
pub trait PlayerTeamExt {
/// Gets the active team name for this player, if any.
fn get_team_name(&self) -> Option<String>;
}
impl PlayerTeamExt for Player {
fn get_team_name(&self) -> Option<String> {
self.get_team()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn team_settings_builder_defaults() {
let dummy_text: TextComponent = unsafe { std::mem::zeroed() };
let dummy_text2: TextComponent = unsafe { std::mem::zeroed() };
let dummy_text3: TextComponent = unsafe { std::mem::zeroed() };
let settings = TeamSettingsBuilder::new()
.display_name(dummy_text)
.color(NamedColor::Red)
.prefix(dummy_text2)
.suffix(dummy_text3)
.friendly_fire(false)
.see_friendly_invisibles(true)
.nametag_visibility(NametagVisibility::HideForOtherTeams)
.collision_rule(CollisionRule::PushOwnTeam)
.build();
assert!(!settings.friendly_fire);
assert!(settings.see_friendly_invisibles);
assert_eq!(settings.color, NamedColor::Red);
assert_eq!(
settings.nametag_visibility,
NametagVisibility::HideForOtherTeams
);
assert_eq!(settings.collision_rule, CollisionRule::PushOwnTeam);
std::mem::forget(settings);
}
}

View File

@@ -0,0 +1,129 @@
use crate::wit::pumpkin::plugin::biomes::Biome;
use crate::wit::pumpkin::plugin::world::ChunkBuffer as WitChunkBuffer;
use std::collections::BTreeMap;
use std::sync::Mutex;
pub use crate::wit::pumpkin::plugin::biomes::Biome as PluginBiome;
pub use crate::wit::pumpkin::plugin::world::GenerationPhase;
/// Wrapper around the WIT `chunk-buffer` resource representing a 16x16 chunk column
/// being generated.
pub struct ChunkBuffer {
inner: WitChunkBuffer,
}
impl ChunkBuffer {
/// Creates a new `ChunkBuffer` wrapper.
#[must_use]
pub const fn new(inner: WitChunkBuffer) -> Self {
Self { inner }
}
/// Returns the chunk X coordinate.
#[must_use]
pub fn x(&self) -> i32 {
self.inner.get_x()
}
/// Returns the chunk Z coordinate.
#[must_use]
pub fn z(&self) -> i32 {
self.inner.get_z()
}
/// Returns the minimum Y coordinate of the world.
#[must_use]
pub fn min_y(&self) -> i32 {
self.inner.get_min_y()
}
/// Returns the height of the chunk column in blocks.
#[must_use]
pub fn height(&self) -> u32 {
self.inner.get_height()
}
/// Sets the block state ID at local chunk coordinates `(x, y, z)` where `0 <= x < 16` and `0 <= z < 16`.
pub fn set_block(&mut self, x: u8, y: i32, z: u8, state_id: u16) {
self.inner.set_block_state_id(x, y, z, state_id);
}
/// Gets the block state ID at local chunk coordinates `(x, y, z)`.
#[must_use]
pub fn get_block(&self, x: u8, y: i32, z: u8) -> u16 {
self.inner.get_block_state_id(x, y, z)
}
/// Fills an entire horizontal 16x16 layer at the given Y level with a block state ID.
pub fn fill_layer(&mut self, y: i32, state_id: u16) {
self.inner.fill_layer(y, state_id);
}
/// Fills a vertical column from `min_y` to `max_y` at local `(x, z)` with a block state ID.
pub fn fill_range(&mut self, x: u8, min_y: i32, max_y: i32, z: u8, state_id: u16) {
self.inner.fill_range(x, min_y, max_y, z, state_id);
}
/// Fills a 3D cuboid with a block state ID.
pub fn fill_cuboid(
&mut self,
min_x: u8,
min_y: i32,
min_z: u8,
max_x: u8,
max_y: i32,
max_z: u8,
state_id: u16,
) {
self.inner
.fill_cuboid(min_x, min_y, min_z, max_x, max_y, max_z, state_id);
}
/// Sets the biome at local chunk coordinates `(x, y, z)`.
pub fn set_biome(&mut self, x: u8, y: i32, z: u8, biome: Biome) {
self.inner.set_biome(x, y, z, biome);
}
/// Fills the entire chunk column with a single biome.
pub fn fill_biome(&mut self, biome: Biome) {
self.inner.fill_biome(biome);
}
}
/// Trait for implementing custom world generation logic in plugins.
#[allow(unused_variables)]
pub trait ChunkGenerator: Send + Sync + 'static {
/// Step 1: Assign biomes across the chunk column.
fn generate_biomes(&self, chunk: &mut ChunkBuffer) {}
/// Step 2: Generate basic terrain / noise shape into the chunk.
fn generate_noise(&self, chunk: &mut ChunkBuffer) {}
/// Step 3: Apply surface rules (e.g. grass, sand, stone layers).
fn generate_surface(&self, chunk: &mut ChunkBuffer) {}
/// Step 4: Populate chunk with features, structures, decorations, ores, etc.
fn generate_features(&self, chunk: &mut ChunkBuffer) {}
}
pub(crate) static GENERATOR_HANDLERS: Mutex<BTreeMap<u32, Box<dyn ChunkGenerator>>> =
Mutex::new(BTreeMap::new());
static NEXT_GENERATOR_ID: Mutex<u32> = Mutex::new(0);
/// Manager for registering custom chunk generators with the server runtime.
pub struct GeneratorManager;
impl GeneratorManager {
/// Registers a custom chunk generator and returns its unique generator ID.
///
/// You can then set this generator on a world using `world.set_chunk_generator(id)`.
pub fn register<G: ChunkGenerator>(generator: G) -> u32 {
let mut id_lock = NEXT_GENERATOR_ID.lock().unwrap_or_else(|e| e.into_inner());
let id = *id_lock;
*id_lock += 1;
let mut handlers = GENERATOR_HANDLERS.lock().unwrap_or_else(|e| e.into_inner());
handlers.insert(id, Box::new(generator));
id
}
}

View File

@@ -185,17 +185,9 @@ impl PacketWrite for CLevelChunk<'_> {
#[cfg(test)]
mod tests {
use std::io::Cursor;
use std::sync::{
Mutex,
atomic::{AtomicBool, AtomicU64},
};
use pumpkin_data::chunk::ChunkStatus;
use pumpkin_nbt::{Nbt, compound::NbtCompound, deserializer::NbtReadHelperBedrock};
use pumpkin_world::{
chunk::{ChunkData, ChunkHeightmaps, ChunkLight, ChunkSections},
tick::scheduler::ChunkTickScheduler,
};
use pumpkin_world::chunk::ChunkData;
use super::CLevelChunk;
use crate::serial::PacketWrite;
@@ -214,21 +206,7 @@ mod tests {
}
fn empty_chunk() -> ChunkData {
ChunkData {
section: ChunkSections::new(24, -64),
heightmap: Mutex::new(ChunkHeightmaps::default()),
x: 0,
z: 0,
block_ticks: ChunkTickScheduler::default(),
fluid_ticks: ChunkTickScheduler::default(),
pending_block_entities: Mutex::default(),
light_engine: Mutex::new(ChunkLight::default()),
light_populated: AtomicBool::new(false),
status: ChunkStatus::Full,
blending_data: None,
dirty: AtomicBool::new(false),
inhabited_time: AtomicU64::new(0),
}
ChunkData::empty(0, 0)
}
#[test]

View File

@@ -37,6 +37,7 @@ pub struct OwnedRecipeResult {
#[derive(Clone, Debug)]
pub enum OwnedCraftingRecipe {
Shaped {
recipe_id: Option<String>,
category: RecipeCategoryTypes,
group: Option<String>,
show_notification: bool,
@@ -45,6 +46,7 @@ pub enum OwnedCraftingRecipe {
result: OwnedRecipeResult,
},
Shapeless {
recipe_id: Option<String>,
category: RecipeCategoryTypes,
group: Option<String>,
ingredients: Vec<OwnedRecipeIngredient>,

View File

@@ -372,6 +372,12 @@ impl ChunkData {
_ => ChunkStatus::Empty,
};
let custom_data = root_tag
.get_compound("PumpkinCustomData")
.or_else(|| root_tag.get_compound("BukkitValues"))
.cloned()
.unwrap_or_default();
Ok(Self {
section,
heightmap: std::sync::Mutex::new(heightmaps),
@@ -387,6 +393,7 @@ impl ChunkData {
status,
blending_data: None,
inhabited_time: AtomicU64::new(root_tag.get_long("InhabitedTime").unwrap_or(0) as u64),
custom_data: std::sync::Mutex::new(custom_data),
})
}
@@ -556,9 +563,78 @@ impl ChunkData {
self.inhabited_time.load(Ordering::Relaxed) as i64,
);
let custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !custom_data.is_empty() {
root_compound.put_compound("PumpkinCustomData", custom_data.clone());
}
let nbt = pumpkin_nbt::Nbt::from(root_compound);
nbt.write()
}
pub fn set_custom_data(&self, namespace: &str, key: &str, value: pumpkin_nbt::tag::NbtTag) {
let mut custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut namespace_data = custom_data
.child_tags
.remove(namespace)
.and_then(|tag| match tag {
pumpkin_nbt::tag::NbtTag::Compound(compound) => Some(compound),
_ => None,
})
.unwrap_or_default();
namespace_data.child_tags.insert(key.into(), value);
custom_data.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
self.dirty.store(true, Ordering::Relaxed);
}
pub fn get_custom_data(&self, namespace: &str, key: &str) -> Option<pumpkin_nbt::tag::NbtTag> {
let custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
custom_data
.get(namespace)?
.extract_compound()?
.get(key)
.cloned()
}
pub fn remove_custom_data(&self, namespace: &str, key: &str) {
let mut custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let Some(pumpkin_nbt::tag::NbtTag::Compound(mut namespace_data)) =
custom_data.child_tags.remove(namespace)
else {
return;
};
namespace_data.child_tags.remove(key);
if !namespace_data.is_empty() {
custom_data.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
}
self.dirty.store(true, Ordering::Relaxed);
}
pub fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.get_custom_data(namespace, key).is_some()
}
}
impl PathFromLevelFolder for ChunkEntityData {

View File

@@ -82,6 +82,7 @@ pub struct ChunkData {
pub blending_data: Option<crate::generation::blender::blending_data::BlendingData>,
pub dirty: AtomicBool,
pub inhabited_time: AtomicU64,
pub custom_data: std::sync::Mutex<NbtCompound>,
}
pub struct ChunkEntityData {
@@ -614,6 +615,7 @@ impl ChunkData {
blending_data: None,
dirty: std::sync::atomic::AtomicBool::new(false),
inhabited_time: std::sync::atomic::AtomicU64::new(0),
custom_data: std::sync::Mutex::new(NbtCompound::new()),
}
}
@@ -915,4 +917,34 @@ mod tests {
assert!(!ChunkHeightmapType::MotionBlockingNoLeaves.is_opaque(leaves)); // Excludes leaves
assert!(ChunkHeightmapType::MotionBlockingNoLeaves.is_opaque(water)); // Water is liquid
}
#[test]
fn chunk_custom_data() {
use pumpkin_nbt::tag::NbtTag;
let chunk = super::ChunkData::empty(0, 0);
assert!(!chunk.has_custom_data("my_plugin", "test_key"));
assert_eq!(chunk.get_custom_data("my_plugin", "test_key"), None);
chunk.set_custom_data(
"my_plugin",
"test_key",
NbtTag::String("hello_pumpkin".into()),
);
assert!(chunk.has_custom_data("my_plugin", "test_key"));
assert_eq!(
chunk.get_custom_data("my_plugin", "test_key"),
Some(NbtTag::String("hello_pumpkin".into()))
);
chunk.set_custom_data("my_plugin", "number_key", NbtTag::Int(42));
assert_eq!(
chunk.get_custom_data("my_plugin", "number_key"),
Some(NbtTag::Int(42))
);
chunk.remove_custom_data("my_plugin", "test_key");
assert!(!chunk.has_custom_data("my_plugin", "test_key"));
assert!(chunk.has_custom_data("my_plugin", "number_key"));
}
}

View File

@@ -14,6 +14,7 @@ use crate::ProtoChunk;
use crate::level::SyncChunk;
use pumpkin_data::chunk::ChunkStatus;
use pumpkin_nbt::compound::NbtCompound;
use std::sync::Mutex;
#[repr(u8)]
@@ -278,6 +279,7 @@ impl Chunk {
blending_data: None,
dirty: AtomicBool::new(false),
inhabited_time: AtomicU64::new(0),
custom_data: Mutex::new(NbtCompound::new()),
})),
) {
Self::Proto(proto) => proto,
@@ -324,6 +326,7 @@ impl Chunk {
status: proto_chunk.stage.into(),
blending_data: proto_chunk.blending_data,
inhabited_time: AtomicU64::new(0),
custom_data: Mutex::new(NbtCompound::new()),
};
*self = Self::Level(Arc::new(chunk));

View File

@@ -381,6 +381,9 @@ impl Cache {
.set_structure_starts(noise_gen);
}
generator::WorldGenerator::Flat(_) => {}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.set_structure_starts(self.chunks[index].get_proto_chunk_mut());
}
},
StagedChunkEnum::StructureReferences => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -389,6 +392,9 @@ impl Cache {
.set_structure_references(noise_gen);
}
generator::WorldGenerator::Flat(_) => {}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.set_structure_references(self.chunks[index].get_proto_chunk_mut());
}
},
StagedChunkEnum::Biomes => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -399,6 +405,9 @@ impl Cache {
generator::WorldGenerator::Flat(flat_gen) => {
flat_gen.step_to_biomes(self.chunks[index].get_proto_chunk_mut());
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_biomes(self.chunks[index].get_proto_chunk_mut());
}
},
_ => {}
}
@@ -413,6 +422,7 @@ impl Cache {
chunks: Vec::with_capacity((size * size) as usize),
}
}
#[allow(clippy::too_many_lines)]
pub fn advance(
&mut self,
stage: StagedChunkEnum,
@@ -435,6 +445,9 @@ impl Cache {
.set_structure_starts(noise_gen);
}
generator::WorldGenerator::Flat(_) => {}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.set_structure_starts(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::StructureReferences => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -443,6 +456,9 @@ impl Cache {
.set_structure_references(noise_gen);
}
generator::WorldGenerator::Flat(_) => {}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.set_structure_references(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::Biomes => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -453,6 +469,9 @@ impl Cache {
generator::WorldGenerator::Flat(flat_gen) => {
flat_gen.step_to_biomes(self.chunks[mid].get_proto_chunk_mut());
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_biomes(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::Noise => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -463,6 +482,9 @@ impl Cache {
generator::WorldGenerator::Flat(flat_gen) => {
flat_gen.step_to_noise(self.chunks[mid].get_proto_chunk_mut());
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_noise(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::Surface => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -473,6 +495,9 @@ impl Cache {
generator::WorldGenerator::Flat(flat_gen) => {
flat_gen.step_to_surface(self.chunks[mid].get_proto_chunk_mut());
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_surface(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::Carvers => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -483,6 +508,9 @@ impl Cache {
generator::WorldGenerator::Flat(flat_gen) => {
flat_gen.step_to_carvers(self.chunks[mid].get_proto_chunk_mut());
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_carvers(self.chunks[mid].get_proto_chunk_mut());
}
},
StagedChunkEnum::Features => match generator {
generator::WorldGenerator::Noise(noise_gen) => {
@@ -495,6 +523,9 @@ impl Cache {
generator::WorldGenerator::Flat(_) => {
self.chunks[mid].get_proto_chunk_mut().stage = StagedChunkEnum::Features;
}
generator::WorldGenerator::Custom(custom_gen) => {
custom_gen.step_to_features(self, block_registry);
}
},
StagedChunkEnum::Lighting => {
let mut engine = crate::lighting::LightEngine::new();

View File

@@ -1430,8 +1430,9 @@ impl GenerationSchedule {
let stage = node.stage;
let send_chunk = self.send_chunk.clone();
let level = level.clone();
let settings =
GenerationSettings::from_dimension(level.world_gen.dimension());
let settings = GenerationSettings::from_dimension(
level.world_gen.load().dimension(),
);
pool.spawn(move || {
let result = crate::chunk_system::worker_logic::run_generation(

View File

@@ -112,7 +112,8 @@ pub async fn io_read_work(
);
// Create ProtoChunk using the async method
let mut proto = ProtoChunk::from_chunk_data(&chunk, &level.world_gen);
let mut proto =
ProtoChunk::from_chunk_data(&chunk, &level.world_gen.load());
// Clear all lighting data
let section_count = proto.light.sky_light.len();
@@ -144,7 +145,7 @@ pub async fn io_read_work(
} else {
// Standard ProtoChunk handling for non-full chunks
let val = RecvChunk::IO(Chunk::Proto(Box::new(
ProtoChunk::from_chunk_data(&chunk, &level.world_gen),
ProtoChunk::from_chunk_data(&chunk, &level.world_gen.load()),
)));
if send.send((pos, val)).is_err() {
break;
@@ -158,7 +159,7 @@ pub async fn io_read_work(
RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::new(
pos.x,
pos.y,
&level.world_gen,
&level.world_gen.load(),
)))),
))
.is_err()
@@ -188,7 +189,7 @@ pub async fn io_write_work(recv: AsyncRx<Vec<(ChunkPos, Chunk)>>, level: Arc<Lev
Chunk::Proto(chunk) => {
let mut temp = Chunk::Proto(chunk);
temp.upgrade_to_level_chunk(
level.world_gen.dimension(),
level.world_gen.load().dimension(),
&level.lighting_config,
);
let Chunk::Level(chunk) = temp else { panic!() };
@@ -250,7 +251,12 @@ pub fn run_generation(
};
// Run generation with panic catching
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
cache.advance(stage, &level.world_gen, portal_ref, &level.lighting_config);
cache.advance(
stage,
&level.world_gen.load(),
portal_ref,
&level.lighting_config,
);
cache // Return cache on success
}));
@@ -283,7 +289,7 @@ pub fn generation_work(
send: &crossfire::compat::MTx<(ChunkPos, RecvChunk)>,
level: &Arc<Level>,
) {
let settings = GenerationSettings::from_dimension(level.world_gen.dimension());
let settings = GenerationSettings::from_dimension(level.world_gen.load().dimension());
loop {
let Ok((pos, cache, stage)) = recv.recv() else {

View File

@@ -18,6 +18,11 @@ pub trait GeneratorInit {
use pumpkin_data::structures::{StructurePlacementCalculator, StructureSet};
use rustc_hash::FxHashMap;
use std::sync::Arc;
use crate::chunk_system::StagedChunkEnum;
use crate::generation::proto_chunk::ProtoChunk;
pub mod flat;
#[derive(Clone, Debug)]
@@ -26,35 +31,83 @@ pub struct FlatLayer {
pub height: i32,
}
pub trait CustomChunkGenerator: Send + Sync {
fn dimension(&self) -> &Dimension;
fn seed(&self) -> u64;
fn default_block(&self) -> &'static BlockState {
pumpkin_data::Block::AIR.default_state
}
fn biome_mixer_seed(&self) -> i64 {
0
}
fn global_structure_cache(
&self,
) -> Option<&crate::generation::structure::placement::GlobalStructureCache> {
None
}
fn step_to_biomes(&self, chunk: &mut ProtoChunk) {
chunk.stage = StagedChunkEnum::Biomes;
}
fn step_to_noise(&self, chunk: &mut ProtoChunk) {
chunk.stage = StagedChunkEnum::Noise;
}
fn step_to_surface(&self, chunk: &mut ProtoChunk) {
chunk.stage = StagedChunkEnum::Surface;
}
fn step_to_carvers(&self, chunk: &mut ProtoChunk) {
chunk.stage = StagedChunkEnum::Carvers;
}
fn step_to_features(
&self,
cache: &mut crate::chunk_system::generation_cache::Cache,
_block_registry: &dyn crate::world::WorldPortalExt,
) {
let mid = ((cache.size * cache.size) >> 1) as usize;
cache.chunks[mid].get_proto_chunk_mut().stage = StagedChunkEnum::Features;
}
fn set_structure_starts(&self, _chunk: &mut ProtoChunk) {}
fn set_structure_references(&self, _chunk: &mut ProtoChunk) {}
}
pub enum WorldGenerator {
Noise(Box<VanillaGenerator>),
Flat(flat::FlatGenerator),
Custom(Arc<dyn CustomChunkGenerator>),
}
impl WorldGenerator {
#[must_use]
pub const fn dimension(&self) -> &Dimension {
pub fn dimension(&self) -> &Dimension {
match self {
Self::Noise(noise_gen) => &noise_gen.dimension,
Self::Flat(flat_gen) => &flat_gen.dimension,
Self::Custom(custom_gen) => custom_gen.dimension(),
}
}
#[must_use]
pub const fn seed(&self) -> u64 {
pub fn seed(&self) -> u64 {
match self {
Self::Noise(noise_gen) => noise_gen.random_config.seed,
Self::Flat(flat_gen) => flat_gen.seed,
Self::Custom(custom_gen) => custom_gen.seed(),
}
}
#[must_use]
pub const fn global_structure_cache(
pub fn global_structure_cache(
&self,
) -> Option<&crate::generation::structure::placement::GlobalStructureCache> {
match self {
Self::Noise(noise_gen) => Some(&noise_gen.global_structure_cache),
Self::Flat(_) => None,
Self::Custom(custom_gen) => custom_gen.global_structure_cache(),
}
}
}

View File

@@ -200,18 +200,21 @@ impl ProtoChunk {
.trim_height(bottom_y, (dimension.min_y + dimension.height) as u16);
(shape.height, shape.min_y)
}
super::generator::WorldGenerator::Flat(_) => (height, bottom_y),
super::generator::WorldGenerator::Flat(_)
| super::generator::WorldGenerator::Custom(_) => (height, bottom_y),
};
let default_block = match generator {
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.default_block,
super::generator::WorldGenerator::Flat(_) => Block::AIR.default_state,
super::generator::WorldGenerator::Custom(custom_gen) => custom_gen.default_block(),
};
let biome_mixer_seed = match generator {
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.biome_mixer_seed,
super::generator::WorldGenerator::Flat(flat_gen) => {
crate::biome::hash_seed(flat_gen.seed)
}
super::generator::WorldGenerator::Custom(custom_gen) => custom_gen.biome_mixer_seed(),
};
let default_heightmap = [i16::MIN; CHUNK_AREA];

View File

@@ -89,7 +89,7 @@ pub struct Level {
pub chunk_saver: Arc<ChunkSaver>,
entity_saver: Arc<EntitySaver>,
pub world_gen: Arc<WorldGenerator>,
pub world_gen: ArcSwap<WorldGenerator>,
/// Handles runtime lighting updates
pub light_engine: DynamicLightEngine,
@@ -251,7 +251,7 @@ impl Level {
let level_ref = Arc::new(Self {
seed,
world_portal: ArcSwap::new(Arc::new(None)),
world_gen,
world_gen: ArcSwap::new(world_gen),
level_folder,
lighting_config: level_config.lighting,
light_engine: DynamicLightEngine::new(),
@@ -302,6 +302,15 @@ impl Level {
level_ref
}
pub fn set_world_gen(&self, generator: Arc<WorldGenerator>) {
self.world_gen.store(generator);
}
#[must_use]
pub fn world_gen(&self) -> Arc<WorldGenerator> {
self.world_gen.load_full()
}
pub fn spawn_entity_generation(self: &Arc<Self>, pos: Vector2<i32>) {
let level = self.clone();
if let Some(pool) = &self.gen_pool {

View File

@@ -11,16 +11,20 @@ use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
use pumpkin_data::translation;
use pumpkin_inventory::{
generic_container_screen_handler::create_generic_9x3,
player::ender_chest_inventory::EnderChestInventory,
player::player_inventory::PlayerInventory,
screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent;
use pumpkin_world::inventory::Inventory;
use pumpkin_world::block::viewer::ViewerCountTracker;
use tokio::sync::Mutex;
struct EnderChestScreenFactory(Arc<dyn Inventory>);
pub struct EnderChestScreenFactory {
pub inventory: Arc<EnderChestInventory>,
pub tracker: Option<Arc<ViewerCountTracker>>,
}
impl ScreenHandlerFactory for EnderChestScreenFactory {
fn create_screen_handler<'a>(
@@ -30,7 +34,11 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone()).await;
if let Some(tracker) = &self.tracker {
self.inventory.set_tracker(tracker.clone()).await;
}
let handler =
create_generic_9x3(sync_id, player_inventory, self.inventory.clone()).await;
let concrete_arc = Arc::new(Mutex::new(handler));
Some(concrete_arc as SharedScreenHandler)
@@ -58,6 +66,7 @@ impl BlockBehaviour for EnderChestBlock {
.entity
.get_horizontal_facing()
.opposite();
props.waterlogged = args.replacing.water_source();
props.to_state_id(args.block)
})
}
@@ -78,13 +87,19 @@ impl BlockBehaviour for EnderChestBlock {
return BlockActionResult::Success;
}
if let Some(block_entity) = args.world.get_block_entity(args.position)
&& let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<EnderChestBlockEntity>()
let block_entity = if let Some(be) = args.world.get_block_entity(args.position) {
be
} else {
let be = Arc::new(EnderChestBlockEntity::new(*args.position));
args.world.add_block_entity(be.clone());
be
};
if let Some(block_entity) = block_entity
.as_any()
.downcast_ref::<EnderChestBlockEntity>()
{
let inventory = args.player.ender_chest_inventory();
inventory.set_tracker(block_entity.get_tracker()).await;
args.player
.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
@@ -94,7 +109,10 @@ impl BlockBehaviour for EnderChestBlock {
.await;
args.player
.open_handled_screen(
&EnderChestScreenFactory(inventory.clone()),
&EnderChestScreenFactory {
inventory: inventory.clone(),
tracker: Some(block_entity.get_tracker()),
},
Some(*args.position),
)
.await;

View File

@@ -200,7 +200,7 @@ impl CommandExecutor for PlaceJigsawExecutor {
let (_piece_count, placer) = {
let seed = hash_block_pos(block_pos.0.x, block_pos.0.y, block_pos.0.z) as u64;
let random = RandomGenerator::Legacy(LegacyRand::from_seed(seed));
let world_gen = &context.world().level.world_gen;
let world_gen = context.world().level.world_gen();
let settings = GenerationSettings::from_dimension(world_gen.dimension());
let mut structure_context = StructureGeneratorContext {
seed: seed as i64,
@@ -299,7 +299,7 @@ impl CommandExecutor for PlaceStructureExecutor {
let seed = hash_block_pos(block_pos.0.x, block_pos.0.y, block_pos.0.z) as u64;
let (_piece_count, placer) = {
let world_gen = context.world().level.world_gen.clone();
let world_gen = context.world().level.world_gen();
let settings = GenerationSettings::from_dimension(world_gen.dimension());
if structure.structure_type == StructureType::Jigsaw {
@@ -569,7 +569,7 @@ impl CommandExecutor for PlaceFeatureExecutor {
BlockPos::new(p.x as i32, p.y as i32, p.z as i32)
});
let world_gen = context.world().level.world_gen.clone();
let world_gen = context.world().level.world_gen();
let cx = block_pos.0.x >> 4;
let cz = block_pos.0.z >> 4;
let mut chunk = ProtoChunk::new(cx, cz, &world_gen);

View File

@@ -26,10 +26,16 @@ static ERROR_RECIPE_NOT_FOUND: CommandErrorType<1> =
fn get_recipe_id(recipe: &DynamicRecipe) -> String {
match recipe {
DynamicRecipe::Crafting(crafting) => match crafting {
pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shaped { result, .. }
| pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shapeless { result, .. } => {
result.item_id.clone()
pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shaped {
recipe_id,
result,
..
}
| pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shapeless {
recipe_id,
result,
..
} => recipe_id.clone().unwrap_or_else(|| result.item_id.clone()),
},
DynamicRecipe::Cooking(cooking) => match cooking {
pumpkin_protocol::codec::recipe::OwnedCookingRecipeType::Smelting(r)

View File

@@ -1,5 +1,3 @@
use std::sync::atomic::Ordering::Relaxed;
use pumpkin_data::translation;
use pumpkin_util::PermissionLvl;
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
@@ -40,27 +38,11 @@ impl CommandExecutor for SaveAllExecutor {
let server = context.server();
if let Err(err) = server.player_data_storage.save_all_players(server).await {
error!("Failed to save player data: {err}");
if let Err(err) = server.save_all().await {
error!("Failed to save server data: {err}");
return Err(SAVE_FAILED_ERROR_TYPE.create_without_context());
}
if let Err(err) = server
.advancement_manager
.save_all_players(&server.get_all_players())
.await
{
error!("Failed to save player advancements: {err}");
return Err(SAVE_FAILED_ERROR_TYPE.create_without_context());
}
// Request a save from every world's chunk scheduler. This works even
// while autosaving is disabled via /save-off, matching Vanilla.
for world in server.worlds.load().iter() {
world.level.should_save.store(true, Relaxed);
world.level.level_channel.notify();
}
context
.source
.send_feedback(

View File

@@ -48,6 +48,7 @@ pub struct DisplayEntity {
pub entity: Entity,
pub interpolation_start_delta_ticks: AtomicI32,
pub interpolation_duration: AtomicI32,
pub teleport_duration: AtomicI32,
pub view_range: Mutex<f32>,
pub shadow_radius: Mutex<f32>,
pub shadow_strength: Mutex<f32>,
@@ -69,6 +70,7 @@ impl DisplayEntity {
entity,
interpolation_start_delta_ticks: AtomicI32::new(0),
interpolation_duration: AtomicI32::new(0),
teleport_duration: AtomicI32::new(0),
view_range: Mutex::new(1.0),
shadow_radius: Mutex::new(0.0),
shadow_strength: Mutex::new(1.0),
@@ -84,6 +86,258 @@ impl DisplayEntity {
}
}
pub fn get_interpolation_start_delta_ticks(&self) -> i32 {
self.interpolation_start_delta_ticks.load(Ordering::Relaxed)
}
pub fn set_interpolation_start_delta_ticks(&self, ticks: i32) {
self.interpolation_start_delta_ticks
.store(ticks, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::START_INTERPOLATION,
MetaDataType::INT,
VarInt(ticks),
)],
None,
);
}
pub fn get_interpolation_duration(&self) -> i32 {
self.interpolation_duration.load(Ordering::Relaxed)
}
pub fn set_interpolation_duration(&self, duration: i32) {
self.interpolation_duration
.store(duration, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::INTERPOLATION_DURATION,
MetaDataType::INT,
VarInt(duration),
)],
None,
);
}
pub fn get_teleport_duration(&self) -> i32 {
self.teleport_duration.load(Ordering::Relaxed)
}
pub fn set_teleport_duration(&self, duration: i32) {
self.teleport_duration.store(duration, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::TELEPORT_DURATION,
MetaDataType::INT,
VarInt(duration),
)],
None,
);
}
pub async fn get_translation(&self) -> Vector3<f32> {
*self.translation.lock().await
}
pub async fn set_translation(&self, translation: Vector3<f32>) {
*self.translation.lock().await = translation;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::TRANSLATION,
MetaDataType::VECTOR_3F,
Vector3fSerializer(translation.x, translation.y, translation.z),
)],
None,
);
}
pub async fn get_scale(&self) -> Vector3<f32> {
*self.scale.lock().await
}
pub async fn set_scale(&self, scale: Vector3<f32>) {
*self.scale.lock().await = scale;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::SCALE,
MetaDataType::VECTOR_3F,
Vector3fSerializer(scale.x, scale.y, scale.z),
)],
None,
);
}
pub async fn get_left_rotation(&self) -> [f32; 4] {
*self.left_rotation.lock().await
}
pub async fn set_left_rotation(&self, left_rotation: [f32; 4]) {
*self.left_rotation.lock().await = left_rotation;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::LEFT_ROTATION,
MetaDataType::QUATERNION_F,
QuaternionfSerializer(
left_rotation[0],
left_rotation[1],
left_rotation[2],
left_rotation[3],
),
)],
None,
);
}
pub async fn get_right_rotation(&self) -> [f32; 4] {
*self.right_rotation.lock().await
}
pub async fn set_right_rotation(&self, right_rotation: [f32; 4]) {
*self.right_rotation.lock().await = right_rotation;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::RIGHT_ROTATION,
MetaDataType::QUATERNION_F,
QuaternionfSerializer(
right_rotation[0],
right_rotation[1],
right_rotation[2],
right_rotation[3],
),
)],
None,
);
}
pub fn get_billboard(&self) -> u8 {
self.billboard.load(Ordering::Relaxed)
}
pub fn set_billboard(&self, billboard: u8) {
self.billboard.store(billboard, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::BILLBOARD,
MetaDataType::BYTE,
billboard,
)],
None,
);
}
pub fn get_brightness(&self) -> i32 {
self.brightness.load(Ordering::Relaxed)
}
pub fn set_brightness(&self, brightness: i32) {
self.brightness.store(brightness, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::BRIGHTNESS,
MetaDataType::INT,
VarInt(brightness),
)],
None,
);
}
pub async fn get_view_range(&self) -> f32 {
*self.view_range.lock().await
}
pub async fn set_view_range(&self, view_range: f32) {
*self.view_range.lock().await = view_range;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::VIEW_RANGE,
MetaDataType::FLOAT,
view_range,
)],
None,
);
}
pub async fn get_shadow_radius(&self) -> f32 {
*self.shadow_radius.lock().await
}
pub async fn set_shadow_radius(&self, shadow_radius: f32) {
*self.shadow_radius.lock().await = shadow_radius;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::SHADOW_RADIUS,
MetaDataType::FLOAT,
shadow_radius,
)],
None,
);
}
pub async fn get_shadow_strength(&self) -> f32 {
*self.shadow_strength.lock().await
}
pub async fn set_shadow_strength(&self, shadow_strength: f32) {
*self.shadow_strength.lock().await = shadow_strength;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::SHADOW_STRENGTH,
MetaDataType::FLOAT,
shadow_strength,
)],
None,
);
}
pub async fn get_display_width(&self) -> f32 {
*self.width.lock().await
}
pub async fn set_display_width(&self, width: f32) {
*self.width.lock().await = width;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::WIDTH,
MetaDataType::FLOAT,
width,
)],
None,
);
}
pub async fn get_display_height(&self) -> f32 {
*self.height.lock().await
}
pub async fn set_display_height(&self, height: f32) {
*self.height.lock().await = height;
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::HEIGHT,
MetaDataType::FLOAT,
height,
)],
None,
);
}
pub fn get_glow_color_override(&self) -> i32 {
self.glow_color_override.load(Ordering::Relaxed)
}
pub fn set_glow_color_override(&self, color: i32) {
self.glow_color_override.store(color, Ordering::Relaxed);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::GLOW_COLOR_OVERRIDE,
MetaDataType::INT,
VarInt(color),
)],
None,
);
}
#[allow(clippy::too_many_lines)]
pub async fn init_display_data_tracker(&self) {
let view_range = *self.view_range.lock().await;
@@ -210,6 +464,22 @@ impl DisplayEntity {
)],
None,
);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::TELEPORT_DURATION,
MetaDataType::INT,
VarInt(self.teleport_duration.load(Ordering::Relaxed)),
)],
None,
);
self.entity.send_meta_data(
&[Metadata::new(
TrackedData::GLOW_COLOR_OVERRIDE,
MetaDataType::INT,
VarInt(self.glow_color_override.load(Ordering::Relaxed)),
)],
None,
);
}
pub async fn write_display_nbt(&self, nbt: &mut NbtCompound) {
@@ -367,6 +637,22 @@ impl BlockDisplayEntity {
block_state: AtomicI32::new(0),
})
}
pub fn get_block_state(&self) -> i32 {
self.block_state.load(Ordering::Relaxed)
}
pub fn set_block_state(&self, block_state: i32) {
self.block_state.store(block_state, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::BLOCK_STATE,
MetaDataType::BLOCK_STATE,
VarInt(block_state),
)],
None,
);
}
}
impl NBTStorage for BlockDisplayEntity {
@@ -475,6 +761,38 @@ impl ItemDisplayEntity {
item_display: AtomicU8::new(0),
})
}
pub async fn get_item(&self) -> ItemStack {
self.item_stack.lock().await.clone()
}
pub async fn set_item(&self, item: ItemStack) {
*self.item_stack.lock().await = item.clone();
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::ITEM,
MetaDataType::ITEM_STACK,
ItemStackSerializer::from(item),
)],
None,
);
}
pub fn get_item_display_mode(&self) -> u8 {
self.item_display.load(Ordering::Relaxed)
}
pub fn set_item_display_mode(&self, mode: u8) {
self.item_display.store(mode, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::ITEM_DISPLAY,
MetaDataType::BYTE,
mode,
)],
None,
);
}
}
impl NBTStorage for ItemDisplayEntity {
@@ -619,6 +937,165 @@ impl TextDisplayEntity {
flags: AtomicU8::new(0),
})
}
pub async fn get_text(&self) -> TextComponent {
self.text.lock().await.clone()
}
pub async fn set_text(&self, text: TextComponent) {
*self.text.lock().await = text.clone();
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT,
MetaDataType::COMPONENT,
text,
)],
None,
);
}
pub fn get_line_width(&self) -> i32 {
self.line_width.load(Ordering::Relaxed)
}
pub fn set_line_width(&self, width: i32) {
self.line_width.store(width, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::LINE_WIDTH,
MetaDataType::INT,
VarInt(width),
)],
None,
);
}
pub fn get_background_color(&self) -> i32 {
self.background.load(Ordering::Relaxed)
}
pub fn set_background_color(&self, color: i32) {
self.background.store(color, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::BACKGROUND,
MetaDataType::INT,
VarInt(color),
)],
None,
);
}
pub fn get_text_opacity(&self) -> i8 {
self.text_opacity.load(Ordering::Relaxed)
}
pub fn set_text_opacity(&self, opacity: i8) {
self.text_opacity.store(opacity, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT_OPACITY,
MetaDataType::BYTE,
opacity as u8,
)],
None,
);
}
pub fn get_shadow(&self) -> bool {
(self.flags.load(Ordering::Relaxed) & 1) != 0
}
pub fn set_shadow(&self, shadow: bool) {
let mut flags = self.flags.load(Ordering::Relaxed);
if shadow {
flags |= 1;
} else {
flags &= !1;
}
self.flags.store(flags, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT_DISPLAY_FLAGS,
MetaDataType::BYTE,
flags,
)],
None,
);
}
pub fn get_see_through(&self) -> bool {
(self.flags.load(Ordering::Relaxed) & 2) != 0
}
pub fn set_see_through(&self, see_through: bool) {
let mut flags = self.flags.load(Ordering::Relaxed);
if see_through {
flags |= 2;
} else {
flags &= !2;
}
self.flags.store(flags, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT_DISPLAY_FLAGS,
MetaDataType::BYTE,
flags,
)],
None,
);
}
pub fn get_use_default_background(&self) -> bool {
(self.flags.load(Ordering::Relaxed) & 4) != 0
}
pub fn set_use_default_background(&self, default_bg: bool) {
let mut flags = self.flags.load(Ordering::Relaxed);
if default_bg {
flags |= 4;
} else {
flags &= !4;
}
self.flags.store(flags, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT_DISPLAY_FLAGS,
MetaDataType::BYTE,
flags,
)],
None,
);
}
pub fn get_alignment(&self) -> u8 {
let flags = self.flags.load(Ordering::Relaxed);
if flags & 8 != 0 {
1
} else if flags & 16 != 0 {
2
} else {
0
}
}
pub fn set_alignment(&self, align: u8) {
let mut flags = self.flags.load(Ordering::Relaxed) & !0b1_1000;
if align == 1 {
flags |= 8;
} else if align == 2 {
flags |= 16;
}
self.flags.store(flags, Ordering::Relaxed);
self.display.entity.send_meta_data(
&[Metadata::new(
TrackedData::TEXT_DISPLAY_FLAGS,
MetaDataType::BYTE,
flags,
)],
None,
);
}
}
impl NBTStorage for TextDisplayEntity {

View File

@@ -2271,7 +2271,7 @@ impl EntityBase for LivingEntity {
let mut damage_event =
crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new(
self.entity.entity_id,
damage_type.id.to_string(),
damage_type,
amount,
);
if let Some(server) = self.entity.world.load().server.upgrade() {

View File

@@ -892,6 +892,8 @@ pub struct Entity {
pub last_sent_pos: AtomicCell<Vector3<f64>>,
/// Cache for the last sent head yaw byte
pub last_sent_head_yaw: AtomicU8,
/// Persistent custom data container for plugins (matching Bukkit's `PersistentDataHolder`)
pub custom_data: Mutex<NbtCompound>,
}
impl Entity {
@@ -1014,6 +1016,7 @@ impl Entity {
last_sent_pitch: AtomicU8::new(0),
last_sent_head_yaw: AtomicU8::new(0),
last_sent_pos: AtomicCell::new(position),
custom_data: Mutex::new(NbtCompound::new()),
}
}
@@ -3700,6 +3703,53 @@ impl Entity {
}
self.movement_multiplier.store(multiplier);
}
pub async fn set_custom_data(&self, namespace: &str, key: &str, value: NbtTag) {
let mut custom_data = self.custom_data.lock().await;
let mut namespace_data = custom_data
.child_tags
.remove(namespace)
.and_then(|tag| match tag {
NbtTag::Compound(compound) => Some(compound),
_ => None,
})
.unwrap_or_default();
namespace_data.child_tags.insert(key.into(), value);
custom_data
.child_tags
.insert(namespace.into(), NbtTag::Compound(namespace_data));
}
pub async fn get_custom_data(&self, namespace: &str, key: &str) -> Option<NbtTag> {
let custom_data = self.custom_data.lock().await;
custom_data
.get(namespace)?
.extract_compound()?
.get(key)
.cloned()
}
pub async fn remove_custom_data(&self, namespace: &str, key: &str) {
let mut custom_data = self.custom_data.lock().await;
let Some(NbtTag::Compound(mut namespace_data)) = custom_data.child_tags.remove(namespace)
else {
return;
};
namespace_data.child_tags.remove(key);
if !namespace_data.is_empty() {
custom_data
.child_tags
.insert(namespace.into(), NbtTag::Compound(namespace_data));
}
}
pub async fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.get_custom_data(namespace, key).await.is_some()
}
}
impl NBTStorage for Entity {
@@ -3759,6 +3809,11 @@ impl NBTStorage for Entity {
);
}
let custom_data = self.custom_data.lock().await;
if !custom_data.is_empty() {
nbt.put_compound("PumpkinCustomData", custom_data.clone());
}
// todo more...
})
}
@@ -3827,6 +3882,14 @@ impl NBTStorage for Entity {
);
}
if let Some(custom_data) = nbt
.get_compound("PumpkinCustomData")
.or_else(|| nbt.get_compound("BukkitValues"))
{
let mut data = self.custom_data.lock().await;
*data = custom_data.clone();
}
// todo more...
})
}

View File

@@ -126,6 +126,10 @@ impl BedrockPlayer<'_> {
}
}
pub async fn get_team(&self) -> Option<crate::world::scoreboard::Team> {
self.0.get_team().await
}
#[must_use]
pub fn client_data(&self) -> Option<Arc<pumpkin_protocol::bedrock::server::login::ClientData>> {
if let ClientPlatform::Bedrock(client) = self.0.client.as_ref() {
@@ -1193,8 +1197,39 @@ impl Player {
&self.ender_chest_inventory
}
/// Opens the player's ender chest screen.
pub async fn open_ender_chest(self: &Arc<Self>) -> Option<u8> {
self.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
pumpkin_data::statistic::CustomStatistic::OpenEnderchest as i32,
1,
)
.await;
let inventory = self.ender_chest_inventory();
self.open_handled_screen(
&crate::block::blocks::ender_chest::EnderChestScreenFactory {
inventory: inventory.clone(),
tracker: None,
},
None,
)
.await
}
/// Removes the [`Player`] out of the current [`World`].
pub async fn remove(self: &Arc<Self>) {
if !self
.current_screen_handler
.lock()
.await
.lock()
.await
.as_any()
.is::<PlayerScreenHandler>()
{
self.on_handled_screen_closed().await;
}
let vehicle = self.living_entity.entity.vehicle.lock().await.clone();
if let Some(vehicle) = vehicle {
self.root_vehicle_uuid
@@ -2782,6 +2817,25 @@ impl Player {
self.stats.lock().await.set(category, stat, value);
}
pub async fn get_stat(&self, category: statistics::StatisticCategory, stat: i32) -> i32 {
self.stats.lock().await.get(category, stat)
}
pub async fn get_custom_stat(&self, stat: statistics::CustomStatistic) -> i32 {
self.get_stat(statistics::StatisticCategory::Custom, stat as i32)
.await
}
pub async fn set_custom_stat(&self, stat: statistics::CustomStatistic, value: i32) {
self.set_stat(statistics::StatisticCategory::Custom, stat as i32, value)
.await;
}
pub async fn increment_custom_stat(&self, stat: statistics::CustomStatistic, amount: i32) {
self.increment_stat(statistics::StatisticCategory::Custom, stat as i32, amount)
.await;
}
pub async fn get_movement_statistic(&self) -> statistics::CustomStatistic {
let entity = self.get_entity();
if entity.has_vehicle().await {
@@ -3042,6 +3096,18 @@ impl Player {
}
}
pub async fn get_team(&self) -> Option<crate::world::scoreboard::Team> {
let guard = self.custom_scoreboard.lock().await;
if let Some(CustomScoreboard::Java(sb)) = guard.as_ref()
&& let Some(team) = sb.get_entity_team(&self.gameprofile.name)
{
return Some(team.clone());
}
let world = self.world();
let sb = world.scoreboard.lock().await;
sb.get_entity_team(&self.gameprofile.name).cloned()
}
pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) {
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
self.compass_target.store(Some(pos));
@@ -5710,6 +5776,7 @@ impl NBTStorage for EnderChestInventory {
for tag in item_list {
if let Some(item_compound) = tag.extract_compound()
&& let Some(slot_byte) = item_compound.get_byte("Slot")
&& (0..Self::INVENTORY_SIZE as i8).contains(&slot_byte)
{
let slot = slot_byte as usize;
if let Some(item_stack) = ItemStack::read_item_stack(item_compound) {

View File

@@ -432,7 +432,18 @@ impl PlayerAdvancement {
result = true;
self.progress_changed.insert(advancement);
if !was_done && progress.is_done() {
//TODO listener
let player_c = player.clone();
let adv_id = advancement.id.to_string();
tokio::spawn(async move {
if let Some(server) = player_c.world().server.upgrade() {
let mut event =
crate::plugin::api::events::player::player_advancement_done::PlayerAdvancementDoneEvent::new(
player_c,
adv_id,
);
server.plugin_manager.fire(&server, &mut event).await;
}
});
Self::grant_reward(player.clone(), advancement.reward);
if let Some(display) = advancement.display
&& display.announce_to_chat

View File

@@ -147,7 +147,7 @@ impl ItemBehaviour for EnderEyeItem {
fn find_stronghold(world: &Arc<World>, origin: BlockPos) -> Option<BlockPos> {
let level = &world.level;
let generator = &level.world_gen;
let generator = level.world_gen();
let seed = level.seed.0;
let global_cache = generator.global_structure_cache()?;

View File

@@ -386,13 +386,28 @@ impl PumpkinServer {
}
pub async fn init_plugins(&self) -> std::time::Duration {
match self.server.plugin_manager.load_plugins(&self.server).await {
if !self.server.advanced_config.plugins.enabled {
info!("Plugin system is disabled in configuration.");
return std::time::Duration::ZERO;
}
let duration = match self.server.plugin_manager.load_plugins(&self.server).await {
Ok(duration) => duration,
Err(err) => {
error!("{err}");
std::time::Duration::ZERO
}
};
if self.server.advanced_config.plugins.hot_reload {
if let Err(err) = self.server.plugin_manager.start_watcher(&self.server).await {
error!("Failed to start plugin hot-reloading watcher: {err}");
} else {
info!("Plugin hot-reloading watcher started from configuration.");
}
}
duration
}
pub async fn unload_plugins(&self) {

View File

@@ -1,3 +1,4 @@
use pumpkin_data::damage::DamageType;
use pumpkin_macros::{Event, cancellable};
/// An event that occurs when an entity takes damage.
@@ -10,17 +11,17 @@ pub struct EntityDamageEvent {
/// The amount of damage taken.
pub damage: f32,
/// The cause or type of damage as a string.
pub cause: String,
/// The damage type.
pub damage_type: DamageType,
}
impl EntityDamageEvent {
#[must_use]
pub const fn new(entity_id: i32, damage_type: String, damage_amount: f32) -> Self {
pub const fn new(entity_id: i32, damage_type: DamageType, damage_amount: f32) -> Self {
Self {
entity_id,
damage: damage_amount,
cause: damage_type,
damage_type,
cancelled: false,
}
}

View File

@@ -15,6 +15,17 @@ pub struct PlayerAdvancementDoneEvent {
pub advancement_id: String,
}
impl PlayerAdvancementDoneEvent {
#[must_use]
pub const fn new(player: Arc<Player>, advancement_id: String) -> Self {
Self {
player,
advancement_id,
cancelled: false,
}
}
}
impl PlayerEvent for PlayerAdvancementDoneEvent {
fn get_player(&self) -> &Arc<Player> {
&self.player

View File

@@ -60,6 +60,7 @@ impl PluginRuntime {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, PluginInitError> {
let mut config = wasmtime::Config::new();
config.wasm_component_model(true);
config.wasm_component_model_async(true);
let mut path = std::path::absolute(path.as_ref()).expect("Failed to get absolute path");
path.pop();
path.push("cache");
@@ -119,6 +120,8 @@ fn setup_linker(engine: &Engine) -> wasmtime::Result<Linker<PluginHostState>> {
let mut linker = Linker::<PluginHostState>::new(engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
wasmtime_wasi_http::p2::add_only_http_to_linker_async(&mut linker)?;
wasmtime_wasi::p3::add_to_linker(&mut linker)?;
wasmtime_wasi_http::p3::add_to_linker(&mut linker)?;
wit::v0_1::add_to_linker(&mut linker)?;
Ok(linker)
}
@@ -155,6 +158,7 @@ fn load_component(
}
impl WasmPlugin {
#[allow(clippy::too_many_lines)]
pub async fn on_load(
&self,
context: Arc<Context>,
@@ -167,12 +171,18 @@ impl WasmPlugin {
builder.inherit_stderr();
let metadata = context.get_metadata();
let blocked_permissions = &context.server.advanced_config.plugins.blocked_permissions;
let plugin_config = &context.server.advanced_config.plugins;
let plugin_override = plugin_config.overrides.get(&metadata.name);
let is_blocked = |p: &str| {
plugin_config.blocked_permissions.iter().any(|b| b == p)
|| plugin_override.is_some_and(|o| o.blocked_permissions.iter().any(|b| b == p))
};
let filtered_permissions: Vec<String> = metadata
.permissions
.iter()
.filter(|p| !blocked_permissions.iter().any(|blocked| blocked == *p))
.filter(|p| !is_blocked(p))
.cloned()
.collect();
@@ -191,7 +201,10 @@ impl WasmPlugin {
let udp_outgoing_datagram =
udp_allowed || has_permission(permissions::NETWORK_UDP_OUTGOING_DATAGRAM);
let loopback_only = has_permission(permissions::NETWORK_LOOPBACK);
let loopback_only = plugin_override
.and_then(|o| o.loopback_only)
.unwrap_or(plugin_config.loopback_only)
|| has_permission(permissions::NETWORK_LOOPBACK);
builder.allow_tcp(tcp_connect || tcp_bind);
builder.allow_udp(udp_connect || udp_bind);
@@ -218,10 +231,10 @@ impl WasmPlugin {
builder.inherit_network();
}
// --- System Permissions ---
// --- System Permissions & Environment Variables ---
// Environment Variables
if has_permission(permissions::SYS_ENV) {
if plugin_config.inherit_env || has_permission(permissions::SYS_ENV) {
builder.inherit_env();
} else {
for (key, value) in std::env::vars() {
@@ -232,6 +245,13 @@ impl WasmPlugin {
}
}
// Injected environment variables from plugin override
if let Some(plugin_override) = plugin_override {
for (key, value) in &plugin_override.environment {
builder.env(key, value);
}
}
builder.preopened_dir(
context.get_data_folder(),
"data",
@@ -257,8 +277,15 @@ impl WasmPlugin {
},
)?;
if has_permission(permissions::HTTP_OUTBOUND) {
store.data_mut().wasi_http_hooks.allow_outbound = true;
let max_memory_mb = plugin_override
.and_then(|o| o.max_memory_mb)
.or(plugin_config.max_memory_mb);
if let Some(mb) = max_memory_mb {
let limit_bytes = (mb as usize).saturating_mul(1024 * 1024);
store.data_mut().limits = wasmtime::StoreLimitsBuilder::new()
.memory_size(limit_bytes)
.build();
}
store.data_mut().permissions = filtered_permissions;
@@ -270,8 +297,16 @@ impl WasmPlugin {
match self.plugin_instance {
PluginInstance::V0_1(ref plugin) => {
let context = store.data_mut().add_context(context)?;
plugin.call_on_load(&mut *store, context).await
let context_res = store.data_mut().add_context(context)?;
let context_rep = context_res.rep();
let res = plugin.call_on_load(&mut *store, context_res).await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ContextResource>(
wasmtime::component::Resource::new_own(context_rep),
);
res
}
}
}
@@ -294,8 +329,16 @@ impl WasmPlugin {
match self.plugin_instance {
PluginInstance::V0_1(ref plugin) => {
let context = store.data_mut().add_context(context)?;
plugin.call_on_unload(&mut *store, context).await
let context_res = store.data_mut().add_context(context)?;
let context_rep = context_res.rep();
let res = plugin.call_on_unload(&mut *store, context_res).await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ContextResource>(
wasmtime::component::Resource::new_own(context_rep),
);
res
}
}
}

View File

@@ -387,7 +387,7 @@ pub fn verify_pumpkin_wasm(wasm_bytes: &[u8], public_key_hex: &str) -> Verificat
}
/// Verifies a WASM plugin binary and logs appropriate warnings if unsigned or invalid.
pub fn verify_wasm_plugin(wasm_bytes: &[u8], path_str: &str) {
pub fn verify_wasm_plugin(wasm_bytes: &[u8], path_str: &str) -> VerificationResult {
let public_key = fetch_market_public_key().unwrap_or_default();
let result = verify_pumpkin_wasm(wasm_bytes, &public_key);
@@ -403,6 +403,16 @@ pub fn verify_wasm_plugin(wasm_bytes: &[u8], path_str: &str) {
result.error.as_deref().unwrap_or("Unknown error")
);
}
result
}
/// Checks if a WASM plugin binary has a valid signature.
#[must_use]
pub fn is_wasm_signed(wasm_bytes: &[u8]) -> bool {
let public_key = fetch_market_public_key().unwrap_or_default();
let result = verify_pumpkin_wasm(wasm_bytes, &public_key);
result.is_signed && result.is_valid
}
#[cfg(test)]

View File

@@ -65,6 +65,8 @@ pub type ConsumedArgsResource = WasmResource<OwnedConsumedArgs>;
pub type CommandNodeResource = WasmResource<NonLeafNodeBuilder>;
pub type ItemStackResource = WasmResource<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>>;
pub type RecipeManagerResource = WasmResource<Arc<RecipeManager>>;
pub type EnchantmentManagerResource =
WasmResource<Arc<crate::server::enchantment::EnchantmentManager>>;
pub type OpManagerResource = WasmResource<Arc<Server>>;
pub type BanManagerResource = WasmResource<Arc<Server>>;
pub type WhitelistManagerResource = WasmResource<Arc<Server>>;
@@ -78,6 +80,28 @@ pub struct ContainerBlockEntity {
pub type ContainerBlockEntityResource = WasmResource<ContainerBlockEntity>;
pub type DisplayEntityResource = WasmResource<Arc<dyn EntityBase>>;
pub type BlockDisplayEntityResource = WasmResource<Arc<dyn EntityBase>>;
pub type ItemDisplayEntityResource = WasmResource<Arc<dyn EntityBase>>;
pub type TextDisplayEntityResource = WasmResource<Arc<dyn EntityBase>>;
pub type InteractionEntityResource = WasmResource<Arc<dyn EntityBase>>;
#[derive(Clone, Copy)]
pub struct ChunkBuffer {
pub x: i32,
pub z: i32,
pub min_y: i32,
pub height: u32,
pub proto_chunk: *mut pumpkin_world::ProtoChunk,
}
// SAFETY: `ChunkBuffer` encapsulates a raw pointer to a proto chunk that is uniquely accessed during custom world generation phases.
unsafe impl Send for ChunkBuffer {}
// SAFETY: `ChunkBuffer` encapsulates a raw pointer to a proto chunk that is uniquely accessed during custom world generation phases.
unsafe impl Sync for ChunkBuffer {}
pub type ChunkBufferResource = WasmResource<ChunkBuffer>;
pub type OwnedConsumedArgs = HashMap<String, OwnedArg>;
pub struct PluginHostState {
@@ -85,6 +109,7 @@ pub struct PluginHostState {
pub wasi_http_ctx: WasiHttpCtx,
pub wasi_http_hooks: PluginHttpHooks,
pub resource_table: ResourceTable,
pub limits: wasmtime::StoreLimits,
pub plugin: Option<Weak<WasmPlugin>>,
pub server: Option<Arc<Server>>,
pub permissions: Vec<String>,
@@ -109,6 +134,7 @@ impl PluginHostState {
wasi_http_ctx: WasiHttpCtx::new(),
wasi_http_hooks: PluginHttpHooks::new(),
resource_table,
limits: wasmtime::StoreLimitsBuilder::new().build(),
plugin: None,
server: None,
permissions: Vec::new(),
@@ -297,6 +323,16 @@ impl PluginHostState {
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn add_enchantment_manager<T>(
&mut self,
provider: Arc<crate::server::enchantment::EnchantmentManager>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(EnchantmentManagerResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn add_op_manager<T>(
&mut self,
provider: Arc<Server>,
@@ -344,6 +380,118 @@ impl PluginHostState {
})?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn add_display_entity<T>(
&mut self,
provider: Arc<dyn EntityBase>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(DisplayEntityResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_display_entity_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&DisplayEntityResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
pub fn add_block_display_entity<T>(
&mut self,
provider: Arc<dyn EntityBase>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(BlockDisplayEntityResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_block_display_entity_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&BlockDisplayEntityResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
pub fn add_item_display_entity<T>(
&mut self,
provider: Arc<dyn EntityBase>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(ItemDisplayEntityResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_item_display_entity_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&ItemDisplayEntityResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
pub fn add_text_display_entity<T>(
&mut self,
provider: Arc<dyn EntityBase>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(TextDisplayEntityResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_text_display_entity_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&TextDisplayEntityResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
pub fn add_interaction_entity<T>(
&mut self,
provider: Arc<dyn EntityBase>,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self
.resource_table
.push(InteractionEntityResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_interaction_entity_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&InteractionEntityResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
pub fn add_chunk_buffer<T>(
&mut self,
provider: ChunkBuffer,
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
let resource = self.resource_table.push(ChunkBufferResource { provider })?;
Ok(wasmtime::component::Resource::new_own(resource.rep()))
}
pub fn get_chunk_buffer_res<T>(
&self,
resource: &wasmtime::component::Resource<T>,
) -> wasmtime::Result<&ChunkBufferResource> {
Ok(self
.resource_table
.get(&wasmtime::component::Resource::new_borrow(resource.rep()))?)
}
}
pub struct PluginHttpHooks {
@@ -379,6 +527,8 @@ impl WasiHttpHooks for PluginHttpHooks {
}
}
impl wasmtime_wasi_http::p3::WasiHttpHooks for PluginHttpHooks {}
impl WasiView for PluginHostState {
fn ctx(&mut self) -> WasiCtxView<'_> {
WasiCtxView {
@@ -397,3 +547,13 @@ impl WasiHttpView for PluginHostState {
}
}
}
impl wasmtime_wasi_http::p3::WasiHttpView for PluginHostState {
fn http(&mut self) -> wasmtime_wasi_http::p3::WasiHttpCtxView<'_> {
wasmtime_wasi_http::p3::WasiHttpCtxView {
ctx: &mut self.wasi_http_ctx,
table: &mut self.resource_table,
hooks: &mut self.wasi_http_hooks,
}
}
}

View File

@@ -0,0 +1,60 @@
use crate::plugin::loader::wasm::wasm_host::state::PluginHostState;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::advancement::{
AdvancementDisplay as WitAdvancementDisplay, AdvancementInfo as WitAdvancementInfo,
FrameType as WitFrameType,
};
use pumpkin_data::Advancement;
use pumpkin_data::advancement_data::FrameType;
#[must_use]
pub fn find_advancement(id: &str) -> Option<&'static Advancement> {
Advancement::from_name(id)
.or_else(|| Advancement::from_minecraft_name(id))
.or_else(|| {
id.strip_prefix("minecraft:")
.and_then(Advancement::from_name)
})
}
#[must_use]
pub const fn to_wasm_frame_type(frame: FrameType) -> WitFrameType {
match frame {
FrameType::Task => WitFrameType::Task,
FrameType::Challenge => WitFrameType::Challenge,
FrameType::Goal => WitFrameType::Goal,
}
}
pub fn to_wasm_advancement_info(
state: &mut PluginHostState,
advancement: &'static Advancement,
) -> wasmtime::Result<WitAdvancementInfo> {
let display = if let Some(disp) = advancement.display {
let title = state.add_text_component(disp.get_title())?;
let description = state.add_text_component(disp.get_description())?;
Some(WitAdvancementDisplay {
title,
description,
frame: to_wasm_frame_type(disp.frame_type),
show_toast: disp.show_toast,
hidden: disp.hidden,
announce_to_chat: disp.announce_to_chat,
background: disp.background_texture.map(ToString::to_string),
x: disp.x,
y: disp.y,
})
} else {
None
};
Ok(WitAdvancementInfo {
id: advancement.id.to_string(),
parent_id: advancement.parent.as_ref().map(ToString::to_string),
criteria: advancement
.criteria
.iter()
.map(ToString::to_string)
.collect(),
display,
})
}

View File

@@ -206,6 +206,86 @@ impl HostBlockEntity for PluginHostState {
Ok(())
}
async fn set_custom_data(
&mut self,
res: Resource<BlockEntity>,
namespace: String,
key: String,
value: super::common::WitNbtTree,
) -> wasmtime::Result<()> {
let entity = block_entity_from_resource(self, &res)?;
let pos = entity.get_position();
let tag = super::common::from_wit_nbt_tree(&value).map_err(wasmtime::Error::msg)?;
if let Some(server) = &self.server {
for world in server.worlds.load().iter() {
if world
.block_entities
.get(&pos.chunk_position())
.is_some_and(|m| m.contains_key(&pos))
{
world.set_block_entity_custom_data(&pos, &namespace, &key, tag);
return Ok(());
}
}
if let Some(world) = server.worlds.load().first() {
world.set_block_entity_custom_data(&pos, &namespace, &key, tag);
}
}
Ok(())
}
async fn get_custom_data(
&mut self,
res: Resource<BlockEntity>,
namespace: String,
key: String,
) -> wasmtime::Result<Option<super::common::WitNbtTree>> {
let entity = block_entity_from_resource(self, &res)?;
let pos = entity.get_position();
if let Some(server) = &self.server {
for world in server.worlds.load().iter() {
if let Some(tag) = world.get_block_entity_custom_data(&pos, &namespace, &key) {
return Ok(Some(super::common::to_wit_nbt_tree(tag)));
}
}
}
Ok(None)
}
async fn remove_custom_data(
&mut self,
res: Resource<BlockEntity>,
namespace: String,
key: String,
) -> wasmtime::Result<()> {
let entity = block_entity_from_resource(self, &res)?;
let pos = entity.get_position();
if let Some(server) = &self.server {
for world in server.worlds.load().iter() {
world.remove_block_entity_custom_data(&pos, &namespace, &key);
}
}
Ok(())
}
async fn has_custom_data(
&mut self,
res: Resource<BlockEntity>,
namespace: String,
key: String,
) -> wasmtime::Result<bool> {
let entity = block_entity_from_resource(self, &res)?;
let pos = entity.get_position();
if let Some(server) = &self.server {
for world in server.worlds.load().iter() {
if world.has_block_entity_custom_data(&pos, &namespace, &key) {
return Ok(true);
}
}
}
Ok(false)
}
async fn drop(&mut self, rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
let _ = self
.resource_table

View File

@@ -43,6 +43,10 @@ impl CommandExecutor for WasmCommandExecutor {
.add_consumed_args(args)
.expect("valid consumed args");
let sender_rep = sender_resource.rep();
let server_rep = server_resource.rep();
let args_rep = args_resource.rep();
match self.plugin.plugin_instance {
PluginInstance::V0_1(ref plugin) => {
let result = plugin
@@ -53,15 +57,35 @@ impl CommandExecutor for WasmCommandExecutor {
server_resource,
args_resource,
)
.await
.map_err(|e| {
CommandError::CommandFailed(
TextComponent::text(format!(
"Wasm command failed with following error: {e}"
))
.color(Color::Named(NamedColor::Red)),
)
})?;
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::CommandSenderResource>(
wasmtime::component::Resource::new_own(sender_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ConsumedArgsResource>(
wasmtime::component::Resource::new_own(args_rep),
);
let result = result.map_err(|e| {
CommandError::CommandFailed(
TextComponent::text(format!(
"Wasm command failed with following error: {e}"
))
.color(Color::Named(NamedColor::Red)),
)
})?;
match result {
Ok(value) => Ok(value),

View File

@@ -1,3 +1,94 @@
use crate::plugin::loader::wasm::wasm_host::{state::PluginHostState, wit::v0_1::pumpkin};
pub use pumpkin::plugin::common::{
NbtEntry as WitNbtEntry, NbtTag as WitNbtTag, NbtTree as WitNbtTree,
};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag;
impl pumpkin::plugin::common::Host for PluginHostState {}
pub fn push_wit_nbt_tag(tag: NbtTag, tags: &mut Vec<WitNbtTag>) -> u32 {
let index = tags.len() as u32;
tags.push(WitNbtTag::Byte(0));
let tag = match tag {
NbtTag::End => WitNbtTag::Compound(Vec::new()),
NbtTag::Byte(value) => WitNbtTag::Byte(value),
NbtTag::Short(value) => WitNbtTag::Short(value),
NbtTag::Int(value) => WitNbtTag::Int(value),
NbtTag::Long(value) => WitNbtTag::Long(value),
NbtTag::Float(value) => WitNbtTag::Float(value),
NbtTag::Double(value) => WitNbtTag::Double(value),
NbtTag::ByteArray(value) => WitNbtTag::ByteArray(value.into_vec()),
NbtTag::String(value) => WitNbtTag::StringTag(value.into()),
NbtTag::List(value) => WitNbtTag::ListTag(
value
.into_iter()
.map(|value| push_wit_nbt_tag(value, tags))
.collect(),
),
NbtTag::Compound(value) => WitNbtTag::Compound(
value
.child_tags
.into_iter()
.map(|(key, value)| WitNbtEntry {
key: key.into(),
value: push_wit_nbt_tag(value, tags),
})
.collect(),
),
NbtTag::IntArray(value) => WitNbtTag::IntArray(value),
NbtTag::LongArray(value) => WitNbtTag::LongArray(value),
};
tags[index as usize] = tag;
index
}
#[must_use]
pub fn to_wit_nbt_tree(tag: NbtTag) -> WitNbtTree {
let mut tags = Vec::new();
let root = push_wit_nbt_tag(tag, &mut tags);
WitNbtTree { root, tags }
}
pub fn from_wit_nbt_tree(tree: &WitNbtTree) -> Result<NbtTag, String> {
fn read_tag(index: u32, tags: &[WitNbtTag], visiting: &mut Vec<u32>) -> Result<NbtTag, String> {
let Some(tag) = tags.get(index as usize) else {
return Err(format!("NBT tag index {index} is out of bounds"));
};
if visiting.contains(&index) {
return Err(format!("NBT tag tree contains a cycle at index {index}"));
}
visiting.push(index);
let tag = match tag {
WitNbtTag::Byte(value) => NbtTag::Byte(*value),
WitNbtTag::Short(value) => NbtTag::Short(*value),
WitNbtTag::Int(value) => NbtTag::Int(*value),
WitNbtTag::Long(value) => NbtTag::Long(*value),
WitNbtTag::Float(value) => NbtTag::Float(*value),
WitNbtTag::Double(value) => NbtTag::Double(*value),
WitNbtTag::ByteArray(value) => NbtTag::ByteArray(value.clone().into()),
WitNbtTag::StringTag(value) => NbtTag::String(value.clone().into()),
WitNbtTag::ListTag(value) => NbtTag::List(
value
.iter()
.map(|value| read_tag(*value, tags, visiting))
.collect::<Result<Vec<_>, _>>()?,
),
WitNbtTag::Compound(value) => NbtTag::Compound(NbtCompound {
child_tags: value
.iter()
.map(|entry| {
read_tag(entry.value, tags, visiting)
.map(|value| (entry.key.clone().into(), value))
})
.collect::<Result<_, _>>()?,
}),
WitNbtTag::IntArray(value) => NbtTag::IntArray(value.clone()),
WitNbtTag::LongArray(value) => NbtTag::LongArray(value.clone()),
};
visiting.pop();
Ok(tag)
}
read_tag(tree.root, &tree.tags, &mut Vec::new())
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
use crate::plugin::loader::wasm::wasm_host::state::PluginHostState;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::enchantments::{
AttributeModifierSlot as WitAttributeModifierSlot, CustomEnchantment as WitCustomEnchantment,
EnchantmentManager as WitEnchantmentManager, HostEnchantmentManager,
};
use crate::server::enchantment::CustomEnchantmentEntry;
use pumpkin_data::enchantment::{AttributeModifierSlot, Enchantment};
use pumpkin_util::text::TextComponent;
use wasmtime::component::Resource;
impl HostEnchantmentManager for PluginHostState {
async fn register_enchantment(
&mut self,
_res: Resource<WitEnchantmentManager>,
enchantment: WitCustomEnchantment,
) -> wasmtime::Result<Result<(), String>> {
let description =
super::player::text_component_from_resource(self, &enchantment.description);
let entry = CustomEnchantmentEntry {
id: enchantment.id,
description,
max_level: enchantment.max_level,
anvil_cost: enchantment.anvil_cost,
supported_items: enchantment.supported_items,
weight: enchantment.weight,
slots: enchantment.slots.into_iter().map(to_data_slot).collect(),
exclusive_set: enchantment.exclusive_set,
};
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server.enchantment_manager.register(entry).await)
}
async fn get_enchantment(
&mut self,
_res: Resource<WitEnchantmentManager>,
id: String,
) -> wasmtime::Result<Option<WitCustomEnchantment>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
if let Some(entry) = server.enchantment_manager.get(&id).await {
let description = self.add_text_component(entry.description)?;
return Ok(Some(WitCustomEnchantment {
id: entry.id,
description,
max_level: entry.max_level,
anvil_cost: entry.anvil_cost,
supported_items: entry.supported_items,
weight: entry.weight,
slots: entry.slots.iter().map(to_wit_slot).collect(),
exclusive_set: entry.exclusive_set,
}));
}
if let Some(vanilla) = find_vanilla_enchantment(&id) {
let description =
self.add_text_component(TextComponent::translate(vanilla.description, []))?;
return Ok(Some(WitCustomEnchantment {
id: vanilla.name.to_string(),
description,
max_level: vanilla.max_level.max(1) as u32,
anvil_cost: vanilla.anvil_cost,
supported_items: vanilla
.supported_items
.0
.first()
.copied()
.unwrap_or("")
.to_string(),
weight: vanilla.weight.max(1) as u32,
slots: vanilla.slots.iter().map(to_wit_slot).collect(),
exclusive_set: vanilla.exclusive_set.map_or_else(Vec::new, |tag| {
tag.0.iter().map(|s| (*s).to_string()).collect()
}),
}));
}
Ok(None)
}
async fn has_enchantment(
&mut self,
_res: Resource<WitEnchantmentManager>,
id: String,
) -> wasmtime::Result<bool> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
if server.enchantment_manager.has(&id).await {
return Ok(true);
}
Ok(find_vanilla_enchantment(&id).is_some())
}
async fn get_all_enchantment_ids(
&mut self,
_res: Resource<WitEnchantmentManager>,
) -> wasmtime::Result<Vec<String>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
let mut ids = server.enchantment_manager.get_all_ids().await;
for enc in Enchantment::ALL {
ids.push(enc.name.to_string());
}
Ok(ids)
}
async fn drop(&mut self, _rep: Resource<WitEnchantmentManager>) -> wasmtime::Result<()> {
Ok(())
}
}
#[must_use]
pub fn find_vanilla_enchantment(id: &str) -> Option<&'static Enchantment> {
Enchantment::from_name(id).or_else(|| {
id.strip_prefix("minecraft:")
.and_then(Enchantment::from_name)
})
}
#[must_use]
pub const fn to_data_slot(slot: WitAttributeModifierSlot) -> AttributeModifierSlot {
match slot {
WitAttributeModifierSlot::Any => AttributeModifierSlot::Any,
WitAttributeModifierSlot::MainHand => AttributeModifierSlot::MainHand,
WitAttributeModifierSlot::OffHand => AttributeModifierSlot::OffHand,
WitAttributeModifierSlot::Hand => AttributeModifierSlot::Hand,
WitAttributeModifierSlot::Feet => AttributeModifierSlot::Feet,
WitAttributeModifierSlot::Legs => AttributeModifierSlot::Legs,
WitAttributeModifierSlot::Chest => AttributeModifierSlot::Chest,
WitAttributeModifierSlot::Head => AttributeModifierSlot::Head,
WitAttributeModifierSlot::Armor => AttributeModifierSlot::Armor,
WitAttributeModifierSlot::Body => AttributeModifierSlot::Body,
WitAttributeModifierSlot::Saddle => AttributeModifierSlot::Saddle,
}
}
#[must_use]
pub const fn to_wit_slot(slot: &AttributeModifierSlot) -> WitAttributeModifierSlot {
match slot {
AttributeModifierSlot::Any => WitAttributeModifierSlot::Any,
AttributeModifierSlot::MainHand => WitAttributeModifierSlot::MainHand,
AttributeModifierSlot::OffHand => WitAttributeModifierSlot::OffHand,
AttributeModifierSlot::Hand => WitAttributeModifierSlot::Hand,
AttributeModifierSlot::Feet => WitAttributeModifierSlot::Feet,
AttributeModifierSlot::Legs => WitAttributeModifierSlot::Legs,
AttributeModifierSlot::Chest => WitAttributeModifierSlot::Chest,
AttributeModifierSlot::Head => WitAttributeModifierSlot::Head,
AttributeModifierSlot::Armor => WitAttributeModifierSlot::Armor,
AttributeModifierSlot::Body => WitAttributeModifierSlot::Body,
AttributeModifierSlot::Saddle => WitAttributeModifierSlot::Saddle,
}
}

View File

@@ -14,7 +14,8 @@ use crate::plugin::loader::wasm::wasm_host::{
Attribute, AttributeModifier as WitAttributeModifier,
ModifierOperation as WitModifierOperation,
},
common::{EntityPose, Position},
common::{EntityPose, NbtTree as WitNbtTree, Position},
damage_types::DamageType as WitDamageType,
entity::Host,
entity_types,
item_stack::ItemStack as WitHostItemStack,
@@ -162,6 +163,18 @@ pub const fn from_wit_equipment_slot(
}
}
#[must_use]
pub const fn to_wit_damage_type(damage_type: &pumpkin_data::damage::DamageType) -> WitDamageType {
// SAFETY: WIT enum is generated in the same order as the internal enum / id
unsafe { std::mem::transmute(damage_type.id) }
}
#[must_use]
pub fn from_wit_damage_type(wit: WitDamageType) -> pumpkin_data::damage::DamageType {
pumpkin_data::damage::DamageType::from_id(wit as u8)
.unwrap_or(pumpkin_data::damage::DamageType::GENERIC)
}
impl HostEntity for PluginHostState {
async fn get_id(&mut self, entity: Resource<Entity>) -> wasmtime::Result<u32> {
let entity = entity_from_resource(self, &entity)?;
@@ -533,10 +546,15 @@ impl HostEntity for PluginHostState {
.map_or(0.0, crate::entity::living::LivingEntity::get_max_health))
}
async fn damage(&mut self, entity: Resource<Entity>, amount: f32) -> wasmtime::Result<()> {
async fn damage(
&mut self,
entity: Resource<Entity>,
amount: f32,
damage_type: WitDamageType,
) -> wasmtime::Result<()> {
let entity = entity_from_resource(self, &entity)?;
entity
.damage(&*entity, amount, pumpkin_data::damage::DamageType::GENERIC)
.damage(&*entity, amount, from_wit_damage_type(damage_type))
.await;
Ok(())
}
@@ -1296,6 +1314,55 @@ impl HostEntity for PluginHostState {
}))
}
async fn set_custom_data(
&mut self,
this: Resource<Entity>,
namespace: String,
key: String,
value: WitNbtTree,
) -> wasmtime::Result<()> {
let entity = entity_from_resource(self, &this)?;
let base_entity = entity.get_entity();
let tag = super::common::from_wit_nbt_tree(&value).map_err(wasmtime::Error::msg)?;
base_entity.set_custom_data(&namespace, &key, tag).await;
Ok(())
}
async fn get_custom_data(
&mut self,
this: Resource<Entity>,
namespace: String,
key: String,
) -> wasmtime::Result<Option<WitNbtTree>> {
let entity = entity_from_resource(self, &this)?;
let base_entity = entity.get_entity();
let tag = base_entity.get_custom_data(&namespace, &key).await;
Ok(tag.map(super::common::to_wit_nbt_tree))
}
async fn remove_custom_data(
&mut self,
this: Resource<Entity>,
namespace: String,
key: String,
) -> wasmtime::Result<()> {
let entity = entity_from_resource(self, &this)?;
let base_entity = entity.get_entity();
base_entity.remove_custom_data(&namespace, &key).await;
Ok(())
}
async fn has_custom_data(
&mut self,
this: Resource<Entity>,
namespace: String,
key: String,
) -> wasmtime::Result<bool> {
let entity = entity_from_resource(self, &this)?;
let base_entity = entity.get_entity();
Ok(base_entity.has_custom_data(&namespace, &key).await)
}
async fn drop(&mut self, rep: Resource<Entity>) -> wasmtime::Result<()> {
let _ = self
.resource_table
@@ -1328,9 +1395,17 @@ impl Goal for CustomWasmGoal {
return false;
};
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_res.rep()),
);
return false;
};
plugin
let server_rep = server_res.rep();
let entity_rep = entity_res.rep();
let result = plugin
.call_handle_ai_goal_can_start(
&mut *store,
self.goal_id,
@@ -1338,7 +1413,20 @@ impl Goal for CustomWasmGoal {
entity_res,
)
.await
.unwrap_or(false)
.unwrap_or(false);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::EntityResource>(
wasmtime::component::Resource::new_own(entity_rep),
);
result
}
}
} else {
@@ -1360,9 +1448,17 @@ impl Goal for CustomWasmGoal {
return false;
};
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_res.rep()),
);
return false;
};
plugin
let server_rep = server_res.rep();
let entity_rep = entity_res.rep();
let result = plugin
.call_handle_ai_goal_should_continue(
&mut *store,
self.goal_id,
@@ -1370,7 +1466,20 @@ impl Goal for CustomWasmGoal {
entity_res,
)
.await
.unwrap_or(false)
.unwrap_or(false);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::EntityResource>(
wasmtime::component::Resource::new_own(entity_rep),
);
result
}
}
} else {
@@ -1392,8 +1501,16 @@ impl Goal for CustomWasmGoal {
return;
};
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_res.rep()),
);
return;
};
let server_rep = server_res.rep();
let entity_rep = entity_res.rep();
let _ = plugin
.call_handle_ai_goal_start(
&mut *store,
@@ -1402,6 +1519,18 @@ impl Goal for CustomWasmGoal {
entity_res,
)
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::EntityResource>(
wasmtime::component::Resource::new_own(entity_rep),
);
}
}
}
@@ -1421,8 +1550,16 @@ impl Goal for CustomWasmGoal {
return;
};
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_res.rep()),
);
return;
};
let server_rep = server_res.rep();
let entity_rep = entity_res.rep();
let _ = plugin
.call_handle_ai_goal_tick(
&mut *store,
@@ -1431,6 +1568,18 @@ impl Goal for CustomWasmGoal {
entity_res,
)
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::EntityResource>(
wasmtime::component::Resource::new_own(entity_rep),
);
}
}
}
@@ -1450,8 +1599,16 @@ impl Goal for CustomWasmGoal {
return;
};
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_res.rep()),
);
return;
};
let server_rep = server_res.rep();
let entity_rep = entity_res.rep();
let _ = plugin
.call_handle_ai_goal_stop(
&mut *store,
@@ -1460,6 +1617,18 @@ impl Goal for CustomWasmGoal {
entity_res,
)
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::EntityResource>(
wasmtime::component::Resource::new_own(entity_rep),
);
}
}
}

View File

@@ -53,8 +53,9 @@ use crate::plugin::{
state::PluginHostState,
wit::v0_1::{
events::{
ToFromWasmEvent, consume_player, consume_world, from_wasm_block_name,
from_wasm_block_position, to_wasm_block_name, to_wasm_block_position,
ToFromWasmEvent, cleanup_event, consume_player, consume_world,
from_wasm_block_name, from_wasm_block_position, to_wasm_block_name,
to_wasm_block_position,
},
pumpkin::plugin::event::{
BellResonateEventData, BellRingEventData, BlockBreakEventData, BlockBrushEventData,
@@ -572,7 +573,8 @@ impl ToFromWasmEvent for BellResonateEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BellResonateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -604,7 +606,8 @@ impl ToFromWasmEvent for BellRingEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BellRingEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -638,7 +641,8 @@ impl ToFromWasmEvent for BlockBrushEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockBrushEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -672,7 +676,8 @@ impl ToFromWasmEvent for BlockCookEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockCookEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -732,7 +737,8 @@ impl ToFromWasmEvent for BlockDispenseArmorEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockDispenseArmorEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -770,7 +776,8 @@ impl ToFromWasmEvent for BlockDispenseLootEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockDispenseLootEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -814,7 +821,8 @@ impl ToFromWasmEvent for BlockDropItemEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockDropItemEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -840,7 +848,8 @@ impl ToFromWasmEvent for BlockExpEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockExpEvent(data) = event {
self.exp = data.exp;
}
@@ -882,7 +891,8 @@ impl ToFromWasmEvent for BlockFertilizeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockFertilizeEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -919,7 +929,8 @@ impl ToFromWasmEvent for BlockMultiPlaceEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockMultiPlaceEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -952,7 +963,8 @@ impl ToFromWasmEvent for BlockReceiveGameEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockReceiveGameEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -985,7 +997,8 @@ impl ToFromWasmEvent for BlockShearEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockShearEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1015,7 +1028,8 @@ impl ToFromWasmEvent for BlockSpreadEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BlockSpreadEvent(data) = event {
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
self.cancelled = data.cancelled;
@@ -1049,7 +1063,8 @@ impl ToFromWasmEvent for BrewingStartEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BrewingStartEvent(data) = event {
self.brewing_time = data.brewing_time;
self.cancelled = data.cancelled;
@@ -1087,7 +1102,8 @@ impl ToFromWasmEvent for CampfireStartEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::CampfireStartEvent(data) = event {
self.cooking_time = data.cooking_time;
self.cancelled = data.cancelled;
@@ -1118,7 +1134,8 @@ impl ToFromWasmEvent for CauldronLevelChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::CauldronLevelChangeEvent(data) = event {
self.new_level = data.new_level;
self.cancelled = data.cancelled;
@@ -1151,7 +1168,8 @@ impl ToFromWasmEvent for CrafterCraftEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::CrafterCraftEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1179,7 +1197,8 @@ impl ToFromWasmEvent for EntityBlockFormEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityBlockFormEvent(data) = event {
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
self.cancelled = data.cancelled;
@@ -1209,7 +1228,8 @@ impl ToFromWasmEvent for FluidLevelChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::FluidLevelChangeEvent(data) = event {
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
self.cancelled = data.cancelled;
@@ -1263,7 +1283,8 @@ impl ToFromWasmEvent for LeavesDecayEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::LeavesDecayEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1294,7 +1315,8 @@ impl ToFromWasmEvent for MoistureChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::MoistureChangeEvent(data) = event {
self.new_moisture = data.new_moisture;
self.cancelled = data.cancelled;
@@ -1327,7 +1349,8 @@ impl ToFromWasmEvent for SculkBloomEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::SculkBloomEvent(data) = event {
self.charge = data.charge;
self.cancelled = data.cancelled;
@@ -1363,7 +1386,8 @@ impl ToFromWasmEvent for VaultDisplayItemEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VaultDisplayItemEvent(data) = event {
self.cancelled = data.cancelled;
}

View File

@@ -0,0 +1,678 @@
use crate::plugin::loader::wasm::wasm_host::{
state::{
EntityResource, ItemStackResource, PlayerResource, PluginHostState, ServerResource,
TextComponentResource, WorldResource,
},
wit::v0_1::pumpkin::plugin::{
entity::Entity, event::Event, item_stack::ItemStack, player::Player, server::Server,
text::TextComponent, world::World,
},
};
use wasmtime::component::Resource;
pub fn cleanup_player(state: &mut PluginHostState, player: &Resource<Player>) {
let _ = state
.resource_table
.delete::<PlayerResource>(Resource::new_own(player.rep()));
}
pub fn cleanup_world(state: &mut PluginHostState, world: &Resource<World>) {
let _ = state
.resource_table
.delete::<WorldResource>(Resource::new_own(world.rep()));
}
pub fn cleanup_text_component(
state: &mut PluginHostState,
text_component: &Resource<TextComponent>,
) {
let _ = state
.resource_table
.delete::<TextComponentResource>(Resource::new_own(text_component.rep()));
}
pub fn cleanup_item_stack(state: &mut PluginHostState, item: &Resource<ItemStack>) {
let _ = state
.resource_table
.delete::<ItemStackResource>(Resource::new_own(item.rep()));
}
pub fn cleanup_entity(state: &mut PluginHostState, entity: &Resource<Entity>) {
let _ = state
.resource_table
.delete::<EntityResource>(Resource::new_own(entity.rep()));
}
pub fn cleanup_server(state: &mut PluginHostState, server: &Resource<Server>) {
let _ = state
.resource_table
.delete::<ServerResource>(Resource::new_own(server.rep()));
}
#[allow(clippy::too_many_lines, clippy::match_same_arms)]
pub fn cleanup_event(event: &Event, state: &mut PluginHostState) {
match event {
Event::PlayerJoinEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.join_message);
}
Event::PlayerLeaveEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.leave_message);
}
Event::PlayerLoginEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.kick_message);
}
Event::PlayerChatEvent(data) => {
cleanup_player(state, &data.player);
for res in &data.recipients {
cleanup_player(state, res);
}
}
Event::PlayerCommandSendEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerPermissionCheckEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerMoveEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerTeleportEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerChangeWorldEvent(data) => {
cleanup_player(state, &data.player);
cleanup_world(state, &data.previous_world);
cleanup_world(state, &data.new_world);
}
Event::PlayerRespawnEvent(data) => {
cleanup_player(state, &data.player);
cleanup_world(state, &data.previous_world);
cleanup_world(state, &data.respawned_world);
}
Event::PlayerExpChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerItemHeldEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerChangedMainHandEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerGamemodeChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerCustomPayloadEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerFishEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerEggThrowEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerInteractUnknownEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerInteractEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerInteractEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerToggleSneakEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerToggleFlightEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerToggleSprintEvent(data) => {
cleanup_player(state, &data.player);
}
Event::InventoryClickEvent(data) => {
cleanup_player(state, &data.player);
if let Some(res) = &data.clicked_item {
cleanup_item_stack(state, res);
}
if let Some(res) = &data.cursor {
cleanup_item_stack(state, res);
}
}
Event::InventoryCloseEvent(data) => {
cleanup_player(state, &data.player);
}
Event::BlockRedstoneEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BlockBreakEvent(data) => {
if let Some(res) = &data.player {
cleanup_player(state, res);
}
}
Event::BlockBurnEvent(_) => {}
Event::BlockCanBuildEvent(data) => {
cleanup_player(state, &data.player);
}
Event::BlockGrowEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BlockPlaceEvent(data) => {
cleanup_player(state, &data.player);
}
Event::BedrockFormResponseEvent(data) => {
cleanup_player(state, &data.player);
}
Event::CustomClickActionEvent(data) => {
cleanup_player(state, &data.player);
}
Event::ServerCommandEvent(_) => {}
Event::ServerListPingEvent(data) => {
cleanup_text_component(state, &data.motd);
}
Event::ServerLoadEvent(_) => {}
Event::SpawnChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::ServerBroadcastEvent(data) => {
cleanup_text_component(state, &data.message);
cleanup_text_component(state, &data.sender);
}
Event::ServerTickStartEvent(_) => {}
Event::ServerTickEndEvent(_) => {}
Event::PacketReceivedEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PacketSentEvent(data) => {
cleanup_player(state, &data.player);
}
Event::ChunkLoadEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::ChunkSaveEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::ChunkSendEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::EntityDamageEvent(_) => {}
Event::EntityDeathEvent(_) => {}
Event::PlayerDeathEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.death_message);
}
Event::EntitySpawnEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::EntityCombustEvent(_) => {}
Event::EntityRegainHealthEvent(_) => {}
Event::EntityAirChangeEvent(_) => {}
Event::EntityBreedEvent(_) => {}
Event::EntityDismountEvent(_) => {}
Event::EntityDyeEvent(data) => {
if let Some(res) = &data.player {
cleanup_player(state, res);
}
}
Event::EntityEnterLoveModeEvent(_) => {}
Event::EntityExplodeEvent(_) => {}
Event::EntityMountEvent(_) => {}
Event::EntityPickupItemEvent(_) => {}
Event::EntityPortalEvent(_) => {}
Event::EntityResurrectEvent(_) => {}
Event::EntityShootBowEvent(_) => {}
Event::EntityTameEvent(data) => {
cleanup_player(state, &data.owner);
}
Event::EntityTargetEvent(_) => {}
Event::EntityTeleportEvent(_) => {}
Event::EntityToggleGlideEvent(_) => {}
Event::EntityTransformEvent(_) => {}
Event::PlayerItemConsumeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerItemDamageEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerDropItemEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerBedEnterEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerBedLeaveEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerBucketEmptyEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerBucketFillEvent(data) => {
cleanup_player(state, &data.player);
}
Event::BlockDamageEvent(data) => {
cleanup_player(state, &data.player);
}
Event::BlockIgniteEvent(_) => {}
Event::BlockFromToEvent(_) => {}
Event::BlockFormEvent(_) => {}
Event::BlockFadeEvent(_) => {}
Event::BlockDispenseEvent(_) => {}
Event::BlockExplodeEvent(_) => {}
Event::BlockPhysicsEvent(_) => {}
Event::BlockPistonExtendEvent(_) => {}
Event::BlockPistonRetractEvent(_) => {}
Event::NotePlayEvent(_) => {}
Event::SignChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::SpongeAbsorbEvent(_) => {}
Event::TntPrimeEvent(_) => {}
Event::WeatherChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::ThunderChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::WorldLoadEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::WorldUnloadEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::AsyncStructureGenerateEvent(_) => {}
Event::AsyncStructureSpawnEvent(_) => {}
Event::ChunkPopulateEvent(_) => {}
Event::ChunkUnloadEvent(_) => {}
Event::EntitiesLoadEvent(_) => {}
Event::EntitiesUnloadEvent(_) => {}
Event::GenericGameEvent(_) => {}
Event::LootGenerateEvent(_) => {}
Event::PortalCreateEvent(_) => {}
Event::StructureGrowEvent(_) => {}
Event::TimeSkipEvent(_) => {}
Event::WorldInitEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::WorldSaveEvent(_) => {}
Event::InventoryOpenEvent(data) => {
cleanup_player(state, &data.player);
}
Event::InventoryDragEvent(data) => {
cleanup_player(state, &data.player);
}
Event::CraftItemEvent(data) => {
cleanup_player(state, &data.player);
}
Event::FurnaceSmeltEvent(_) => {}
Event::BrewEvent(_) => {}
Event::BrewingStandFuelEvent(_) => {}
Event::FurnaceBurnEvent(_) => {}
Event::FurnaceExtractEvent(data) => {
cleanup_player(state, &data.player);
}
Event::FurnaceStartSmeltEvent(_) => {}
Event::HopperInventorySearchEvent(_) => {}
Event::InventoryCreativeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::InventoryInteractEvent(data) => {
cleanup_player(state, &data.player);
}
Event::InventoryMoveItemEvent(_) => {}
Event::InventoryPickupItemEvent(_) => {}
Event::PrepareAnvilEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PrepareGrindstoneEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PrepareInventoryResultEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PrepareItemCraftEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PrepareSmithingEvent(data) => {
cleanup_player(state, &data.player);
}
Event::SmithItemEvent(data) => {
cleanup_player(state, &data.player);
}
Event::TradeSelectEvent(data) => {
cleanup_player(state, &data.player);
}
Event::VehicleBlockCollisionEvent(_) => {}
Event::VehicleCollisionEvent(_) => {}
Event::VehicleCreateEvent(_) => {}
Event::VehicleDamageEvent(_) => {}
Event::VehicleDestroyEvent(_) => {}
Event::VehicleEnterEvent(_) => {}
Event::VehicleEntityCollisionEvent(_) => {}
Event::VehicleExitEvent(_) => {}
Event::VehicleMoveEvent(_) => {}
Event::VehicleUpdateEvent(_) => {}
Event::PrepareItemEnchantEvent(data) => {
cleanup_player(state, &data.player);
cleanup_item_stack(state, &data.item);
}
Event::EnchantItemEvent(data) => {
cleanup_player(state, &data.player);
cleanup_item_stack(state, &data.item);
}
Event::MapInitializeEvent(_) => {}
Event::HangingBreakEvent(_) => {}
Event::HangingBreakByEntityEvent(_) => {}
Event::HangingPlaceEvent(data) => {
if let Some(res) = &data.player {
cleanup_player(state, res);
}
}
Event::BellResonateEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BellRingEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BlockBrushEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_player(state, &data.player);
cleanup_item_stack(state, &data.item);
}
Event::BlockCookEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.source);
cleanup_item_stack(state, &data.result);
}
Event::BlockDamageAbortEvent(data) => {
cleanup_player(state, &data.player);
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.item_in_hand);
}
Event::BlockDispenseArmorEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.item);
}
Event::BlockDispenseLootEvent(data) => {
cleanup_world(state, &data.target_world);
for res in &data.items {
cleanup_item_stack(state, res);
}
}
Event::BlockDropItemEvent(data) => {
cleanup_world(state, &data.target_world);
if let Some(res) = &data.player {
cleanup_player(state, res);
}
for res in &data.items {
cleanup_item_stack(state, res);
}
}
Event::BlockExpEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BlockFertilizeEvent(data) => {
cleanup_world(state, &data.target_world);
if let Some(res) = &data.player {
cleanup_player(state, res);
}
}
Event::BlockMultiPlaceEvent(data) => {
cleanup_player(state, &data.player);
cleanup_world(state, &data.target_world);
}
Event::BlockReceiveGameEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BlockShearEntityEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.item);
}
Event::BlockSpreadEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::BrewingStartEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::CampfireStartEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.item);
}
Event::CauldronLevelChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::CrafterCraftEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.result);
}
Event::EntityBlockFormEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::FluidLevelChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::InventoryBlockStartEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::LeavesDecayEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::MoistureChangeEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::SculkBloomEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::VaultDisplayItemEvent(data) => {
cleanup_world(state, &data.target_world);
cleanup_item_stack(state, &data.item);
}
Event::CreatureSpawnEvent(data) => {
cleanup_world(state, &data.target_world);
}
Event::EnderDragonChangePhaseEvent(_) => {}
Event::EntityBreakDoorEvent(_) => {}
Event::EntityChangeBlockEvent(_) => {}
Event::EntityDamageByBlockEvent(_) => {}
Event::EntityDamageByEntityEvent(_) => {}
Event::EntityDropItemEvent(_) => {}
Event::EntityEnterBlockEvent(_) => {}
Event::EntityExhaustionEvent(_) => {}
Event::EntityInteractEvent(_) => {}
Event::EntityKnockbackEvent(_) => {}
Event::EntityPlaceEvent(_) => {}
Event::EntityPoseChangeEvent(_) => {}
Event::EntityPotionEffectEvent(_) => {}
Event::EntitySpellCastEvent(_) => {}
Event::EntityTargetLivingEntityEvent(_) => {}
Event::EntityToggleSwimEvent(_) => {}
Event::ExplosionPrimeEvent(_) => {}
Event::FireworkExplodeEvent(_) => {}
Event::FoodLevelChangeEvent(_) => {}
Event::ItemDespawnEvent(_) => {}
Event::ItemMergeEvent(_) => {}
Event::ItemSpawnEvent(_) => {}
Event::PiglinBarterEvent(data) => {
cleanup_item_stack(state, &data.input_item);
for res in &data.outcome {
cleanup_item_stack(state, res);
}
}
Event::ProjectileHitEvent(_) => {}
Event::ProjectileLaunchEvent(_) => {}
Event::SheepDyeWoolEvent(_) => {}
Event::SheepRegrowWoolEvent(_) => {}
Event::SlimeSplitEvent(_) => {}
Event::StriderTemperatureChangeEvent(_) => {}
Event::VillagerAcquireTradeEvent(_) => {}
Event::VillagerCareerChangeEvent(_) => {}
Event::VillagerReplenishTradeEvent(_) => {}
Event::WardenAngerChangeEvent(_) => {}
Event::AreaEffectCloudApplyEvent(_) => {}
Event::ArrowBodyCountChangeEvent(_) => {}
Event::BatToggleSleepEvent(_) => {}
Event::CreeperPowerEvent(_) => {}
Event::EntityCombustByBlockEvent(_) => {}
Event::EntityCombustByEntityEvent(_) => {}
Event::EntityKnockbackByEntityEvent(_) => {}
Event::EntityPortalEnterEvent(_) => {}
Event::EntityPortalExitEvent(_) => {}
Event::EntityRemoveEvent(_) => {}
Event::EntityTargetBlockEvent(_) => {}
Event::EntityUnleashEvent(_) => {}
Event::ExpBottleEvent(_) => {}
Event::HorseJumpEvent(_) => {}
Event::LingeringPotionSplashEvent(_) => {}
Event::PigZapEvent(_) => {}
Event::PigZombieAngerEvent(_) => {}
Event::PotionSplashEvent(_) => {}
Event::SpawnerSpawnEvent(_) => {}
Event::TrialSpawnerSpawnEvent(_) => {}
Event::VillagerReputationChangeEvent(_) => {}
Event::AsyncPlayerChatEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.format);
}
Event::AsyncPlayerPreLoginEvent(data) => {
cleanup_text_component(state, &data.kick_message);
}
Event::PlayerAdvancementDoneEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerAnimationEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerArmorStandManipulateEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerBucketEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerChangedWorldEvent(data) => {
cleanup_player(state, &data.player);
cleanup_world(state, &data.from_world);
cleanup_world(state, &data.to_world);
}
Event::PlayerChannelEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerCommandPreprocessEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerEditBookEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerElytraBoostEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerExpCooldownChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerHarvestBlockEvent(data) => {
cleanup_player(state, &data.player);
for res in &data.harvested_items {
cleanup_item_stack(state, res);
}
}
Event::PlayerHideEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerItemBreakEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerItemMendEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerKickEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerLeashEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerLevelChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerLocaleChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerNameEntityEvent(data) => {
cleanup_player(state, &data.player);
cleanup_text_component(state, &data.name);
}
Event::PlayerOpenSignEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerPortalEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerPreLoginEvent(data) => {
cleanup_text_component(state, &data.kick_message);
}
Event::PlayerRiptideEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerShearEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerShowEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerSpawnChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerStatisticIncrementEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerSwapHandsEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerTakeLecternBookEvent(data) => {
cleanup_player(state, &data.player);
cleanup_item_stack(state, &data.book);
}
Event::PlayerUnleashEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerVelocityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerInputEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerInteractAtEntityEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerLinksSendEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerPickupArrowEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerRecipeBookClickEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerRecipeBookSettingsChangeEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerRecipeDiscoverEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerRegisterChannelEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerResourcePackStatusEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerSpawnLocationEvent(data) => {
cleanup_player(state, &data.player);
}
Event::PlayerUnregisterChannelEvent(data) => {
cleanup_player(state, &data.player);
}
Event::RaidFinishEvent(_) => {}
Event::RaidSpawnWaveEvent(_) => {}
Event::RaidStopEvent(_) => {}
Event::RaidTriggerEvent(_) => {}
Event::LightningStrikeEvent(_) => {}
}
}

View File

@@ -67,10 +67,11 @@ use crate::plugin::{
loader::wasm::wasm_host::{
state::PluginHostState,
wit::v0_1::{
entity::{from_wit_damage_type, to_wit_damage_type},
events::{
ToFromWasmEvent, consume_player, consume_text_component, consume_world,
from_wasm_block_position, from_wasm_position, to_wasm_block_position,
to_wasm_position,
ToFromWasmEvent, cleanup_event, consume_player, consume_text_component,
consume_world, from_wasm_block_position, from_wasm_position,
to_wasm_block_position, to_wasm_position,
},
pumpkin::plugin::event::{
AreaEffectCloudApplyEventData, ArrowBodyCountChangeEventData,
@@ -112,7 +113,7 @@ impl ToFromWasmEvent for EntityDamageEvent {
Event::EntityDamageEvent(EntityDamageEventData {
entity_id: self.entity_id,
damage: self.damage,
cause: self.cause.clone(),
damage_type: to_wit_damage_type(&self.damage_type),
cancelled: self.cancelled,
})
}
@@ -122,7 +123,7 @@ impl ToFromWasmEvent for EntityDamageEvent {
Event::EntityDamageEvent(data) => Self {
entity_id: data.entity_id,
damage: data.damage,
cause: data.cause,
damage_type: from_wit_damage_type(data.damage_type),
cancelled: data.cancelled,
},
_ => panic!("unexpected event type"),
@@ -655,7 +656,8 @@ impl ToFromWasmEvent for CreatureSpawnEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::CreatureSpawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -686,7 +688,8 @@ impl ToFromWasmEvent for EnderDragonChangePhaseEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EnderDragonChangePhaseEvent(data) = event {
self.cancelled = data.cancelled;
self.new_phase = data.new_phase;
@@ -715,7 +718,8 @@ impl ToFromWasmEvent for EntityBreakDoorEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityBreakDoorEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -743,7 +747,8 @@ impl ToFromWasmEvent for EntityChangeBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityChangeBlockEvent(data) = event {
self.cancelled = data.cancelled;
self.new_block = data.new_block;
@@ -774,7 +779,8 @@ impl ToFromWasmEvent for EntityDamageByBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityDamageByBlockEvent(data) = event {
self.cancelled = data.cancelled;
self.damage = data.damage;
@@ -806,7 +812,8 @@ impl ToFromWasmEvent for EntityDamageByEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityDamageByEntityEvent(data) = event {
self.cancelled = data.cancelled;
self.damage = data.damage;
@@ -837,7 +844,8 @@ impl ToFromWasmEvent for EntityDropItemEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityDropItemEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -865,7 +873,8 @@ impl ToFromWasmEvent for EntityEnterBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityEnterBlockEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -892,7 +901,8 @@ impl ToFromWasmEvent for EntityExhaustionEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityExhaustionEvent(data) = event {
self.cancelled = data.cancelled;
self.exhaustion = data.exhaustion;
@@ -920,7 +930,8 @@ impl ToFromWasmEvent for EntityInteractEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityInteractEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -948,7 +959,8 @@ impl ToFromWasmEvent for EntityKnockbackEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityKnockbackEvent(data) = event {
self.cancelled = data.cancelled;
self.knockback = from_wasm_position(data.knockback);
@@ -978,7 +990,8 @@ impl ToFromWasmEvent for EntityPlaceEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityPlaceEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1006,7 +1019,8 @@ impl ToFromWasmEvent for EntityPoseChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityPoseChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.pose = data.pose;
@@ -1036,7 +1050,8 @@ impl ToFromWasmEvent for EntityPotionEffectEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityPotionEffectEvent(data) = event {
self.cancelled = data.cancelled;
self.duration = data.duration;
@@ -1067,7 +1082,8 @@ impl ToFromWasmEvent for EntitySpellCastEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntitySpellCastEvent(data) = event {
self.cancelled = data.cancelled;
self.spell = data.spell;
@@ -1096,7 +1112,8 @@ impl ToFromWasmEvent for EntityTargetLivingEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityTargetLivingEntityEvent(data) = event {
self.cancelled = data.cancelled;
self.target_id = data.target_id;
@@ -1125,7 +1142,8 @@ impl ToFromWasmEvent for EntityToggleSwimEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityToggleSwimEvent(data) = event {
self.cancelled = data.cancelled;
self.is_swimming = data.is_swimming;
@@ -1154,7 +1172,8 @@ impl ToFromWasmEvent for ExplosionPrimeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ExplosionPrimeEvent(data) = event {
self.cancelled = data.cancelled;
self.radius = data.radius;
@@ -1183,7 +1202,8 @@ impl ToFromWasmEvent for FireworkExplodeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::FireworkExplodeEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1209,7 +1229,8 @@ impl ToFromWasmEvent for FoodLevelChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::FoodLevelChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.food_level = data.food_level;
@@ -1236,7 +1257,8 @@ impl ToFromWasmEvent for ItemDespawnEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ItemDespawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1262,7 +1284,8 @@ impl ToFromWasmEvent for ItemMergeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ItemMergeEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1290,7 +1313,8 @@ impl ToFromWasmEvent for ItemSpawnEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ItemSpawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1331,7 +1355,8 @@ impl ToFromWasmEvent for PiglinBarterEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PiglinBarterEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1355,7 +1380,8 @@ impl ToFromWasmEvent for ProjectileHitEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ProjectileHitEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1383,7 +1409,8 @@ impl ToFromWasmEvent for ProjectileLaunchEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ProjectileLaunchEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1411,7 +1438,8 @@ impl ToFromWasmEvent for SheepDyeWoolEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::SheepDyeWoolEvent(data) = event {
self.cancelled = data.cancelled;
self.dye_color = data.dye_color;
@@ -1439,7 +1467,8 @@ impl ToFromWasmEvent for SheepRegrowWoolEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::SheepRegrowWoolEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1465,7 +1494,8 @@ impl ToFromWasmEvent for SlimeSplitEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::SlimeSplitEvent(data) = event {
self.cancelled = data.cancelled;
self.count = data.count;
@@ -1493,7 +1523,8 @@ impl ToFromWasmEvent for StriderTemperatureChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::StriderTemperatureChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.is_shivering = data.is_shivering;
@@ -1521,7 +1552,8 @@ impl ToFromWasmEvent for VillagerAcquireTradeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VillagerAcquireTradeEvent(data) = event {
self.cancelled = data.cancelled;
self.recipe_index = data.recipe_index;
@@ -1550,7 +1582,8 @@ impl ToFromWasmEvent for VillagerCareerChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VillagerCareerChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.profession = data.profession;
@@ -1579,7 +1612,8 @@ impl ToFromWasmEvent for VillagerReplenishTradeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VillagerReplenishTradeEvent(data) = event {
self.cancelled = data.cancelled;
self.restock_quantity = data.restock_quantity;
@@ -1609,7 +1643,8 @@ impl ToFromWasmEvent for WardenAngerChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::WardenAngerChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.new_anger = data.new_anger;
@@ -1639,7 +1674,8 @@ impl ToFromWasmEvent for AreaEffectCloudApplyEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::AreaEffectCloudApplyEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1667,7 +1703,8 @@ impl ToFromWasmEvent for ArrowBodyCountChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ArrowBodyCountChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.new_amount = data.new_amount;
@@ -1696,7 +1733,8 @@ impl ToFromWasmEvent for BatToggleSleepEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::BatToggleSleepEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1724,7 +1762,8 @@ impl ToFromWasmEvent for CreeperPowerEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::CreeperPowerEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1753,7 +1792,8 @@ impl ToFromWasmEvent for EntityCombustByBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityCombustByBlockEvent(data) = event {
self.cancelled = data.cancelled;
self.duration = data.duration;
@@ -1783,7 +1823,8 @@ impl ToFromWasmEvent for EntityCombustByEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityCombustByEntityEvent(data) = event {
self.cancelled = data.cancelled;
self.duration = data.duration;
@@ -1815,7 +1856,8 @@ impl ToFromWasmEvent for EntityKnockbackByEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityKnockbackByEntityEvent(data) = event {
self.cancelled = data.cancelled;
self.force = data.force;
@@ -1848,7 +1890,8 @@ impl ToFromWasmEvent for EntityPortalEnterEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityPortalEnterEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1876,7 +1919,8 @@ impl ToFromWasmEvent for EntityPortalExitEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityPortalExitEvent(data) = event {
self.cancelled = data.cancelled;
self.to_pos = data.to_pos.map(from_wasm_block_position);
@@ -1905,7 +1949,8 @@ impl ToFromWasmEvent for EntityRemoveEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityRemoveEvent(data) = event {
self.cancelled = data.cancelled;
self.cause = data.cause;
@@ -1933,7 +1978,8 @@ impl ToFromWasmEvent for EntityTargetBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityTargetBlockEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1960,7 +2006,8 @@ impl ToFromWasmEvent for EntityUnleashEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntityUnleashEvent(data) = event {
self.cancelled = data.cancelled;
self.reason = data.reason;
@@ -1990,7 +2037,8 @@ impl ToFromWasmEvent for ExpBottleEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ExpBottleEvent(data) = event {
self.cancelled = data.cancelled;
self.experience = data.experience;
@@ -2021,7 +2069,8 @@ impl ToFromWasmEvent for HorseJumpEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::HorseJumpEvent(data) = event {
self.cancelled = data.cancelled;
self.power = data.power;
@@ -2050,7 +2099,8 @@ impl ToFromWasmEvent for LingeringPotionSplashEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::LingeringPotionSplashEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2079,7 +2129,8 @@ impl ToFromWasmEvent for PigZapEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PigZapEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2108,7 +2159,8 @@ impl ToFromWasmEvent for PigZombieAngerEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PigZombieAngerEvent(data) = event {
self.cancelled = data.cancelled;
self.new_anger = data.new_anger;
@@ -2139,7 +2191,8 @@ impl ToFromWasmEvent for PotionSplashEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PotionSplashEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2168,7 +2221,8 @@ impl ToFromWasmEvent for SpawnerSpawnEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::SpawnerSpawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2195,7 +2249,8 @@ impl ToFromWasmEvent for TrialSpawnerSpawnEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::TrialSpawnerSpawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2223,7 +2278,8 @@ impl ToFromWasmEvent for VillagerReputationChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VillagerReputationChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.reputation_change = data.reputation_change;

View File

@@ -6,7 +6,7 @@ use crate::plugin::{
loader::wasm::wasm_host::{
state::PluginHostState,
wit::v0_1::{
events::{ToFromWasmEvent, to_wasm_block_position},
events::{ToFromWasmEvent, cleanup_event, to_wasm_block_position},
pumpkin::plugin::event::{
Event, HangingBreakByEntityEventData, HangingBreakEventData, HangingPlaceEventData,
},
@@ -23,7 +23,8 @@ impl ToFromWasmEvent for HangingBreakEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::HangingBreakEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -48,7 +49,8 @@ impl ToFromWasmEvent for HangingBreakByEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::HangingBreakByEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -81,7 +83,8 @@ impl ToFromWasmEvent for HangingPlaceEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::HangingPlaceEvent(data) = event {
self.cancelled = data.cancelled;
}

View File

@@ -24,6 +24,7 @@ use crate::{
};
pub mod block;
pub mod cleanup;
pub mod enchantment;
pub mod entity;
pub mod hanging;
@@ -34,6 +35,8 @@ pub mod server;
pub mod vehicle;
pub mod world;
pub use cleanup::*;
impl pumpkin::plugin::event::Host for PluginHostState {}
pub struct WasmPluginEventHandler {
@@ -230,15 +233,32 @@ impl<E: Payload + ToFromWasmEvent> EventHandler<E> for WasmPluginEventHandler {
fn handle<'a>(&'a self, server: &'a Arc<Server>, event: &'a E) -> BoxFuture<'a, ()> {
Box::pin(async {
let mut store = self.plugin.store.lock().await;
let event = event.to_wasm_event(store.data_mut());
let wasm_event = event.to_wasm_event(store.data_mut());
match self.plugin.plugin_instance {
PluginInstance::V0_1(ref plugin) => {
let Ok(server) = store.data_mut().add_server(server.clone()) else {
let Ok(server_res) = store.data_mut().add_server(server.clone()) else {
cleanup_event(&wasm_event, store.data_mut());
return;
};
let _ = plugin
.call_handle_event(&mut *store, self.handler_id, server, &event)
let server_rep = server_res.rep();
let result = plugin
.call_handle_event(&mut *store, self.handler_id, server_res, &wasm_event)
.await;
match result {
Ok(returned_event) => {
cleanup_event(&returned_event, store.data_mut());
cleanup_event(&wasm_event, store.data_mut());
}
Err(_) => {
cleanup_event(&wasm_event, store.data_mut());
}
}
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
}
}
})
@@ -254,15 +274,29 @@ impl<E: Payload + ToFromWasmEvent> EventHandler<E> for WasmPluginEventHandler {
let wasm_event = event.to_wasm_event(store.data_mut());
match self.plugin.plugin_instance {
PluginInstance::V0_1(ref plugin) => {
let Ok(server) = store.data_mut().add_server(server.clone()) else {
let Ok(server_res) = store.data_mut().add_server(server.clone()) else {
cleanup_event(&wasm_event, store.data_mut());
return;
};
if let Ok(returned_event) = plugin
.call_handle_event(&mut *store, self.handler_id, server, &wasm_event)
.await
{
event.apply_wasm_event(returned_event, store.data_mut());
let server_rep = server_res.rep();
let result = plugin
.call_handle_event(&mut *store, self.handler_id, server_res, &wasm_event)
.await;
match result {
Ok(returned_event) => {
event.apply_wasm_event(returned_event, store.data_mut());
cleanup_event(&wasm_event, store.data_mut());
}
Err(_) => {
cleanup_event(&wasm_event, store.data_mut());
}
}
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
}
}
})

View File

@@ -7,13 +7,14 @@ use crate::plugin::{
state::PluginHostState,
wit::v0_1::{
events::{
ToFromWasmEvent, consume_player, consume_text_component, consume_world,
from_wasm_block_name, from_wasm_block_position, from_wasm_click_type,
from_wasm_entity_interaction_action, from_wasm_entity_type, from_wasm_game_mode,
from_wasm_hand, from_wasm_position, to_wasm_block_position, to_wasm_click_type,
to_wasm_entity_interaction_action, to_wasm_entity_type, to_wasm_game_mode,
to_wasm_hand, to_wasm_position,
ToFromWasmEvent, cleanup_event, consume_player, consume_text_component,
consume_world, from_wasm_block_name, from_wasm_block_position,
from_wasm_click_type, from_wasm_entity_interaction_action, from_wasm_entity_type,
from_wasm_game_mode, from_wasm_hand, from_wasm_position, to_wasm_block_position,
to_wasm_click_type, to_wasm_entity_interaction_action, to_wasm_entity_type,
to_wasm_game_mode, to_wasm_hand, to_wasm_position,
},
gui::{from_wit_screen, to_wit_screen},
pumpkin::plugin::event::{
AsyncPlayerChatEventData, AsyncPlayerPreLoginEventData,
BedrockFormResponseEventData, CustomClickActionEventData, Event,
@@ -159,7 +160,7 @@ impl ToFromWasmEvent for InventoryCloseEvent {
Event::InventoryCloseEvent(InventoryCloseEventData {
player,
window_type: self.window_type.map(|wt| format!("{wt:?}")),
window_type: self.window_type.map(to_wit_screen),
})
}
@@ -167,7 +168,7 @@ impl ToFromWasmEvent for InventoryCloseEvent {
match event {
Event::InventoryCloseEvent(data) => Self {
player: consume_player(state, &data.player),
window_type: None, // We don't change window_type from WASM
window_type: data.window_type.map(from_wit_screen),
},
_ => panic!("unexpected event type"),
}
@@ -182,7 +183,7 @@ impl ToFromWasmEvent for InventoryClickEvent {
Event::InventoryClickEvent(InventoryClickEventData {
player,
window_type: self.window_type.map(|wt| format!("{wt:?}")),
window_type: self.window_type.map(to_wit_screen),
click_type: to_wasm_click_type(self.click_type),
slot: self.slot,
raw_slot: self.raw_slot,
@@ -205,7 +206,7 @@ impl ToFromWasmEvent for InventoryClickEvent {
match event {
Event::InventoryClickEvent(data) => Self {
player: consume_player(state, &data.player),
window_type: None, // We don't change window_type from WASM
window_type: data.window_type.map(from_wit_screen),
click_type: from_wasm_click_type(data.click_type),
slot: data.slot,
raw_slot: data.raw_slot,
@@ -1153,6 +1154,7 @@ impl ToFromWasmEvent for AsyncPlayerChatEvent {
}
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::AsyncPlayerChatEvent(data) = event {
self.cancelled = data.cancelled;
self.message = data.message;
@@ -1188,6 +1190,7 @@ impl ToFromWasmEvent for AsyncPlayerPreLoginEvent {
}
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::AsyncPlayerPreLoginEvent(data) = event {
self.cancelled = data.cancelled;
self.kick_message = consume_text_component(state, &data.kick_message);
@@ -1225,7 +1228,8 @@ impl ToFromWasmEvent for PlayerAdvancementDoneEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerAdvancementDoneEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1260,7 +1264,8 @@ impl ToFromWasmEvent for PlayerAnimationEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerAnimationEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1295,7 +1300,8 @@ impl ToFromWasmEvent for PlayerArmorStandManipulateEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerArmorStandManipulateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1327,7 +1333,8 @@ impl ToFromWasmEvent for PlayerBucketEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerBucketEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1365,7 +1372,8 @@ impl ToFromWasmEvent for PlayerChangedWorldEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerChangedWorldEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1396,7 +1404,8 @@ impl ToFromWasmEvent for PlayerChannelEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerChannelEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1426,7 +1435,8 @@ impl ToFromWasmEvent for PlayerCommandPreprocessEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerCommandPreprocessEvent(data) = event {
self.cancelled = data.cancelled;
self.command = data.command;
@@ -1460,7 +1470,8 @@ impl ToFromWasmEvent for PlayerEditBookEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerEditBookEvent(data) = event {
self.cancelled = data.cancelled;
self.pages = data.pages;
@@ -1495,7 +1506,8 @@ impl ToFromWasmEvent for PlayerElytraBoostEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerElytraBoostEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1525,7 +1537,8 @@ impl ToFromWasmEvent for PlayerExpCooldownChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerExpCooldownChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.new_cooldown = data.new_cooldown;
@@ -1566,7 +1579,8 @@ impl ToFromWasmEvent for PlayerHarvestBlockEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerHarvestBlockEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1594,7 +1608,8 @@ impl ToFromWasmEvent for PlayerHideEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerHideEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1648,7 +1663,8 @@ impl ToFromWasmEvent for PlayerItemMendEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerItemMendEvent(data) = event {
self.cancelled = data.cancelled;
self.repair_amount = data.repair_amount;
@@ -1682,7 +1698,8 @@ impl ToFromWasmEvent for PlayerKickEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerKickEvent(data) = event {
self.cancelled = data.cancelled;
self.reason = data.reason;
@@ -1714,7 +1731,8 @@ impl ToFromWasmEvent for PlayerLeashEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerLeashEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1769,7 +1787,8 @@ impl ToFromWasmEvent for PlayerLocaleChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerLocaleChangeEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1804,6 +1823,7 @@ impl ToFromWasmEvent for PlayerNameEntityEvent {
}
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerNameEntityEvent(data) = event {
self.cancelled = data.cancelled;
self.name = consume_text_component(state, &data.name);
@@ -1836,7 +1856,8 @@ impl ToFromWasmEvent for PlayerOpenSignEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerOpenSignEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1868,7 +1889,8 @@ impl ToFromWasmEvent for PlayerPortalEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerPortalEvent(data) = event {
self.cancelled = data.cancelled;
self.to_pos = data.to_pos.map(from_wasm_block_position);
@@ -1903,6 +1925,7 @@ impl ToFromWasmEvent for PlayerPreLoginEvent {
}
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerPreLoginEvent(data) = event {
self.cancelled = data.cancelled;
self.kick_message = consume_text_component(state, &data.kick_message);
@@ -1940,7 +1963,8 @@ impl ToFromWasmEvent for PlayerRiptideEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerRiptideEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -1971,7 +1995,8 @@ impl ToFromWasmEvent for PlayerShearEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerShearEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2002,7 +2027,8 @@ impl ToFromWasmEvent for PlayerShowEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerShowEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2033,7 +2059,8 @@ impl ToFromWasmEvent for PlayerSpawnChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerSpawnChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.new_spawn = data.new_spawn.map(from_wasm_block_position);
@@ -2066,7 +2093,8 @@ impl ToFromWasmEvent for PlayerStatisticIncrementEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerStatisticIncrementEvent(data) = event {
self.cancelled = data.cancelled;
self.amount = data.amount;
@@ -2097,7 +2125,8 @@ impl ToFromWasmEvent for PlayerSwapHandItemsEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerSwapHandsEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2130,7 +2159,8 @@ impl ToFromWasmEvent for PlayerTakeLecternBookEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerTakeLecternBookEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2158,7 +2188,8 @@ impl ToFromWasmEvent for PlayerUnleashEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerUnleashEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2188,7 +2219,8 @@ impl ToFromWasmEvent for PlayerVelocityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerVelocityEvent(data) = event {
self.cancelled = data.cancelled;
self.velocity = from_wasm_position(data.velocity);
@@ -2219,7 +2251,8 @@ impl ToFromWasmEvent for PlayerInputEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerInputEvent(data) = event {
self.cancelled = data.cancelled;
self.input = data.input;
@@ -2254,7 +2287,8 @@ impl ToFromWasmEvent for PlayerInteractAtEntityEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerInteractAtEntityEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2288,7 +2322,8 @@ impl ToFromWasmEvent for PlayerLinksSendEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerLinksSendEvent(data) = event {
self.cancelled = data.cancelled;
self.links = data.links;
@@ -2319,7 +2354,8 @@ impl ToFromWasmEvent for PlayerPickupArrowEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerPickupArrowEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -2350,7 +2386,8 @@ impl ToFromWasmEvent for PlayerRecipeBookClickEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerRecipeBookClickEvent(data) = event {
self.cancelled = data.cancelled;
self.recipe_id = data.recipe_id;
@@ -2385,7 +2422,8 @@ impl ToFromWasmEvent for PlayerRecipeBookSettingsChangeEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerRecipeBookSettingsChangeEvent(data) = event {
self.cancelled = data.cancelled;
self.book_type = data.book_type;
@@ -2420,7 +2458,8 @@ impl ToFromWasmEvent for PlayerRecipeDiscoverEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerRecipeDiscoverEvent(data) = event {
self.cancelled = data.cancelled;
self.recipe_id = data.recipe_id;
@@ -2451,7 +2490,8 @@ impl ToFromWasmEvent for PlayerRegisterChannelEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerRegisterChannelEvent(data) = event {
self.cancelled = data.cancelled;
self.channel = data.channel;
@@ -2483,7 +2523,8 @@ impl ToFromWasmEvent for PlayerResourcePackStatusEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerResourcePackStatusEvent(data) = event {
self.cancelled = data.cancelled;
self.status = data.status;
@@ -2515,7 +2556,8 @@ impl ToFromWasmEvent for PlayerSpawnLocationEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerSpawnLocationEvent(data) = event {
self.cancelled = data.cancelled;
self.spawn_pos = from_wasm_position(data.spawn_pos);
@@ -2546,7 +2588,8 @@ impl ToFromWasmEvent for PlayerUnregisterChannelEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PlayerUnregisterChannelEvent(data) = event {
self.cancelled = data.cancelled;
self.channel = data.channel;

View File

@@ -2,7 +2,9 @@ use crate::plugin::{
loader::wasm::wasm_host::{
state::PluginHostState,
wit::v0_1::{
events::{ToFromWasmEvent, from_wasm_block_position, to_wasm_block_position},
events::{
ToFromWasmEvent, cleanup_event, from_wasm_block_position, to_wasm_block_position,
},
pumpkin::plugin::event::{
Event, RaidFinishEventData, RaidSpawnWaveEventData, RaidStopEventData,
RaidTriggerEventData,
@@ -23,7 +25,8 @@ impl ToFromWasmEvent for RaidFinishEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::RaidFinishEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -49,7 +52,8 @@ impl ToFromWasmEvent for RaidSpawnWaveEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::RaidSpawnWaveEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -75,7 +79,8 @@ impl ToFromWasmEvent for RaidStopEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::RaidStopEvent(data) = event {
self.cancelled = data.cancelled;
self.reason = data.reason;
@@ -101,7 +106,8 @@ impl ToFromWasmEvent for RaidTriggerEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::RaidTriggerEvent(data) = event {
self.cancelled = data.cancelled;
}

View File

@@ -3,7 +3,7 @@ use crate::plugin::{
loader::wasm::wasm_host::{
state::PluginHostState,
wit::v0_1::{
events::{ToFromWasmEvent, consume_text_component},
events::{ToFromWasmEvent, cleanup_event, consume_text_component},
generated_packets,
pumpkin::plugin::event::{
ClientboundPacket, Event, MapInitializeEventData, PacketReceivedEventData,
@@ -60,7 +60,8 @@ impl ToFromWasmEvent for PacketReceivedEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PacketReceivedEvent(data) = event {
self.packet_id = data.packet_id;
self.payload = data.raw_payload.into();
@@ -107,7 +108,8 @@ impl ToFromWasmEvent for PacketSentEvent {
})
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PacketSentEvent(data) = event {
self.payload = data.raw_payload.into();
self.cancelled = data.cancelled;
@@ -207,6 +209,7 @@ impl ToFromWasmEvent for ServerListPingEvent {
}
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
match event {
Event::ServerListPingEvent(data) => {
self.motd = consume_text_component(state, &data.motd);

View File

@@ -2,7 +2,9 @@ use crate::plugin::{
loader::wasm::wasm_host::{
state::PluginHostState,
wit::v0_1::{
events::{ToFromWasmEvent, from_wasm_block_position, to_wasm_block_position},
events::{
ToFromWasmEvent, cleanup_event, from_wasm_block_position, to_wasm_block_position,
},
pumpkin::plugin::event::{
Event, VehicleBlockCollisionEventData, VehicleCollisionEventData,
VehicleCreateEventData, VehicleDamageEventData, VehicleDestroyEventData,
@@ -42,7 +44,8 @@ impl ToFromWasmEvent for VehicleBlockCollisionEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleBlockCollisionEvent(data) = event {
self.block_pos = from_wasm_block_position(data.block_pos);
self.cancelled = data.cancelled;
@@ -68,7 +71,8 @@ impl ToFromWasmEvent for VehicleCollisionEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleCollisionEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -93,7 +97,8 @@ impl ToFromWasmEvent for VehicleCreateEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleCreateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -122,7 +127,8 @@ impl ToFromWasmEvent for VehicleDamageEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleDamageEvent(data) = event {
self.damage = data.damage;
self.attacker_id = data.attacker_id;
@@ -151,7 +157,8 @@ impl ToFromWasmEvent for VehicleDestroyEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleDestroyEvent(data) = event {
self.attacker_id = data.attacker_id;
self.cancelled = data.cancelled;
@@ -179,7 +186,8 @@ impl ToFromWasmEvent for VehicleEnterEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleEnterEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -206,7 +214,8 @@ impl ToFromWasmEvent for VehicleEntityCollisionEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleEntityCollisionEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -233,7 +242,8 @@ impl ToFromWasmEvent for VehicleExitEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleExitEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -266,7 +276,8 @@ impl ToFromWasmEvent for VehicleMoveEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleMoveEvent(data) = event {
self.from = Vector3::new(
data.from_position.0,
@@ -297,7 +308,8 @@ impl ToFromWasmEvent for VehicleUpdateEvent {
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::VehicleUpdateEvent(data) = event {
self.cancelled = data.cancelled;
}

View File

@@ -5,7 +5,8 @@ use crate::plugin::{
state::PluginHostState,
wit::v0_1::{
events::{
ToFromWasmEvent, consume_world, from_wasm_block_position, to_wasm_block_position,
ToFromWasmEvent, cleanup_event, consume_world, from_wasm_block_position,
to_wasm_block_position,
},
pumpkin::plugin::event::{
ChunkLoadEventData, ChunkSaveEventData, ChunkSendEventData, Event,
@@ -90,6 +91,7 @@ impl ToFromWasmEvent for ChunkLoad {
blending_data: None,
dirty: std::sync::atomic::AtomicBool::new(false),
inhabited_time: std::sync::atomic::AtomicU64::new(0),
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
};
Self {
world,
@@ -139,6 +141,7 @@ impl ToFromWasmEvent for ChunkSave {
blending_data: None,
dirty: std::sync::atomic::AtomicBool::new(false),
inhabited_time: std::sync::atomic::AtomicU64::new(0),
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
};
Self {
world,
@@ -187,6 +190,7 @@ impl ToFromWasmEvent for ChunkSend {
blending_data: None,
dirty: std::sync::atomic::AtomicBool::new(false),
inhabited_time: std::sync::atomic::AtomicU64::new(0),
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
};
Self {
world,
@@ -315,7 +319,8 @@ impl ToFromWasmEvent
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::AsyncStructureGenerateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -346,7 +351,8 @@ impl ToFromWasmEvent
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::AsyncStructureSpawnEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -372,7 +378,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::chunk_populate::Chun
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ChunkPopulateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -398,7 +405,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::chunk_unload::ChunkU
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::ChunkUnloadEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -426,7 +434,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::entities_load::Entit
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntitiesLoadEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -454,7 +463,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::entities_unload::Ent
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::EntitiesUnloadEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -483,7 +493,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::generic_game::Generi
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::GenericGameEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -508,7 +519,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::loot_generate::LootG
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::LootGenerateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -541,7 +553,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::portal_create::Porta
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::PortalCreateEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -591,7 +604,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::structure_grow::Stru
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::StructureGrowEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -616,7 +630,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::time_skip::TimeSkipE
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::TimeSkipEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -662,7 +677,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::world_save::WorldSav
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::WorldSaveEvent(data) = event {
self.cancelled = data.cancelled;
}
@@ -692,7 +708,8 @@ impl ToFromWasmEvent for crate::plugin::api::events::world::lightning_strike::Li
}
}
fn apply_wasm_event(&mut self, event: Event, _state: &mut PluginHostState) {
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
cleanup_event(&event, state);
if let Event::LightningStrikeEvent(data) = event {
self.cancelled = data.cancelled;
}

View File

@@ -6,10 +6,74 @@ use crate::plugin::api::gui::{PluginGui, PluginInventory};
use crate::plugin::loader::wasm::wasm_host::{
state::{GuiResource, PluginHostState},
wit::v0_1::pumpkin::plugin::{
gui::{self, Gui, GuiType},
gui::{self, Gui},
item_stack::ItemStack as WitHostItemStack,
screens::Screen as WitScreen,
},
};
use pumpkin_data::screen::WindowType;
#[must_use]
pub const fn to_wit_screen(window_type: WindowType) -> WitScreen {
match window_type {
WindowType::Generic9x1 => WitScreen::Generic9x1,
WindowType::Generic9x2 => WitScreen::Generic9x2,
WindowType::Generic9x3 => WitScreen::Generic9x3,
WindowType::Generic9x4 => WitScreen::Generic9x4,
WindowType::Generic9x5 => WitScreen::Generic9x5,
WindowType::Generic9x6 => WitScreen::Generic9x6,
WindowType::Generic3x3 => WitScreen::Generic3x3,
WindowType::Crafter3x3 => WitScreen::Crafter3x3,
WindowType::Anvil => WitScreen::Anvil,
WindowType::Beacon => WitScreen::Beacon,
WindowType::BlastFurnace => WitScreen::BlastFurnace,
WindowType::BrewingStand => WitScreen::BrewingStand,
WindowType::Crafting => WitScreen::Crafting,
WindowType::Enchantment => WitScreen::Enchantment,
WindowType::Furnace => WitScreen::Furnace,
WindowType::Grindstone => WitScreen::Grindstone,
WindowType::Hopper => WitScreen::Hopper,
WindowType::Lectern => WitScreen::Lectern,
WindowType::Loom => WitScreen::Loom,
WindowType::Merchant => WitScreen::Merchant,
WindowType::ShulkerBox => WitScreen::ShulkerBox,
WindowType::Smithing => WitScreen::Smithing,
WindowType::Smoker => WitScreen::Smoker,
WindowType::CartographyTable => WitScreen::CartographyTable,
WindowType::Stonecutter => WitScreen::Stonecutter,
}
}
#[must_use]
pub const fn from_wit_screen(screen: WitScreen) -> WindowType {
match screen {
WitScreen::Generic9x1 => WindowType::Generic9x1,
WitScreen::Generic9x2 => WindowType::Generic9x2,
WitScreen::Generic9x3 => WindowType::Generic9x3,
WitScreen::Generic9x4 => WindowType::Generic9x4,
WitScreen::Generic9x5 => WindowType::Generic9x5,
WitScreen::Generic9x6 => WindowType::Generic9x6,
WitScreen::Generic3x3 => WindowType::Generic3x3,
WitScreen::Crafter3x3 => WindowType::Crafter3x3,
WitScreen::Anvil => WindowType::Anvil,
WitScreen::Beacon => WindowType::Beacon,
WitScreen::BlastFurnace => WindowType::BlastFurnace,
WitScreen::BrewingStand => WindowType::BrewingStand,
WitScreen::Crafting => WindowType::Crafting,
WitScreen::Enchantment => WindowType::Enchantment,
WitScreen::Furnace => WindowType::Furnace,
WitScreen::Grindstone => WindowType::Grindstone,
WitScreen::Hopper => WindowType::Hopper,
WitScreen::Lectern => WindowType::Lectern,
WitScreen::Loom => WindowType::Loom,
WitScreen::Merchant => WindowType::Merchant,
WitScreen::ShulkerBox => WindowType::ShulkerBox,
WitScreen::Smithing => WindowType::Smithing,
WitScreen::Smoker => WindowType::Smoker,
WitScreen::CartographyTable => WindowType::CartographyTable,
WitScreen::Stonecutter => WindowType::Stonecutter,
}
}
impl PluginHostState {
fn get_gui_res(&self, res: &Resource<Gui>) -> wasmtime::Result<&GuiResource> {
@@ -24,39 +88,13 @@ impl gui::Host for PluginHostState {}
impl gui::HostGui for PluginHostState {
async fn new(
&mut self,
gui_type: GuiType,
screen: WitScreen,
title: Resource<
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::text::TextComponent,
>,
) -> wasmtime::Result<Resource<Gui>> {
let title = self.get_text_provider(&title)?;
let window_type = match gui_type {
GuiType::Generic9x1 => pumpkin_data::screen::WindowType::Generic9x1,
GuiType::Generic9x2 => pumpkin_data::screen::WindowType::Generic9x2,
GuiType::Generic9x3 => pumpkin_data::screen::WindowType::Generic9x3,
GuiType::Generic9x4 => pumpkin_data::screen::WindowType::Generic9x4,
GuiType::Generic9x5 => pumpkin_data::screen::WindowType::Generic9x5,
GuiType::Generic9x6 => pumpkin_data::screen::WindowType::Generic9x6,
GuiType::Generic3x3 => pumpkin_data::screen::WindowType::Generic3x3,
GuiType::Crafter3x3 => pumpkin_data::screen::WindowType::Crafter3x3,
GuiType::Anvil => pumpkin_data::screen::WindowType::Anvil,
GuiType::Beacon => pumpkin_data::screen::WindowType::Beacon,
GuiType::BlastFurnace => pumpkin_data::screen::WindowType::BlastFurnace,
GuiType::BrewingStand => pumpkin_data::screen::WindowType::BrewingStand,
GuiType::Crafting => pumpkin_data::screen::WindowType::Crafting,
GuiType::Enchantment => pumpkin_data::screen::WindowType::Enchantment,
GuiType::Furnace => pumpkin_data::screen::WindowType::Furnace,
GuiType::Grindstone => pumpkin_data::screen::WindowType::Grindstone,
GuiType::Hopper => pumpkin_data::screen::WindowType::Hopper,
GuiType::Lectern => pumpkin_data::screen::WindowType::Lectern,
GuiType::Loom => pumpkin_data::screen::WindowType::Loom,
GuiType::Merchant => pumpkin_data::screen::WindowType::Merchant,
GuiType::ShulkerBox => pumpkin_data::screen::WindowType::ShulkerBox,
GuiType::Smithing => pumpkin_data::screen::WindowType::Smithing,
GuiType::Smoker => pumpkin_data::screen::WindowType::Smoker,
GuiType::CartographyTable => pumpkin_data::screen::WindowType::CartographyTable,
GuiType::Stonecutter => pumpkin_data::screen::WindowType::Stonecutter,
};
let window_type = from_wit_screen(screen);
let size = match window_type {
pumpkin_data::screen::WindowType::Generic9x2 => 18,
@@ -123,35 +161,9 @@ impl gui::HostGui for PluginHostState {
}
}
async fn get_type(&mut self, res: Resource<Gui>) -> wasmtime::Result<GuiType> {
async fn get_type(&mut self, res: Resource<Gui>) -> wasmtime::Result<WitScreen> {
let gui = self.get_gui_res(&res)?.provider.lock().await;
Ok(match gui.window_type {
pumpkin_data::screen::WindowType::Generic9x1 => GuiType::Generic9x1,
pumpkin_data::screen::WindowType::Generic9x2 => GuiType::Generic9x2,
pumpkin_data::screen::WindowType::Generic9x3 => GuiType::Generic9x3,
pumpkin_data::screen::WindowType::Generic9x4 => GuiType::Generic9x4,
pumpkin_data::screen::WindowType::Generic9x5 => GuiType::Generic9x5,
pumpkin_data::screen::WindowType::Generic9x6 => GuiType::Generic9x6,
pumpkin_data::screen::WindowType::Generic3x3 => GuiType::Generic3x3,
pumpkin_data::screen::WindowType::Crafter3x3 => GuiType::Crafter3x3,
pumpkin_data::screen::WindowType::Anvil => GuiType::Anvil,
pumpkin_data::screen::WindowType::Beacon => GuiType::Beacon,
pumpkin_data::screen::WindowType::BlastFurnace => GuiType::BlastFurnace,
pumpkin_data::screen::WindowType::BrewingStand => GuiType::BrewingStand,
pumpkin_data::screen::WindowType::Crafting => GuiType::Crafting,
pumpkin_data::screen::WindowType::Enchantment => GuiType::Enchantment,
pumpkin_data::screen::WindowType::Furnace => GuiType::Furnace,
pumpkin_data::screen::WindowType::Grindstone => GuiType::Grindstone,
pumpkin_data::screen::WindowType::Hopper => GuiType::Hopper,
pumpkin_data::screen::WindowType::Lectern => GuiType::Lectern,
pumpkin_data::screen::WindowType::Loom => GuiType::Loom,
pumpkin_data::screen::WindowType::Merchant => GuiType::Merchant,
pumpkin_data::screen::WindowType::ShulkerBox => GuiType::ShulkerBox,
pumpkin_data::screen::WindowType::Smithing => GuiType::Smithing,
pumpkin_data::screen::WindowType::Smoker => GuiType::Smoker,
pumpkin_data::screen::WindowType::CartographyTable => GuiType::CartographyTable,
pumpkin_data::screen::WindowType::Stonecutter => GuiType::Stonecutter,
})
Ok(to_wit_screen(gui.window_type))
}
async fn get_title(

View File

@@ -2,20 +2,20 @@ use crate::plugin::loader::wasm::wasm_host::state::{ItemStackResource, PluginHos
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::{
CustomEnchantmentValue as WitCustomEnchantmentValue,
DataComponentValue as WitDataComponentValue, EnchantmentValue as WitEnchantmentValue,
Host as ItemStackInterfaceHost, HostItemStack, ItemStack as ItemStackHandle,
NbtEntry as WitNbtEntry, NbtTag as WitNbtTag, NbtTree as WitNbtTree,
};
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 super::common::{WitNbtTree, from_wit_nbt_tree, to_wit_nbt_tree};
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::text_component_from_resource;
use pumpkin_data::Enchantment;
use pumpkin_data::data_component::DataComponent;
use pumpkin_data::data_component_impl::{CustomNameImpl, EnchantmentsImpl};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag;
use pumpkin_protocol::codec::data_component::{deserialize, serialize};
use std::borrow::Cow;
@@ -40,91 +40,6 @@ pub(crate) fn from_wit_enchantment(id: WitEnchantment) -> &'static Enchantment {
Enchantment::from_id(id as u8).expect("valid enchantment ID")
}
fn push_wit_nbt_tag(tag: NbtTag, tags: &mut Vec<WitNbtTag>) -> u32 {
let index = tags.len() as u32;
tags.push(WitNbtTag::Byte(0));
let tag = match tag {
NbtTag::End => WitNbtTag::Compound(Vec::new()),
NbtTag::Byte(value) => WitNbtTag::Byte(value),
NbtTag::Short(value) => WitNbtTag::Short(value),
NbtTag::Int(value) => WitNbtTag::Int(value),
NbtTag::Long(value) => WitNbtTag::Long(value),
NbtTag::Float(value) => WitNbtTag::Float(value),
NbtTag::Double(value) => WitNbtTag::Double(value),
NbtTag::ByteArray(value) => WitNbtTag::ByteArray(value.into_vec()),
NbtTag::String(value) => WitNbtTag::StringTag(value.into()),
NbtTag::List(value) => WitNbtTag::ListTag(
value
.into_iter()
.map(|value| push_wit_nbt_tag(value, tags))
.collect(),
),
NbtTag::Compound(value) => WitNbtTag::Compound(
value
.child_tags
.into_iter()
.map(|(key, value)| WitNbtEntry {
key: key.into(),
value: push_wit_nbt_tag(value, tags),
})
.collect(),
),
NbtTag::IntArray(value) => WitNbtTag::IntArray(value),
NbtTag::LongArray(value) => WitNbtTag::LongArray(value),
};
tags[index as usize] = tag;
index
}
fn to_wit_nbt_tree(tag: NbtTag) -> WitNbtTree {
let mut tags = Vec::new();
let root = push_wit_nbt_tag(tag, &mut tags);
WitNbtTree { root, tags }
}
fn from_wit_nbt_tree(tree: &WitNbtTree) -> Result<NbtTag, String> {
fn read_tag(index: u32, tags: &[WitNbtTag], visiting: &mut Vec<u32>) -> Result<NbtTag, String> {
let Some(tag) = tags.get(index as usize) else {
return Err(format!("NBT tag index {index} is out of bounds"));
};
if visiting.contains(&index) {
return Err(format!("NBT tag tree contains a cycle at index {index}"));
}
visiting.push(index);
let tag = match tag {
WitNbtTag::Byte(value) => NbtTag::Byte(*value),
WitNbtTag::Short(value) => NbtTag::Short(*value),
WitNbtTag::Int(value) => NbtTag::Int(*value),
WitNbtTag::Long(value) => NbtTag::Long(*value),
WitNbtTag::Float(value) => NbtTag::Float(*value),
WitNbtTag::Double(value) => NbtTag::Double(*value),
WitNbtTag::ByteArray(value) => NbtTag::ByteArray(value.clone().into()),
WitNbtTag::StringTag(value) => NbtTag::String(value.clone().into()),
WitNbtTag::ListTag(value) => NbtTag::List(
value
.iter()
.map(|value| read_tag(*value, tags, visiting))
.collect::<Result<Vec<_>, _>>()?,
),
WitNbtTag::Compound(value) => NbtTag::Compound(NbtCompound {
child_tags: value
.iter()
.map(|entry| {
read_tag(entry.value, tags, visiting)
.map(|value| (entry.key.clone().into(), value))
})
.collect::<Result<_, _>>()?,
}),
WitNbtTag::IntArray(value) => NbtTag::IntArray(value.clone()),
WitNbtTag::LongArray(value) => NbtTag::LongArray(value.clone()),
};
visiting.pop();
Ok(tag)
}
read_tag(tree.root, &tree.tags, &mut Vec::new())
}
impl PluginHostState {
pub fn get_item_stack(
&self,
@@ -289,6 +204,182 @@ impl HostItemStack for PluginHostState {
Ok(())
}
async fn get_custom_enchantments(
&mut self,
res: Resource<ItemStackHandle>,
) -> wasmtime::Result<Vec<WitCustomEnchantmentValue>> {
let stack = self.get_item_stack(&res)?;
let stack = stack.lock().await;
let mut result = Vec::new();
if let Some(compound) = stack.custom_data_compound()
&& let Some(pumpkin_encs) = compound
.get("pumpkin:enchantments")
.and_then(NbtTag::extract_compound)
{
for (k, v) in &pumpkin_encs.child_tags {
if let Some(lvl) = v.extract_int() {
result.push(WitCustomEnchantmentValue {
enchantment_id: k.to_string(),
level: (lvl.max(1)) as u32,
});
}
}
}
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() {
if !result.iter().any(|e| e.enchantment_id == enc.name) {
result.push(WitCustomEnchantmentValue {
enchantment_id: enc.name.to_string(),
level: (*level).max(1) as u32,
});
}
}
}
Ok(result)
}
async fn add_custom_enchantment(
&mut self,
res: Resource<ItemStackHandle>,
enchantment_id: String,
level: u32,
) -> wasmtime::Result<()> {
let stack = self.get_item_stack(&res)?;
let mut stack = stack.lock().await;
stack.set_custom_data(
"pumpkin:enchantments",
&enchantment_id,
NbtTag::Int(level as i32),
);
if let Some(vanilla) = super::enchantment::find_vanilla_enchantment(&enchantment_id) {
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 != vanilla.id);
current_encs.push((vanilla, 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_custom_enchantment(
&mut self,
res: Resource<ItemStackHandle>,
enchantment_id: String,
) -> wasmtime::Result<()> {
let stack = self.get_item_stack(&res)?;
let mut stack = stack.lock().await;
stack.remove_custom_data("pumpkin:enchantments", &enchantment_id);
if let Some(vanilla) = super::enchantment::find_vanilla_enchantment(&enchantment_id)
&& 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 != vanilla.id);
enc_impl.enchantment = Cow::from(encs);
}
Ok(())
}
async fn get_custom_enchantment_level(
&mut self,
res: Resource<ItemStackHandle>,
enchantment_id: String,
) -> wasmtime::Result<Option<u32>> {
let stack = self.get_item_stack(&res)?;
let stack = stack.lock().await;
if let Some(NbtTag::Int(level)) =
stack.get_custom_data("pumpkin:enchantments", &enchantment_id)
{
return Ok(Some(level.max(1) as u32));
}
if let Some(vanilla) = super::enchantment::find_vanilla_enchantment(&enchantment_id)
&& let Some((_, Some(data))) = stack
.patch
.iter()
.find(|(id, _)| *id == DataComponent::Enchantments)
&& let Some(enc_impl) = data.as_any().downcast_ref::<EnchantmentsImpl>()
&& let Some((_, level)) = enc_impl
.enchantment
.iter()
.find(|(e, _)| e.id == vanilla.id)
{
return Ok(Some((*level).max(1) as u32));
}
Ok(None)
}
async fn has_custom_enchantment(
&mut self,
res: Resource<ItemStackHandle>,
enchantment_id: String,
) -> wasmtime::Result<bool> {
let stack = self.get_item_stack(&res)?;
let stack = stack.lock().await;
if stack.has_custom_data("pumpkin:enchantments", &enchantment_id) {
return Ok(true);
}
if let Some(vanilla) = super::enchantment::find_vanilla_enchantment(&enchantment_id)
&& let Some((_, Some(data))) = stack
.patch
.iter()
.find(|(id, _)| *id == DataComponent::Enchantments)
&& let Some(enc_impl) = data.as_any().downcast_ref::<EnchantmentsImpl>()
{
return Ok(enc_impl.enchantment.iter().any(|(e, _)| e.id == vanilla.id));
}
Ok(false)
}
async fn get_lore(
&mut self,
res: Resource<ItemStackHandle>,

View File

@@ -8,11 +8,14 @@ use tokio::sync::Mutex;
use wasmtime::component::{HasSelf, InstancePre, Linker, bindgen};
use wasmtime::{Engine, Store};
pub mod advancement;
pub mod block_entity;
pub mod boss_bar;
pub mod commands;
pub mod common;
pub mod context;
pub mod display;
pub mod enchantment;
pub mod entity;
pub mod events;
pub mod forms;
@@ -47,6 +50,11 @@ impl pumpkin::plugin::data_components::Host for PluginHostState {}
impl pumpkin::plugin::enchantments::Host for PluginHostState {}
impl pumpkin::plugin::biomes::Host for PluginHostState {}
impl pumpkin::plugin::attributes::Host for PluginHostState {}
impl pumpkin::plugin::advancement::Host for PluginHostState {}
impl pumpkin::plugin::damage_types::Host for PluginHostState {}
impl pumpkin::plugin::screens::Host for PluginHostState {}
impl pumpkin::plugin::statistics::Host for PluginHostState {}
impl pumpkin::plugin::game_rules::Host for PluginHostState {}
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
@@ -64,6 +72,7 @@ pub async fn init_plugin(
plugin_pre: PluginPre<PluginHostState>,
) -> Result<(WasmPlugin, PluginMetadata), PluginInitError> {
let mut store = Store::new(engine, PluginHostState::new());
store.limiter(|state| &mut state.limits);
let plugin = plugin_pre
.instantiate_async(&mut store)
.await

View File

@@ -17,12 +17,18 @@ use crate::{
GuiResource, PlayerResource, PluginHostState, TextComponentResource, WorldResource,
},
wit::v0_1::{
entity::from_wit_damage_type,
events::{
from_wasm_game_mode, from_wasm_position, to_wasm_game_mode, to_wasm_position,
},
pumpkin::{
self,
plugin::damage_types::DamageType as WitDamageType,
plugin::player::{Player, PlayerSkin, SkinParts},
plugin::statistics::{
CustomStatistic as WitCustomStatistic,
StatisticCategory as WitStatisticCategory,
},
plugin::uuid::Uuid,
plugin::world::World,
},
@@ -133,6 +139,56 @@ const fn to_wasm_chat_mode(
}
}
#[must_use]
pub const fn to_wit_statistic_category(
category: pumpkin_data::statistic::StatisticCategory,
) -> WitStatisticCategory {
match category {
pumpkin_data::statistic::StatisticCategory::Mined => WitStatisticCategory::Mined,
pumpkin_data::statistic::StatisticCategory::Crafted => WitStatisticCategory::Crafted,
pumpkin_data::statistic::StatisticCategory::Used => WitStatisticCategory::Used,
pumpkin_data::statistic::StatisticCategory::Broken => WitStatisticCategory::Broken,
pumpkin_data::statistic::StatisticCategory::PickedUp => WitStatisticCategory::PickedUp,
pumpkin_data::statistic::StatisticCategory::Dropped => WitStatisticCategory::Dropped,
pumpkin_data::statistic::StatisticCategory::Killed => WitStatisticCategory::Killed,
pumpkin_data::statistic::StatisticCategory::KilledBy => WitStatisticCategory::KilledBy,
pumpkin_data::statistic::StatisticCategory::Custom => WitStatisticCategory::Custom,
}
}
#[must_use]
pub const fn from_wit_statistic_category(
wit: WitStatisticCategory,
) -> pumpkin_data::statistic::StatisticCategory {
match wit {
WitStatisticCategory::Mined => pumpkin_data::statistic::StatisticCategory::Mined,
WitStatisticCategory::Crafted => pumpkin_data::statistic::StatisticCategory::Crafted,
WitStatisticCategory::Used => pumpkin_data::statistic::StatisticCategory::Used,
WitStatisticCategory::Broken => pumpkin_data::statistic::StatisticCategory::Broken,
WitStatisticCategory::PickedUp => pumpkin_data::statistic::StatisticCategory::PickedUp,
WitStatisticCategory::Dropped => pumpkin_data::statistic::StatisticCategory::Dropped,
WitStatisticCategory::Killed => pumpkin_data::statistic::StatisticCategory::Killed,
WitStatisticCategory::KilledBy => pumpkin_data::statistic::StatisticCategory::KilledBy,
WitStatisticCategory::Custom => pumpkin_data::statistic::StatisticCategory::Custom,
}
}
#[must_use]
pub const fn to_wit_custom_statistic(
stat: pumpkin_data::statistic::CustomStatistic,
) -> WitCustomStatistic {
// SAFETY: WitCustomStatistic is generated in the same numerical order as CustomStatistic
unsafe { std::mem::transmute(stat as u8) }
}
#[must_use]
pub fn from_wit_custom_statistic(
wit: WitCustomStatistic,
) -> pumpkin_data::statistic::CustomStatistic {
pumpkin_data::statistic::CustomStatistic::from_i32(wit as i32)
.unwrap_or(pumpkin_data::statistic::CustomStatistic::PlayTime)
}
const fn to_wasm_bedrock_device_os(os: i32) -> pumpkin::plugin::player::BedrockDeviceOs {
match os {
1 => pumpkin::plugin::player::BedrockDeviceOs::Android,
@@ -1024,9 +1080,11 @@ impl pumpkin::plugin::player::Host for PluginHostState {
}
}
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::events::from_wasm_hand;
use pumpkin_inventory::generic_container_screen_handler::GenericContainerScreenHandler;
use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory;
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
use pumpkin_protocol::java::client::play::CSetContainerSlot;
use pumpkin_world::inventory::Inventory;
use pumpkin_world::inventory::{Clearable, Inventory};
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::ItemStack as WitHostItemStack;
@@ -1102,6 +1160,88 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
}
}
async fn get_ender_chest_item(
&mut self,
player: Resource<Player>,
slot: u8,
) -> wasmtime::Result<Option<Resource<WitHostItemStack>>> {
let player = player_from_resource(self, &player)?;
let ec = player.ender_chest_inventory();
let stack = ec.get_stack(slot as usize).await;
if stack.is_empty() {
Ok(None)
} else {
Ok(Some(self.add_item_stack(Arc::new(
tokio::sync::Mutex::new(stack),
))?))
}
}
async fn set_ender_chest_item(
&mut self,
player: Resource<Player>,
slot: u8,
stack: Option<Resource<WitHostItemStack>>,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
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 ec = player.ender_chest_inventory();
ec.set_stack(slot as usize, stack.clone()).await;
// If the player currently has their ender chest screen open, sync slot
let screen_handler_arc = player.current_screen_handler.lock().await.clone();
let handler = screen_handler_arc.lock().await;
if let Some(generic) = handler
.as_any()
.downcast_ref::<GenericContainerScreenHandler>()
&& generic.inventory.as_any().is::<EnderChestInventory>()
{
let sync_id = handler.sync_id();
let stack_serializer = ItemStackSerializer::from(stack);
let packet = CSetContainerSlot::new(sync_id as i8, 0, slot as i16, &stack_serializer);
player.client.enqueue_packet(&packet).await;
}
Ok(())
}
async fn clear_ender_chest(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
let ec = player.ender_chest_inventory();
ec.clear().await;
// If the player currently has their ender chest screen open, sync all slots
let screen_handler_arc = player.current_screen_handler.lock().await.clone();
let handler = screen_handler_arc.lock().await;
if let Some(generic) = handler
.as_any()
.downcast_ref::<GenericContainerScreenHandler>()
&& generic.inventory.as_any().is::<EnderChestInventory>()
{
let sync_id = handler.sync_id();
let empty_serializer =
ItemStackSerializer::from(pumpkin_data::item_stack::ItemStack::EMPTY.clone());
for slot in 0..27 {
let packet =
CSetContainerSlot::new(sync_id as i8, 0, slot as i16, &empty_serializer);
player.client.enqueue_packet(&packet).await;
}
}
Ok(())
}
async fn open_ender_chest(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player.open_ender_chest().await;
Ok(())
}
async fn get_item_in_hand(
&mut self,
player: Resource<Player>,
@@ -1434,9 +1574,16 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
Ok(())
}
async fn damage(&mut self, player: Resource<Player>, amount: f32) -> wasmtime::Result<()> {
async fn damage(
&mut self,
player: Resource<Player>,
amount: f32,
damage_type: WitDamageType,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player.damage_generic(amount).await;
player
.damage(&*player, amount, from_wit_damage_type(damage_type))
.await;
Ok(())
}
@@ -1446,6 +1593,95 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
Ok(())
}
async fn get_statistic(
&mut self,
player: Resource<Player>,
category: WitStatisticCategory,
stat_id: i32,
) -> wasmtime::Result<i32> {
let player = player_from_resource(self, &player)?;
Ok(player
.get_stat(from_wit_statistic_category(category), stat_id)
.await)
}
async fn set_statistic(
&mut self,
player: Resource<Player>,
category: WitStatisticCategory,
stat_id: i32,
value: i32,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player
.set_stat(from_wit_statistic_category(category), stat_id, value)
.await;
Ok(())
}
async fn increment_statistic(
&mut self,
player: Resource<Player>,
category: WitStatisticCategory,
stat_id: i32,
amount: i32,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player
.increment_stat(from_wit_statistic_category(category), stat_id, amount)
.await;
Ok(())
}
async fn get_custom_statistic(
&mut self,
player: Resource<Player>,
stat: WitCustomStatistic,
) -> wasmtime::Result<i32> {
let player = player_from_resource(self, &player)?;
Ok(player
.get_custom_stat(from_wit_custom_statistic(stat))
.await)
}
async fn set_custom_statistic(
&mut self,
player: Resource<Player>,
stat: WitCustomStatistic,
value: i32,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player
.set_custom_stat(from_wit_custom_statistic(stat), value)
.await;
Ok(())
}
async fn increment_custom_statistic(
&mut self,
player: Resource<Player>,
stat: WitCustomStatistic,
amount: i32,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player
.increment_custom_stat(from_wit_custom_statistic(stat), amount)
.await;
Ok(())
}
async fn send_stats(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
player.send_stats().await;
Ok(())
}
async fn get_team(&mut self, player: Resource<Player>) -> wasmtime::Result<Option<String>> {
let player = player_from_resource(self, &player)?;
let team = player.get_team().await;
Ok(team.map(|t| t.name))
}
async fn start_cooldown(
&mut self,
player: Resource<Player>,
@@ -2313,6 +2549,214 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
Ok(())
}
async fn get_advancement_progress(
&mut self,
player: Resource<Player>,
advancement_id: String,
) -> wasmtime::Result<Option<pumpkin::plugin::advancement::AdvancementProgress>> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(None);
};
let guard = player.advancements.lock().await;
let progress = guard.progress.map.get(advancement).map_or_else(
|| pumpkin::plugin::advancement::AdvancementProgress {
advancement_id: advancement.id.to_string(),
done: false,
awarded_criteria: Vec::new(),
remaining_criteria: advancement
.criteria
.iter()
.map(ToString::to_string)
.collect(),
},
|progress| pumpkin::plugin::advancement::AdvancementProgress {
advancement_id: advancement.id.to_string(),
done: progress.is_done(),
awarded_criteria: progress
.get_completed_criteria()
.map(|s| s.to_string())
.collect(),
remaining_criteria: progress
.get_remaining_criteria()
.map(|s| s.to_string())
.collect(),
},
);
Ok(Some(progress))
}
async fn award_advancement_criterion(
&mut self,
player: Resource<Player>,
advancement_id: String,
criterion: String,
) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(false);
};
let mut guard = player.advancements.lock().await;
let awarded = guard.award(advancement, &criterion);
if awarded {
guard.flush_dirty(&player, true);
}
Ok(awarded)
}
async fn revoke_advancement_criterion(
&mut self,
player: Resource<Player>,
advancement_id: String,
criterion: String,
) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(false);
};
let mut guard = player.advancements.lock().await;
let revoked = guard.revoke(advancement, &criterion);
if revoked {
guard.flush_dirty(&player, true);
}
Ok(revoked)
}
async fn award_advancement(
&mut self,
player: Resource<Player>,
advancement_id: String,
) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(false);
};
let mut guard = player.advancements.lock().await;
let progress = guard.progress.get_mut_or_start_progress(advancement);
if progress.is_done() {
return Ok(false);
}
let remaining: Vec<Arc<str>> = progress.get_remaining_criteria().collect();
let mut any_awarded = false;
for criterion in remaining {
if guard.award(advancement, &criterion) {
any_awarded = true;
}
}
if any_awarded {
guard.flush_dirty(&player, true);
}
Ok(any_awarded)
}
async fn revoke_advancement(
&mut self,
player: Resource<Player>,
advancement_id: String,
) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(false);
};
let mut guard = player.advancements.lock().await;
let progress = guard.progress.get_mut_or_start_progress(advancement);
if !progress.has_progress() {
return Ok(false);
}
let completed: Vec<Arc<str>> = progress.get_completed_criteria().collect();
let mut any_revoked = false;
for criterion in completed {
if guard.revoke(advancement, &criterion) {
any_revoked = true;
}
}
if any_revoked {
guard.flush_dirty(&player, true);
}
Ok(any_revoked)
}
async fn has_advancement(
&mut self,
player: Resource<Player>,
advancement_id: String,
) -> wasmtime::Result<bool> {
let player = player_from_resource(self, &player)?;
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(
&advancement_id,
)
else {
return Ok(false);
};
let guard = player.advancements.lock().await;
let done = guard
.progress
.map
.get(advancement)
.is_some_and(crate::entity::player::advancement::AdvancementProgress::is_done);
Ok(done)
}
async fn get_completed_advancements(
&mut self,
player: Resource<Player>,
) -> wasmtime::Result<Vec<String>> {
let player = player_from_resource(self, &player)?;
let guard = player.advancements.lock().await;
let list = guard
.progress
.map
.iter()
.filter(|(_, p)| p.is_done())
.map(|(adv, _)| adv.id.to_string())
.collect();
Ok(list)
}
async fn get_selected_advancement_tab(
&mut self,
player: Resource<Player>,
) -> wasmtime::Result<Option<String>> {
let player = player_from_resource(self, &player)?;
let guard = player.advancements.lock().await;
Ok(guard.last_selected_tab.map(|adv| adv.id.to_string()))
}
async fn set_selected_advancement_tab(
&mut self,
player: Resource<Player>,
tab_id: Option<String>,
) -> wasmtime::Result<()> {
let player = player_from_resource(self, &player)?;
let target_adv = tab_id.as_deref().and_then(
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement,
);
let mut guard = player.advancements.lock().await;
guard.set_selected_tab(target_adv).await;
Ok(())
}
async fn as_java(
&mut self,
player: Resource<Player>,

View File

@@ -1,8 +1,9 @@
use crate::plugin::loader::wasm::wasm_host::state::PluginHostState;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::recipe::{
CookingRecipe as WitCookingRecipe, CookingType as WitCookingType, Host as RecipeHost,
HostRecipeManager, Ingredient as WitIngredient, RecipeManager as WitRecipeManager,
ShapedRecipe as WitShapedRecipe, ShapelessRecipe as WitShapelessRecipe,
HostRecipeManager, Ingredient as WitIngredient, RecipeCategory as WitRecipeCategory,
RecipeManager as WitRecipeManager, ShapedRecipe as WitShapedRecipe,
ShapelessRecipe as WitShapelessRecipe,
};
use pumpkin_data::recipes::RecipeCategoryTypes;
use pumpkin_protocol::codec::recipe::{
@@ -17,16 +18,21 @@ impl HostRecipeManager for PluginHostState {
async fn register_shaped(
&mut self,
_res: Resource<WitRecipeManager>,
_id: String,
id: String,
recipe: WitShapedRecipe,
) -> wasmtime::Result<()> {
let result_stack = self.get_item_stack(&recipe.output)?;
let result_stack = result_stack.lock().await;
let category = recipe
.category
.map_or(RecipeCategoryTypes::Misc, to_data_category);
let owned_recipe = OwnedCraftingRecipe::Shaped {
category: RecipeCategoryTypes::Misc, // TODO: Allow specifying category
recipe_id: Some(id),
category,
group: recipe.group,
show_notification: true,
show_notification: recipe.show_notification.unwrap_or(true),
key: recipe
.key
.into_iter()
@@ -53,14 +59,19 @@ impl HostRecipeManager for PluginHostState {
async fn register_shapeless(
&mut self,
_res: Resource<WitRecipeManager>,
_id: String,
id: String,
recipe: WitShapelessRecipe,
) -> wasmtime::Result<()> {
let result_stack = self.get_item_stack(&recipe.output)?;
let result_stack = result_stack.lock().await;
let category = recipe
.category
.map_or(RecipeCategoryTypes::Misc, to_data_category);
let owned_recipe = OwnedCraftingRecipe::Shapeless {
category: RecipeCategoryTypes::Misc,
recipe_id: Some(id),
category,
group: recipe.group,
ingredients: recipe
.ingredients
@@ -94,9 +105,13 @@ impl HostRecipeManager for PluginHostState {
let result_stack = self.get_item_stack(&recipe.output)?;
let result_stack = result_stack.lock().await;
let category = recipe
.category
.map_or(RecipeCategoryTypes::Misc, to_data_category);
let owned_cooking = OwnedCookingRecipe {
recipe_id: id,
category: RecipeCategoryTypes::Misc,
category,
group: recipe.group,
ingredient: to_owned_ingredient(recipe.ingredient),
cooking_time: recipe.cooking_time as i32,
@@ -135,9 +150,21 @@ impl HostRecipeManager for PluginHostState {
}
}
const fn to_data_category(cat: WitRecipeCategory) -> RecipeCategoryTypes {
match cat {
WitRecipeCategory::Building => RecipeCategoryTypes::Building,
WitRecipeCategory::Redstone => RecipeCategoryTypes::Restone,
WitRecipeCategory::Equipment => RecipeCategoryTypes::Equipment,
WitRecipeCategory::Misc => RecipeCategoryTypes::Misc,
WitRecipeCategory::Food => RecipeCategoryTypes::Food,
WitRecipeCategory::Blocks => RecipeCategoryTypes::Blocks,
}
}
fn to_owned_ingredient(ing: WitIngredient) -> OwnedRecipeIngredient {
match ing {
WitIngredient::Item(id) => OwnedRecipeIngredient::Simple(id),
WitIngredient::Tag(tag) => OwnedRecipeIngredient::Tagged(tag),
WitIngredient::OneOf(items) => OwnedRecipeIngredient::OneOf(items),
}
}

View File

@@ -534,6 +534,120 @@ impl scoreboard::HostScoreboard for PluginHostState {
Ok(())
}
async fn get_teams(
&mut self,
res: Resource<scoreboard::Scoreboard>,
) -> wasmtime::Result<Vec<String>> {
let provider = self.get_scoreboard_res(&res)?.provider.clone();
let teams = match provider {
ScoreboardProvider::World(world) => world
.scoreboard
.lock()
.await
.get_teams()
.keys()
.cloned()
.collect(),
ScoreboardProvider::Player(player) => {
let custom_guard = player.custom_scoreboard.lock().await;
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
custom_guard.as_ref()
{
sb.get_teams().keys().cloned().collect()
} else {
Vec::new()
}
}
};
Ok(teams)
}
async fn get_team(
&mut self,
res: Resource<scoreboard::Scoreboard>,
name: String,
) -> wasmtime::Result<Option<TeamSettings>> {
let provider = self.get_scoreboard_res(&res)?.provider.clone();
let team_opt = match provider {
ScoreboardProvider::World(world) => {
world.scoreboard.lock().await.get_team(&name).cloned()
}
ScoreboardProvider::Player(player) => {
let custom_guard = player.custom_scoreboard.lock().await;
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
custom_guard.as_ref()
{
sb.get_team(&name).cloned()
} else {
None
}
}
};
if let Some(team) = team_opt {
Ok(Some(map_team_to_settings(&team, self)?))
} else {
Ok(None)
}
}
async fn get_team_players(
&mut self,
res: Resource<scoreboard::Scoreboard>,
team_name: String,
) -> wasmtime::Result<Vec<String>> {
let provider = self.get_scoreboard_res(&res)?.provider.clone();
let players = match provider {
ScoreboardProvider::World(world) => world
.scoreboard
.lock()
.await
.get_team(&team_name)
.map(|t| t.players.clone())
.unwrap_or_default(),
ScoreboardProvider::Player(player) => {
let custom_guard = player.custom_scoreboard.lock().await;
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
custom_guard.as_ref()
{
sb.get_team(&team_name)
.map(|t| t.players.clone())
.unwrap_or_default()
} else {
Vec::new()
}
}
};
Ok(players)
}
async fn get_player_team(
&mut self,
res: Resource<scoreboard::Scoreboard>,
player_name: String,
) -> wasmtime::Result<Option<String>> {
let provider = self.get_scoreboard_res(&res)?.provider.clone();
let team_name = match provider {
ScoreboardProvider::World(world) => world
.scoreboard
.lock()
.await
.get_entity_team(&player_name)
.map(|t| t.name.clone()),
ScoreboardProvider::Player(player) => {
let custom_guard = player.custom_scoreboard.lock().await;
if let Some(crate::entity::player::CustomScoreboard::Java(sb)) =
custom_guard.as_ref()
{
sb.get_entity_team(&player_name).map(|t| t.name.clone())
} else {
None
}
}
};
Ok(team_name)
}
async fn drop(&mut self, rep: Resource<scoreboard::Scoreboard>) -> wasmtime::Result<()> {
self.resource_table
.delete::<ScoreboardResource>(Resource::new_own(rep.rep()))
@@ -667,6 +781,88 @@ const fn map_named_color(
}
}
fn map_team_to_settings(
team: &Team,
state: &mut PluginHostState,
) -> wasmtime::Result<TeamSettings> {
let display_name = state.add_text_component(team.display_name.clone())?;
let prefix = state.add_text_component(team.player_prefix.clone())?;
let suffix = state.add_text_component(team.player_suffix.clone())?;
let friendly_fire = (team.options & 0x01) != 0;
let see_friendly_invisibles = (team.options & 0x02) != 0;
let nametag_visibility = match team.nametag_visibility {
crate::world::scoreboard::NameTagVisibility::Always => NametagVisibility::Always,
crate::world::scoreboard::NameTagVisibility::Never => NametagVisibility::Never,
crate::world::scoreboard::NameTagVisibility::HideForOtherTeams => {
NametagVisibility::HideForOtherTeams
}
crate::world::scoreboard::NameTagVisibility::HideForOwnTeam => {
NametagVisibility::HideForOwnTeam
}
};
let collision_rule = match team.collision_rule {
crate::world::scoreboard::CollisionRule::Always => CollisionRule::Always,
crate::world::scoreboard::CollisionRule::Never => CollisionRule::Never,
crate::world::scoreboard::CollisionRule::PushOtherTeams => CollisionRule::PushOtherTeams,
crate::world::scoreboard::CollisionRule::PushOwnTeam => CollisionRule::PushOwnTeam,
};
let color = map_named_color_rev(team.color);
Ok(TeamSettings {
display_name,
friendly_fire,
see_friendly_invisibles,
nametag_visibility,
collision_rule,
color,
prefix,
suffix,
})
}
const fn map_named_color_rev(
color: pumpkin_util::text::color::NamedColor,
) -> pumpkin::plugin::common::NamedColor {
match color {
pumpkin_util::text::color::NamedColor::Black => pumpkin::plugin::common::NamedColor::Black,
pumpkin_util::text::color::NamedColor::DarkBlue => {
pumpkin::plugin::common::NamedColor::DarkBlue
}
pumpkin_util::text::color::NamedColor::DarkGreen => {
pumpkin::plugin::common::NamedColor::DarkGreen
}
pumpkin_util::text::color::NamedColor::DarkAqua => {
pumpkin::plugin::common::NamedColor::DarkAqua
}
pumpkin_util::text::color::NamedColor::DarkRed => {
pumpkin::plugin::common::NamedColor::DarkRed
}
pumpkin_util::text::color::NamedColor::DarkPurple => {
pumpkin::plugin::common::NamedColor::DarkPurple
}
pumpkin_util::text::color::NamedColor::Gold => pumpkin::plugin::common::NamedColor::Gold,
pumpkin_util::text::color::NamedColor::Gray => pumpkin::plugin::common::NamedColor::Gray,
pumpkin_util::text::color::NamedColor::DarkGray => {
pumpkin::plugin::common::NamedColor::DarkGray
}
pumpkin_util::text::color::NamedColor::Blue => pumpkin::plugin::common::NamedColor::Blue,
pumpkin_util::text::color::NamedColor::Green => pumpkin::plugin::common::NamedColor::Green,
pumpkin_util::text::color::NamedColor::Aqua => pumpkin::plugin::common::NamedColor::Aqua,
pumpkin_util::text::color::NamedColor::Red => pumpkin::plugin::common::NamedColor::Red,
pumpkin_util::text::color::NamedColor::LightPurple => {
pumpkin::plugin::common::NamedColor::LightPurple
}
pumpkin_util::text::color::NamedColor::Yellow => {
pumpkin::plugin::common::NamedColor::Yellow
}
pumpkin_util::text::color::NamedColor::White => pumpkin::plugin::common::NamedColor::White,
}
}
impl HostBedrockScoreboard for PluginHostState {
async fn add_objective(
&mut self,

View File

@@ -2,6 +2,9 @@ use pumpkin_util::text::TextComponent;
use wasmtime::component::Resource;
use crate::command::CommandSender;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::enchantments::{
CustomEnchantment as WitCustomEnchantment, EnchantmentManager as WitEnchantmentManager,
};
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::recipe::RecipeManager as WitRecipeManager;
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
@@ -196,13 +199,26 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
.worlds
.load()
.iter()
.find(|world| world.dimension.minecraft_name == name)
.find(|world| world.get_world_name() == name || world.dimension.minecraft_name == name)
.map(|world| {
self.add_world(world.clone())
.expect("failed to add world resource")
}))
}
async fn has_world(&mut self, _rep: Resource<Server>, name: String) -> wasmtime::Result<bool> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server
.worlds
.load()
.iter()
.any(|world| world.get_world_name() == name || world.dimension.minecraft_name == name))
}
async fn create_world(
&mut self,
_rep: Resource<Server>,
@@ -225,6 +241,52 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
.map_err(|_| wasmtime::Error::msg("failed to add world resource"))
}
async fn unload_world(
&mut self,
_rep: Resource<Server>,
name: String,
) -> wasmtime::Result<Result<(), String>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server.unload_world(&name).await)
}
async fn save_all(&mut self, _rep: Resource<Server>) -> wasmtime::Result<Result<(), String>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
Ok(server.save_all().await)
}
async fn get_players_in_world(
&mut self,
_rep: Resource<Server>,
world: Resource<pumpkin::plugin::world::World>,
) -> wasmtime::Result<Vec<Resource<pumpkin::plugin::player::Player>>> {
let world_res = self.get_world_res(&world)?;
let players = world_res.provider.players.load();
let mut player_resources = Vec::with_capacity(players.len());
for p in players.iter() {
let res = self.add_player(p.clone())?;
player_resources.push(res);
}
Ok(player_resources)
}
async fn get_player_count_in_world(
&mut self,
_rep: Resource<Server>,
world: Resource<pumpkin::plugin::world::World>,
) -> wasmtime::Result<u32> {
let world_res = self.get_world_res(&world)?;
Ok(world_res.provider.players.load().len() as u32)
}
async fn broadcast(&mut self, _rep: Resource<Server>, message: String) -> wasmtime::Result<()> {
let server = self
.server
@@ -438,6 +500,118 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
self.add_whitelist_manager(server.clone())
}
async fn get_advancement(
&mut self,
_rep: Resource<Server>,
id: String,
) -> wasmtime::Result<Option<pumpkin::plugin::advancement::AdvancementInfo>> {
let Some(advancement) =
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::find_advancement(&id)
else {
return Ok(None);
};
crate::plugin::loader::wasm::wasm_host::wit::v0_1::advancement::to_wasm_advancement_info(
self,
advancement,
)
.map(Some)
}
async fn get_all_advancement_ids(
&mut self,
_rep: Resource<Server>,
) -> wasmtime::Result<Vec<String>> {
let ids = pumpkin_data::Advancement::get_identifier_list()
.iter()
.map(ToString::to_string)
.collect();
Ok(ids)
}
async fn get_enchantment_manager(
&mut self,
_rep: Resource<Server>,
) -> wasmtime::Result<Resource<WitEnchantmentManager>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
self.add_enchantment_manager(server.enchantment_manager.clone())
}
async fn get_enchantment(
&mut self,
_rep: Resource<Server>,
id: String,
) -> wasmtime::Result<Option<WitCustomEnchantment>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
if let Some(entry) = server.enchantment_manager.get(&id).await {
let description = self.add_text_component(entry.description)?;
return Ok(Some(WitCustomEnchantment {
id: entry.id,
description,
max_level: entry.max_level,
anvil_cost: entry.anvil_cost,
supported_items: entry.supported_items,
weight: entry.weight,
slots: entry
.slots
.iter()
.map(super::enchantment::to_wit_slot)
.collect(),
exclusive_set: entry.exclusive_set,
}));
}
if let Some(vanilla) = super::enchantment::find_vanilla_enchantment(&id) {
let description =
self.add_text_component(TextComponent::translate(vanilla.description, []))?;
return Ok(Some(WitCustomEnchantment {
id: vanilla.name.to_string(),
description,
max_level: vanilla.max_level.max(1) as u32,
anvil_cost: vanilla.anvil_cost,
supported_items: vanilla
.supported_items
.0
.first()
.copied()
.unwrap_or("")
.to_string(),
weight: vanilla.weight.max(1) as u32,
slots: vanilla
.slots
.iter()
.map(super::enchantment::to_wit_slot)
.collect(),
exclusive_set: vanilla.exclusive_set.map_or_else(Vec::new, |tag| {
tag.0.iter().map(|s| (*s).to_string()).collect()
}),
}));
}
Ok(None)
}
async fn get_all_enchantment_ids(
&mut self,
_rep: Resource<Server>,
) -> wasmtime::Result<Vec<String>> {
let server = self
.server
.as_ref()
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
let mut ids = server.enchantment_manager.get_all_ids().await;
for enc in pumpkin_data::enchantment::Enchantment::ALL {
ids.push(enc.name.to_string());
}
Ok(ids)
}
async fn drop(&mut self, rep: Resource<Server>) -> wasmtime::Result<()> {
self.resource_table
.delete::<ServerResource>(Resource::new_own(rep.rep()))

View File

@@ -60,6 +60,9 @@ use crate::block::entities::trapped_chest::TrappedChestBlockEntity as InternalTr
use crate::block::entities::trial_spawner::TrialSpawnerBlockEntity as InternalTrialSpawnerBlockEntity;
use crate::block::entities::vault::VaultBlockEntity as InternalVaultBlockEntity;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position as WitPosition;
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::game_rules::{
GameRule as WitGameRule, GameRuleValue as WitGameRuleValue,
};
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags,
BlockPos as WitBlockPos, BlockState as WitBlockState, BlockStateInfo as WitBlockStateInfo,
@@ -74,6 +77,28 @@ use crate::plugin::loader::wasm::wasm_host::{
wit::v0_1::pumpkin::{self, plugin::world::World},
};
use crate::world::explosion::Explosion;
use pumpkin_data::game_rules::{GameRule, GameRuleValue};
pub(crate) fn from_wit_game_rule(rule: WitGameRule) -> GameRule {
// SAFETY: WIT GameRule and pumpkin_data::game_rules::GameRule have identical variant order
unsafe { std::mem::transmute::<u8, GameRule>(rule as u8) }
}
pub(crate) fn to_wit_game_rule_value(value: &GameRuleValue<i64, bool>) -> WitGameRuleValue {
match *value {
GameRuleValue::Int(v) => {
WitGameRuleValue::Int(v.clamp(i32::MIN as i64, i32::MAX as i64) as i32)
}
GameRuleValue::Bool(v) => WitGameRuleValue::Bool(v),
}
}
pub(crate) const fn from_wit_game_rule_value(value: WitGameRuleValue) -> GameRuleValue<i64, bool> {
match value {
WitGameRuleValue::Int(v) => GameRuleValue::Int(v as i64),
WitGameRuleValue::Bool(v) => GameRuleValue::Bool(v),
}
}
pub(crate) const fn to_wasm_block_direction(dir: InternalBlockDirection) -> WitBlockDirection {
match dir {
@@ -131,7 +156,7 @@ pub(crate) const fn to_wit_bounding_box(
// --- Trapping Helpers ---
impl PluginHostState {
fn get_world_res(&self, res: &Resource<World>) -> wasmtime::Result<&WorldResource> {
pub(crate) fn get_world_res(&self, res: &Resource<World>) -> wasmtime::Result<&WorldResource> {
self.resource_table
.get::<WorldResource>(&Resource::new_own(res.rep()))
.map_err(wasmtime::Error::from)
@@ -892,6 +917,117 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
Ok(Ok(()))
}
async fn set_chunk_generator(
&mut self,
world: Resource<World>,
generator_id: u32,
) -> wasmtime::Result<()> {
let world_ref = self.get_world_res(&world)?.provider.clone();
let Some(plugin_weak) = self.plugin.as_ref() else {
return Ok(());
};
let Some(plugin) = plugin_weak.upgrade() else {
return Ok(());
};
let wasm_gen = Arc::new(WasmChunkGenerator {
generator_id,
plugin,
dimension: world_ref.dimension.clone(),
seed: world_ref.level.seed.0,
});
world_ref.level.set_world_gen(Arc::new(
pumpkin_world::generation::generator::WorldGenerator::Custom(wasm_gen),
));
Ok(())
}
async fn get_name(&mut self, world: Resource<World>) -> wasmtime::Result<String> {
Ok(self
.get_world_res(&world)?
.provider
.get_world_name()
.to_string())
}
async fn save(&mut self, world: Resource<World>) -> wasmtime::Result<Result<(), String>> {
let world_res = self.get_world_res(&world)?;
world_res.provider.save().await;
Ok(Ok(()))
}
async fn set_custom_data(
&mut self,
world: Resource<World>,
namespace: String,
key: String,
value: super::common::WitNbtTree,
) -> wasmtime::Result<()> {
let world_res = self.get_world_res(&world)?;
let tag = super::common::from_wit_nbt_tree(&value).map_err(wasmtime::Error::msg)?;
world_res.provider.set_custom_data(&namespace, &key, tag);
Ok(())
}
async fn get_custom_data(
&mut self,
world: Resource<World>,
namespace: String,
key: String,
) -> wasmtime::Result<Option<super::common::WitNbtTree>> {
let world_res = self.get_world_res(&world)?;
let tag = world_res.provider.get_custom_data(&namespace, &key);
Ok(tag.map(super::common::to_wit_nbt_tree))
}
async fn remove_custom_data(
&mut self,
world: Resource<World>,
namespace: String,
key: String,
) -> wasmtime::Result<()> {
let world_res = self.get_world_res(&world)?;
world_res.provider.remove_custom_data(&namespace, &key);
Ok(())
}
async fn has_custom_data(
&mut self,
world: Resource<World>,
namespace: String,
key: String,
) -> wasmtime::Result<bool> {
let world_res = self.get_world_res(&world)?;
Ok(world_res.provider.has_custom_data(&namespace, &key))
}
async fn get_game_rule(
&mut self,
world: Resource<World>,
rule: WitGameRule,
) -> wasmtime::Result<WitGameRuleValue> {
let world_res = self.get_world_res(&world)?;
let internal_rule = from_wit_game_rule(rule);
let value = world_res.provider.get_game_rule(&internal_rule);
Ok(to_wit_game_rule_value(&value))
}
async fn set_game_rule(
&mut self,
world: Resource<World>,
rule: WitGameRule,
value: WitGameRuleValue,
) -> wasmtime::Result<()> {
let world_res = self.get_world_res(&world)?;
let internal_rule = from_wit_game_rule(rule);
let internal_value = from_wit_game_rule_value(value);
world_res
.provider
.set_game_rule(&internal_rule, internal_value);
Ok(())
}
async fn drop(&mut self, rep: Resource<World>) -> wasmtime::Result<()> {
self.resource_table
.delete::<WorldResource>(Resource::new_own(rep.rep()))
@@ -1130,6 +1266,67 @@ impl pumpkin::plugin::world::HostChunk for PluginHostState {
}))
}
async fn set_custom_data(
&mut self,
chunk: Resource<WitChunk>,
namespace: String,
key: String,
value: super::common::WitNbtTree,
) -> wasmtime::Result<()> {
let chunk_res = self.get_chunk_res(&chunk)?;
let (_, chunk_data) = &chunk_res.provider;
let Some(chunk_data) = chunk_data.upgrade() else {
return Err(wasmtime::Error::msg("Chunk unloaded"));
};
let tag = super::common::from_wit_nbt_tree(&value).map_err(wasmtime::Error::msg)?;
chunk_data.set_custom_data(&namespace, &key, tag);
Ok(())
}
async fn get_custom_data(
&mut self,
chunk: Resource<WitChunk>,
namespace: String,
key: String,
) -> wasmtime::Result<Option<super::common::WitNbtTree>> {
let chunk_res = self.get_chunk_res(&chunk)?;
let (_, chunk_data) = &chunk_res.provider;
let Some(chunk_data) = chunk_data.upgrade() else {
return Err(wasmtime::Error::msg("Chunk unloaded"));
};
let tag = chunk_data.get_custom_data(&namespace, &key);
Ok(tag.map(super::common::to_wit_nbt_tree))
}
async fn remove_custom_data(
&mut self,
chunk: Resource<WitChunk>,
namespace: String,
key: String,
) -> wasmtime::Result<()> {
let chunk_res = self.get_chunk_res(&chunk)?;
let (_, chunk_data) = &chunk_res.provider;
let Some(chunk_data) = chunk_data.upgrade() else {
return Err(wasmtime::Error::msg("Chunk unloaded"));
};
chunk_data.remove_custom_data(&namespace, &key);
Ok(())
}
async fn has_custom_data(
&mut self,
chunk: Resource<WitChunk>,
namespace: String,
key: String,
) -> wasmtime::Result<bool> {
let chunk_res = self.get_chunk_res(&chunk)?;
let (_, chunk_data) = &chunk_res.provider;
let Some(chunk_data) = chunk_data.upgrade() else {
return Err(wasmtime::Error::msg("Chunk unloaded"));
};
Ok(chunk_data.has_custom_data(&namespace, &key))
}
async fn drop(&mut self, rep: Resource<WitChunk>) -> wasmtime::Result<()> {
self.resource_table
.delete::<ChunkResource>(Resource::new_own(rep.rep()))
@@ -1245,3 +1442,317 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState {
Ok(())
}
}
impl pumpkin::plugin::world::HostChunkBuffer for PluginHostState {
async fn get_x(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
) -> wasmtime::Result<i32> {
let res = self.get_chunk_buffer_res(&this)?;
Ok(res.provider.x)
}
async fn get_z(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
) -> wasmtime::Result<i32> {
let res = self.get_chunk_buffer_res(&this)?;
Ok(res.provider.z)
}
async fn get_min_y(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
) -> wasmtime::Result<i32> {
let res = self.get_chunk_buffer_res(&this)?;
Ok(res.provider.min_y)
}
async fn get_height(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
) -> wasmtime::Result<u32> {
let res = self.get_chunk_buffer_res(&this)?;
Ok(res.provider.height)
}
async fn set_block_state_id(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
x: u8,
y: i32,
z: u8,
state_id: u16,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
if x < 16 && z < 16 {
let world_x =
pumpkin_world::generation::positions::chunk_pos::start_block_x(res.provider.x)
+ x as i32;
let world_z =
pumpkin_world::generation::positions::chunk_pos::start_block_z(res.provider.z)
+ z as i32;
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
let block_state = pumpkin_data::BlockState::from_id(
pumpkin_data::BlockStateId::new(state_id)
.unwrap_or(pumpkin_data::BlockStateId::AIR),
);
proto.set_block_state(world_x, y, world_z, block_state);
}
Ok(())
}
async fn get_block_state_id(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
x: u8,
y: i32,
z: u8,
) -> wasmtime::Result<u16> {
let res = self.get_chunk_buffer_res(&this)?;
if x < 16 && z < 16 {
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &*res.provider.proto_chunk };
let local_y = y - proto.bottom_y() as i32;
if local_y >= 0 && local_y < proto.height() as i32 {
Ok(proto
.get_block_state_raw(x as i32, local_y, z as i32)
.as_u16())
} else {
Ok(0)
}
} else {
Ok(0)
}
}
async fn fill_layer(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
y: i32,
state_id: u16,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
let start_x =
pumpkin_world::generation::positions::chunk_pos::start_block_x(res.provider.x);
let start_z =
pumpkin_world::generation::positions::chunk_pos::start_block_z(res.provider.z);
let block_state = pumpkin_data::BlockState::from_id(
pumpkin_data::BlockStateId::new(state_id).unwrap_or(pumpkin_data::BlockStateId::AIR),
);
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
for x in 0..16 {
for z in 0..16 {
proto.set_block_state(start_x + x, y, start_z + z, block_state);
}
}
Ok(())
}
async fn fill_range(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
x: u8,
min_y: i32,
max_y: i32,
z: u8,
state_id: u16,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
if x < 16 && z < 16 {
let world_x =
pumpkin_world::generation::positions::chunk_pos::start_block_x(res.provider.x)
+ x as i32;
let world_z =
pumpkin_world::generation::positions::chunk_pos::start_block_z(res.provider.z)
+ z as i32;
let block_state = pumpkin_data::BlockState::from_id(
pumpkin_data::BlockStateId::new(state_id)
.unwrap_or(pumpkin_data::BlockStateId::AIR),
);
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
for y in min_y..=max_y {
proto.set_block_state(world_x, y, world_z, block_state);
}
}
Ok(())
}
async fn fill_cuboid(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
min_x: u8,
min_y: i32,
min_z: u8,
max_x: u8,
max_y: i32,
max_z: u8,
state_id: u16,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
let start_x =
pumpkin_world::generation::positions::chunk_pos::start_block_x(res.provider.x);
let start_z =
pumpkin_world::generation::positions::chunk_pos::start_block_z(res.provider.z);
let block_state = pumpkin_data::BlockState::from_id(
pumpkin_data::BlockStateId::new(state_id).unwrap_or(pumpkin_data::BlockStateId::AIR),
);
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
let max_x = max_x.min(15);
let max_z = max_z.min(15);
for x in min_x..=max_x {
for y in min_y..=max_y {
for z in min_z..=max_z {
proto.set_block_state(start_x + x as i32, y, start_z + z as i32, block_state);
}
}
}
Ok(())
}
async fn set_biome(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
x: u8,
y: i32,
z: u8,
biome: pumpkin::plugin::biomes::Biome,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
if x < 16 && z < 16 {
let biome_id = biome as u8;
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
let biome_x = x as i32 / 4;
let biome_z = z as i32 / 4;
let biome_y = (y - proto.bottom_y() as i32) / 4;
if biome_y >= 0 && (biome_y as usize) < (proto.height() as usize / 4) {
let index = proto.local_biome_pos_to_biome_index(biome_x, biome_y, biome_z);
if index < proto.flat_biome_map.len() {
proto.flat_biome_map[index] = biome_id;
}
}
}
Ok(())
}
async fn fill_biome(
&mut self,
this: Resource<pumpkin::plugin::world::ChunkBuffer>,
biome: pumpkin::plugin::biomes::Biome,
) -> wasmtime::Result<()> {
let res = self.get_chunk_buffer_res(&this)?;
let biome_id = biome as u8;
// SAFETY: `proto_chunk` points to a valid proto chunk allocated for world generation and is not aliased across threads.
let proto = unsafe { &mut *res.provider.proto_chunk };
proto.flat_biome_map.fill(biome_id);
Ok(())
}
async fn drop(
&mut self,
rep: Resource<pumpkin::plugin::world::ChunkBuffer>,
) -> wasmtime::Result<()> {
self.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ChunkBufferResource>(
Resource::new_own(rep.rep()),
)
.map_err(wasmtime::Error::from)?;
Ok(())
}
}
pub struct WasmChunkGenerator {
pub generator_id: u32,
pub plugin: Arc<crate::plugin::loader::wasm::wasm_host::WasmPlugin>,
pub dimension: pumpkin_data::dimension::Dimension,
pub seed: u64,
}
impl WasmChunkGenerator {
fn invoke_phase(
&self,
phase: pumpkin::plugin::world::GenerationPhase,
proto_chunk: &mut pumpkin_world::ProtoChunk,
) {
let chunk_buffer = crate::plugin::loader::wasm::wasm_host::state::ChunkBuffer {
x: proto_chunk.x,
z: proto_chunk.z,
min_y: proto_chunk.bottom_y() as i32,
height: proto_chunk.height() as u32,
proto_chunk,
};
futures::executor::block_on(async {
let mut store = self.plugin.store.lock().await;
let Ok(buffer_res) = store.data_mut().add_chunk_buffer(chunk_buffer) else {
return;
};
let buffer_rep = buffer_res.rep();
match self.plugin.plugin_instance {
crate::plugin::loader::wasm::wasm_host::PluginInstance::V0_1(ref plugin) => {
let _ = plugin
.call_handle_generate_phase(
&mut *store,
self.generator_id,
phase,
buffer_res,
)
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ChunkBufferResource>(
wasmtime::component::Resource::new_own(buffer_rep),
);
}
}
});
}
}
impl pumpkin_world::generation::generator::CustomChunkGenerator for WasmChunkGenerator {
fn dimension(&self) -> &pumpkin_data::dimension::Dimension {
&self.dimension
}
fn seed(&self) -> u64 {
self.seed
}
fn step_to_biomes(&self, chunk: &mut pumpkin_world::ProtoChunk) {
self.invoke_phase(pumpkin::plugin::world::GenerationPhase::Biomes, chunk);
chunk.stage = pumpkin_world::chunk_system::StagedChunkEnum::Biomes;
}
fn step_to_noise(&self, chunk: &mut pumpkin_world::ProtoChunk) {
self.invoke_phase(pumpkin::plugin::world::GenerationPhase::Noise, chunk);
chunk.stage = pumpkin_world::chunk_system::StagedChunkEnum::Noise;
}
fn step_to_surface(&self, chunk: &mut pumpkin_world::ProtoChunk) {
self.invoke_phase(pumpkin::plugin::world::GenerationPhase::Surface, chunk);
chunk.stage = pumpkin_world::chunk_system::StagedChunkEnum::Surface;
}
fn step_to_carvers(&self, chunk: &mut pumpkin_world::ProtoChunk) {
chunk.stage = pumpkin_world::chunk_system::StagedChunkEnum::Carvers;
}
fn step_to_features(
&self,
cache: &mut pumpkin_world::chunk_system::generation_cache::Cache,
_block_registry: &dyn pumpkin_world::world::WorldPortalExt,
) {
let mid = ((cache.size * cache.size) >> 1) as usize;
let chunk = cache.chunks[mid].get_proto_chunk_mut();
self.invoke_phase(pumpkin::plugin::world::GenerationPhase::Features, chunk);
chunk.stage = pumpkin_world::chunk_system::StagedChunkEnum::Features;
}
}

View File

@@ -658,6 +658,38 @@ impl PluginManager {
if loader.can_load(&path) {
match loader.load(&path).await {
Ok((instance, metadata, loader_data)) => {
let plugin_override =
server.advanced_config.plugins.overrides.get(&metadata.name);
if plugin_override.is_some_and(|o| !o.enabled) {
info!(
"Plugin \"{}\" is disabled in configuration, skipping.",
metadata.name
);
loader_found = true;
break;
}
let allow_unsigned = plugin_override
.and_then(|o| o.allow_unsigned)
.unwrap_or(server.advanced_config.plugins.allow_unsigned);
if !allow_unsigned
&& path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("wasm"))
{
let wasm_bytes = std::fs::read(&path).unwrap_or_default();
if !crate::plugin::loader::wasm::wasm_host::signature::is_wasm_signed(&wasm_bytes) {
error!(
"Plugin \"{}\" ({:?}) is unsigned or invalid and allow_unsigned is disabled in configuration, skipping.",
metadata.name, path
);
loader_found = true;
break;
}
}
prepared_plugins.push((
instance,
metadata,
@@ -710,7 +742,7 @@ impl PluginManager {
{
let (allowed, wait_time) = self
.clone()
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path)
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path, server)
.await;
total_wait_time += wait_time;
@@ -754,7 +786,33 @@ impl PluginManager {
metadata: &PluginMetadata,
cache: &mut cache::PermissionCache,
cache_path: &Path,
server: &Arc<Server>,
) -> (bool, std::time::Duration) {
let plugin_config = &server.advanced_config.plugins;
let plugin_override = plugin_config.overrides.get(&metadata.name);
let is_blocked = |p: &str| {
plugin_config.blocked_permissions.iter().any(|b| b == p)
|| plugin_override.is_some_and(|o| o.blocked_permissions.iter().any(|b| b == p))
};
let is_pre_allowed = |p: &str| {
plugin_config.allowed_permissions.iter().any(|a| a == p)
|| plugin_override.is_some_and(|o| o.allowed_permissions.iter().any(|a| a == p))
};
let effective_permissions: Vec<String> = metadata
.permissions
.iter()
.filter(|p| !is_blocked(p))
.cloned()
.collect();
// If all requested permissions are pre-allowed, grant without prompting
if !effective_permissions.iter().any(|p| !is_pre_allowed(p)) {
return (true, std::time::Duration::ZERO);
}
let hash = cache::calculate_hash(path).await.unwrap_or_default();
if let Some(entry) = cache.entries.get(&hash)
@@ -767,6 +825,22 @@ impl PluginManager {
return (entry.approved, std::time::Duration::ZERO);
}
if !plugin_config.ask_permission_confirmation {
info!(
"Auto-approving permissions for plugin \"{}\" (ask_permission_confirmation is disabled)",
metadata.name
);
cache.entries.insert(
hash,
cache::PermissionCacheEntry {
permissions_requested: metadata.permissions.clone(),
approved: true,
},
);
let _ = cache.save(cache_path).await;
return (true, std::time::Duration::ZERO);
}
let (allowed, wait_time) = Self::ask_permission_confirmation(metadata);
cache.entries.insert(
hash,
@@ -785,15 +859,51 @@ impl PluginManager {
server: &Arc<Server>,
path: &Path,
) -> Result<tokio::task::JoinHandle<()>, ManagerError> {
if !server.advanced_config.plugins.enabled {
return Err(ManagerError::LoaderError(LoaderError::RuntimeError(
"Plugin system is disabled in configuration".to_string(),
)));
}
for loader in self.loaders.read().await.iter() {
if loader.can_load(path) {
let (instance, metadata, loader_data) = loader.load(path).await?;
let plugin_override = server.advanced_config.plugins.overrides.get(&metadata.name);
if plugin_override.is_some_and(|o| !o.enabled) {
return Err(ManagerError::LoaderError(LoaderError::RuntimeError(
format!("Plugin \"{}\" is disabled in configuration", metadata.name),
)));
}
let allow_unsigned = plugin_override
.and_then(|o| o.allow_unsigned)
.unwrap_or(server.advanced_config.plugins.allow_unsigned);
if !allow_unsigned
&& path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("wasm"))
{
let wasm_bytes = std::fs::read(path).unwrap_or_default();
if !crate::plugin::loader::wasm::wasm_host::signature::is_wasm_signed(
&wasm_bytes,
) {
return Err(ManagerError::LoaderError(LoaderError::RuntimeError(
format!(
"Plugin \"{}\" is unsigned or invalid and allow_unsigned is disabled",
metadata.name
),
)));
}
}
let cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
let mut cache = cache::PermissionCache::load(&cache_path).await;
let (allowed, _) = self
.check_permissions_cached(path, &metadata, &mut cache, &cache_path)
.check_permissions_cached(path, &metadata, &mut cache, &cache_path, server)
.await;
if !allowed {

View File

@@ -0,0 +1,62 @@
use pumpkin_data::enchantment::AttributeModifierSlot;
use pumpkin_util::text::TextComponent;
use std::collections::HashMap;
use tokio::sync::RwLock;
#[derive(Clone, Debug)]
pub struct CustomEnchantmentEntry {
pub id: String,
pub description: TextComponent,
pub max_level: u32,
pub anvil_cost: u32,
pub supported_items: String,
pub weight: u32,
pub slots: Vec<AttributeModifierSlot>,
pub exclusive_set: Vec<String>,
}
pub struct EnchantmentManager {
custom_enchantments: RwLock<HashMap<String, CustomEnchantmentEntry>>,
}
impl Default for EnchantmentManager {
fn default() -> Self {
Self::new()
}
}
impl EnchantmentManager {
#[must_use]
pub fn new() -> Self {
Self {
custom_enchantments: RwLock::new(HashMap::new()),
}
}
pub async fn register(&self, enchantment: CustomEnchantmentEntry) -> Result<(), String> {
let mut map = self.custom_enchantments.write().await;
if map.contains_key(&enchantment.id) {
return Err(format!(
"Enchantment '{}' is already registered",
enchantment.id
));
}
map.insert(enchantment.id.clone(), enchantment);
Ok(())
}
pub async fn get(&self, id: &str) -> Option<CustomEnchantmentEntry> {
let map = self.custom_enchantments.read().await;
map.get(id).cloned()
}
pub async fn has(&self, id: &str) -> bool {
let map = self.custom_enchantments.read().await;
map.contains_key(id)
}
pub async fn get_all_ids(&self) -> Vec<String> {
let map = self.custom_enchantments.read().await;
map.keys().cloned().collect()
}
}

View File

@@ -52,6 +52,7 @@ use tokio::task::{JoinHandle, JoinSet};
use tokio_util::task::TaskTracker;
mod connection_cache;
pub mod enchantment;
mod key_store;
pub mod recipe;
pub mod scheduler;
@@ -105,6 +106,7 @@ pub struct Server {
/// Assigns unique IDs to containers.
container_id: AtomicU32,
pub recipe_manager: Arc<recipe::RecipeManager>,
pub enchantment_manager: Arc<enchantment::EnchantmentManager>,
/// Assigns unique IDs to maps.
map_id: AtomicI32,
/// Mojang's public keys, used for chat session signing
@@ -283,6 +285,7 @@ impl Server {
permission_registry,
container_id: 0.into(),
recipe_manager: Arc::new(recipe::RecipeManager::new()),
enchantment_manager: Arc::new(enchantment::EnchantmentManager::new()),
map_id: level_info.load().map_id.into(),
worlds: ArcSwap::from_pointee(vec![]),
dimensions,
@@ -492,6 +495,61 @@ impl Server {
})
}
pub async fn unload_world(&self, name: &str) -> Result<(), String> {
let worlds = self.worlds.load();
let world_to_unload = worlds
.iter()
.find(|w| w.get_world_name() == name || w.dimension.minecraft_name == name)
.cloned()
.ok_or_else(|| format!("World '{name}' not found"))?;
if let Some(first_world) = worlds.first()
&& Arc::ptr_eq(first_world, &world_to_unload)
{
return Err("Cannot unload the primary/default world".to_string());
}
let player_count = world_to_unload.players.load().len();
if player_count > 0 {
return Err(format!(
"Cannot unload world '{name}': {player_count} players are still in this world"
));
}
world_to_unload.shutdown().await;
world_to_unload.unload().await;
self.worlds.rcu(|w_list| {
let mut new_list = (**w_list).clone();
new_list.retain(|w| !Arc::ptr_eq(w, &world_to_unload));
new_list
});
Ok(())
}
pub async fn save_all(&self) -> Result<(), String> {
if let Err(err) = self.player_data_storage.save_all_players(self).await {
error!("Failed to save player data: {err}");
return Err(format!("Failed to save player data: {err}"));
}
if let Err(err) = self
.advancement_manager
.save_all_players(&self.get_all_players())
.await
{
error!("Failed to save player advancements: {err}");
return Err(format!("Failed to save player advancements: {err}"));
}
for world in self.worlds.load().iter() {
world.save().await;
}
Ok(())
}
/// Adds a new player to the server.
///
/// This function takes an `Arc<Client>` representing the connected client and performs the following actions:

View File

@@ -147,9 +147,16 @@ impl TaskScheduler {
match plugin.plugin_instance {
crate::plugin::loader::wasm::wasm_host::PluginInstance::V0_1(ref instance) => {
if let Ok(server_res) = store.data_mut().add_server(server_clone) {
let server_rep = server_res.rep();
let _ = instance
.call_handle_task(&mut *store, handler_id, server_res)
.await;
let _ = store
.data_mut()
.resource_table
.delete::<crate::plugin::loader::wasm::wasm_host::state::ServerResource>(
wasmtime::component::Resource::new_own(server_rep),
);
}
}
}

View File

@@ -61,6 +61,7 @@ use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::MobCategory;
use pumpkin_data::fluid::{Falling, FluidProperties, FluidState};
use pumpkin_data::game_rules::{GameRule, GameRuleValue};
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_data::{
@@ -274,6 +275,10 @@ pub struct World {
/// Block entities indexed by chunk, so ticking only visits the currently
/// active chunks instead of scanning every loaded block entity each tick.
pub block_entities: DashMap<Vector2<i32>, FxHashMap<BlockPos, Arc<dyn BlockEntity>>>,
/// Persistent custom data for the world (matching Bukkit's `PersistentDataHolder`)
pub custom_data: std::sync::Mutex<NbtCompound>,
/// Persistent custom data for block entities at specific positions
pub custom_block_entity_data: DashMap<BlockPos, NbtCompound>,
}
#[derive(Clone, Copy)]
@@ -346,6 +351,23 @@ impl World {
let portal_poi = portal::PortalPoiStorage::new(level.level_folder.poi_folder.clone());
let dragon_fight = (dimension.minecraft_name == Dimension::THE_END.minecraft_name)
.then(|| Mutex::new(dragon_fight::DragonFight::new()));
let custom_data_path = level
.level_folder
.root_folder
.join("pumpkin_custom_data.nbt");
let custom_data = if custom_data_path.exists()
&& let Ok(bytes) = std::fs::read(&custom_data_path)
&& let Ok(nbt) = pumpkin_nbt::Nbt::read_unnamed(
&mut pumpkin_nbt::deserializer::NbtReadHelperJava::new(&mut std::io::Cursor::new(
bytes,
)),
) {
nbt.root_tag
} else {
NbtCompound::new()
};
Self {
uuid: Uuid::new_v4(),
level,
@@ -369,6 +391,8 @@ impl World {
forced_chunks: std::sync::Mutex::new(FxHashSet::default()),
server,
block_entities: DashMap::new(),
custom_data: std::sync::Mutex::new(custom_data),
custom_block_entity_data: DashMap::new(),
}
}
@@ -482,6 +506,13 @@ impl World {
for block_entity in block_entities {
let mut nbt = NbtCompound::new();
block_entity.write_internal(&mut nbt).await;
if let Some(custom_data) = self
.custom_block_entity_data
.get(&block_entity.get_position())
&& !custom_data.is_empty()
{
nbt.put_compound("PumpkinCustomData", custom_data.clone());
}
self.add_block_entity_nbt(block_entity.get_position(), &nbt);
}
}
@@ -566,6 +597,29 @@ impl World {
self.level_info.store(Arc::new(new_info));
}
pub fn get_game_rule(&self, rule: &GameRule) -> GameRuleValue<i64, bool> {
let level_info = self.level_info.load();
match level_info.game_rules.get(rule) {
GameRuleValue::Int(v) => GameRuleValue::Int(*v),
GameRuleValue::Bool(v) => GameRuleValue::Bool(*v),
}
}
pub fn set_game_rule(&self, rule: &GameRule, value: GameRuleValue<i64, bool>) {
let current_info = self.level_info.load();
let mut new_info = (**current_info).clone();
match (new_info.game_rules.get_mut(rule), value) {
(GameRuleValue::Int(target), GameRuleValue::Int(val)) => {
*target = val;
}
(GameRuleValue::Bool(target), GameRuleValue::Bool(val)) => {
*target = val;
}
_ => {}
}
self.level_info.store(Arc::new(new_info));
}
pub async fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) {
let mut queue = self.synced_block_event_queue.lock().await;
queue.push(BlockEvent { pos, r#type, data });
@@ -5522,6 +5576,13 @@ impl World {
.remove(block_pos)
})
.flatten()?;
if let Some(custom_data) = nbt
.get_compound("PumpkinCustomData")
.or_else(|| nbt.get_compound("BukkitValues"))
{
self.custom_block_entity_data
.insert(*block_pos, custom_data.clone());
}
let entity = block_entity_from_nbt(&nbt)?;
self.block_entities
.entry(chunk_pos)
@@ -5646,6 +5707,7 @@ impl World {
chunk_block_entities.remove(block_pos).is_some()
});
if removed {
self.custom_block_entity_data.remove(block_pos);
// Drop the chunk's map once its last block entity is gone.
self.block_entities
.remove_if(&chunk_pos, |_, entities| entities.is_empty());
@@ -6040,6 +6102,44 @@ impl World {
}
pub async fn save(&self) {
for entity in self.entities.load().iter() {
self.save_entity(entity).await;
}
let chunks: Vec<Vector2<i32>> = self
.block_entities
.iter()
.map(|chunk_block_entities| *chunk_block_entities.key())
.collect();
for chunk_pos in chunks {
self.save_block_entities(&chunk_pos).await;
}
if let Ok(mut portal_poi) = self.portal_poi.try_lock() {
let _ = portal_poi.save_all();
}
{
let custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if !custom_data.is_empty() {
let custom_data_path = self
.level
.level_folder
.root_folder
.join("pumpkin_custom_data.nbt");
let nbt = pumpkin_nbt::Nbt::from(custom_data.clone());
let _ = std::fs::write(custom_data_path, nbt.write());
}
}
self.level
.should_save
.store(true, std::sync::atomic::Ordering::Relaxed);
self.level.level_channel.notify();
let mut save_event = crate::plugin::api::events::world::world_save::WorldSaveEvent::new(
format!("{:?}", self.dimension),
);
@@ -6048,6 +6148,126 @@ impl World {
}
}
pub fn set_custom_data(&self, namespace: &str, key: &str, value: pumpkin_nbt::tag::NbtTag) {
let mut custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut namespace_data = custom_data
.child_tags
.remove(namespace)
.and_then(|tag| match tag {
pumpkin_nbt::tag::NbtTag::Compound(compound) => Some(compound),
_ => None,
})
.unwrap_or_default();
namespace_data.child_tags.insert(key.into(), value);
custom_data.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
}
pub fn get_custom_data(&self, namespace: &str, key: &str) -> Option<pumpkin_nbt::tag::NbtTag> {
let custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
custom_data
.get(namespace)?
.extract_compound()?
.get(key)
.cloned()
}
pub fn remove_custom_data(&self, namespace: &str, key: &str) {
let mut custom_data = self
.custom_data
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let Some(pumpkin_nbt::tag::NbtTag::Compound(mut namespace_data)) =
custom_data.child_tags.remove(namespace)
else {
return;
};
namespace_data.child_tags.remove(key);
if !namespace_data.is_empty() {
custom_data.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
}
}
pub fn has_custom_data(&self, namespace: &str, key: &str) -> bool {
self.get_custom_data(namespace, key).is_some()
}
pub fn set_block_entity_custom_data(
&self,
pos: &BlockPos,
namespace: &str,
key: &str,
value: pumpkin_nbt::tag::NbtTag,
) {
let mut entry = self.custom_block_entity_data.entry(*pos).or_default();
let mut namespace_data = entry
.child_tags
.remove(namespace)
.and_then(|tag| match tag {
pumpkin_nbt::tag::NbtTag::Compound(compound) => Some(compound),
_ => None,
})
.unwrap_or_default();
namespace_data.child_tags.insert(key.into(), value);
entry.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
}
pub fn get_block_entity_custom_data(
&self,
pos: &BlockPos,
namespace: &str,
key: &str,
) -> Option<pumpkin_nbt::tag::NbtTag> {
self.custom_block_entity_data
.get(pos)?
.get(namespace)?
.extract_compound()?
.get(key)
.cloned()
}
pub fn remove_block_entity_custom_data(&self, pos: &BlockPos, namespace: &str, key: &str) {
if let Some(mut entry) = self.custom_block_entity_data.get_mut(pos) {
let Some(pumpkin_nbt::tag::NbtTag::Compound(mut namespace_data)) =
entry.child_tags.remove(namespace)
else {
return;
};
namespace_data.child_tags.remove(key);
if !namespace_data.is_empty() {
entry.child_tags.insert(
namespace.into(),
pumpkin_nbt::tag::NbtTag::Compound(namespace_data),
);
}
}
}
pub fn has_block_entity_custom_data(&self, pos: &BlockPos, namespace: &str, key: &str) -> bool {
self.get_block_entity_custom_data(pos, namespace, key)
.is_some()
}
pub async fn populate_chunk(&self, chunk_pos: Vector2<i32>) {
let mut populate_event =
crate::plugin::api::events::world::chunk_populate::ChunkPopulateEvent::new(chunk_pos);
@@ -6307,4 +6527,40 @@ mod tests {
assert_eq!(actor.get_int("pairz"), Some(7));
assert_eq!(actor.get_bool("pairlead"), Some(true));
}
#[test]
fn game_rules_registry() {
use pumpkin_data::game_rules::{GameRule, GameRuleRegistry, GameRuleValue};
let mut registry = GameRuleRegistry::default();
match registry.get(&GameRule::KeepInventory) {
GameRuleValue::Bool(v) => assert!(!v),
GameRuleValue::Int(_) => panic!("expected bool"),
}
match registry.get_mut(&GameRule::KeepInventory) {
GameRuleValue::Bool(v) => *v = true,
GameRuleValue::Int(_) => panic!("expected bool"),
}
match registry.get(&GameRule::KeepInventory) {
GameRuleValue::Bool(v) => assert!(v),
GameRuleValue::Int(_) => panic!("expected bool"),
}
match registry.get(&GameRule::RandomTickSpeed) {
GameRuleValue::Int(v) => assert_eq!(*v, 3),
GameRuleValue::Bool(_) => panic!("expected int"),
}
match registry.get_mut(&GameRule::RandomTickSpeed) {
GameRuleValue::Int(v) => *v = 20,
GameRuleValue::Bool(_) => panic!("expected int"),
}
match registry.get(&GameRule::RandomTickSpeed) {
GameRuleValue::Int(v) => assert_eq!(*v, 20),
GameRuleValue::Bool(_) => panic!("expected int"),
}
}
}

View File

@@ -67,6 +67,7 @@ pub fn build() -> TokenStream {
let mut constants = Vec::new();
let mut type_from_name = TokenStream::new();
let mut type_from_id = TokenStream::new();
for (name, entry) in damage_types {
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
@@ -76,6 +77,11 @@ pub fn build() -> TokenStream {
#resource_name => Some(Self::#const_ident),
});
let id_lit = LitInt::new(&entry.id.to_string(), proc_macro2::Span::call_site());
type_from_id.extend(quote! {
#id_lit => Some(Self::#const_ident),
});
let data = &entry.components;
let death_message_type = if let Some(msg) = &data.death_message_type {
let msg_ident = Ident::new(&format!("{msg:?}"), proc_macro2::Span::call_site());
@@ -98,7 +104,6 @@ pub fn build() -> TokenStream {
proc_macro2::Span::call_site(),
);
let scaling = quote! {DamageScaling::#scaling_ident};
let id_lit = LitInt::new(&entry.id.to_string(), proc_macro2::Span::call_site());
constants.push(quote! {
pub const #const_ident: DamageType = DamageType {
@@ -160,6 +165,13 @@ pub fn build() -> TokenStream {
}
}
#[doc = r" Try to parse a damage type from a numeric registry id."]
pub const fn from_id(id: u8) -> Option<Self> {
match id {
#type_from_id
_ => None
}
}
}
impl Taggable for DamageType {

View File

@@ -0,0 +1,43 @@
use semver::Version;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::fs;
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
#[derive(Deserialize)]
struct DamageTypeEntry {
id: u8,
}
pub fn build() -> String {
let damage_types: BTreeMap<String, DamageTypeEntry> =
serde_json::from_str(&fs::read_to_string("../../assets/damage_type.json").unwrap())
.expect("Failed to parse damage_type.json");
let mut package = Package::new(PackageName::new(
"pumpkin",
"plugin",
Some(Version::new(0, 1, 0)),
));
let mut interface = Interface::new("damage-types");
let mut damage_type_enum = Enum::empty();
let mut entries: Vec<(&String, &DamageTypeEntry)> = damage_types.iter().collect();
entries.sort_by_key(|(_, entry)| entry.id);
for (raw_name, _) in entries {
let name = raw_name
.strip_prefix("minecraft:")
.unwrap_or(raw_name)
.replace('_', "-");
damage_type_enum.case(name);
}
interface.type_def(TypeDef::new(
"damage-type",
TypeDefKind::Enum(damage_type_enum),
));
package.interface(interface);
package.to_string()
}

View File

@@ -1,37 +1,83 @@
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();
let mut cases = String::new();
for raw_name in enchantment_vec {
let name = raw_name
.strip_prefix("minecraft:")
.unwrap_or(raw_name)
.replace('_', "-");
enchantment_enum.case(name);
cases.push_str(&format!(" {name},\n"));
}
interface.type_def(TypeDef::new(
"enchantment",
TypeDefKind::Enum(enchantment_enum),
));
package.interface(interface);
format!(
r##"package pumpkin:plugin@0.1.0;
package.to_string()
interface enchantments {{
use text.{{text-component}};
/// Equipment slot where an enchantment is active.
enum attribute-modifier-slot {{
any,
main-hand,
off-hand,
hand,
feet,
legs,
chest,
head,
armor,
body,
saddle,
}}
/// Vanilla enchantments enum.
enum enchantment {{
{cases} }}
/// Represents a custom enchantment definition.
record custom-enchantment {{
/// Unique identifier for the enchantment (e.g. "my_plugin:lifesteal").
id: string,
/// Description or display name of the enchantment.
description: text-component,
/// Maximum level of the enchantment (e.g. 1..=10).
max-level: u32,
/// Base anvil repair/combination cost multiplier.
anvil-cost: u32,
/// Tag or item pattern for supported items (e.g. "#minecraft:enchantable/weapon").
supported-items: string,
/// Weight / rarity of the enchantment (higher = more common, default 5).
weight: u32,
/// Equipment slots where this enchantment is active.
slots: list<attribute-modifier-slot>,
/// List of exclusive/conflicting enchantment IDs.
exclusive-set: list<string>,
}}
/// Global manager for registering and querying custom enchantments.
resource enchantment-manager {{
/// Registers a new custom enchantment with the server.
register-enchantment: func(enchantment: custom-enchantment) -> result<_, string>;
/// Gets an enchantment definition by its ID.
get-enchantment: func(id: string) -> option<custom-enchantment>;
/// Checks if an enchantment ID is registered.
has-enchantment: func(id: string) -> bool;
/// Returns all registered custom enchantment IDs.
get-all-enchantment-ids: func() -> list<string>;
}}
}}
"##
)
}

View File

@@ -0,0 +1,41 @@
use semver::Version;
use serde_json::Value;
use std::collections::BTreeMap;
use std::fs;
use wit_encoder::{
Enum, Interface, Package, PackageName, Type, TypeDef, TypeDefKind, Variant, VariantCase,
};
pub fn build() -> String {
let game_rules: BTreeMap<String, Value> =
serde_json::from_str(&fs::read_to_string("../../assets/game_rules.json").unwrap())
.expect("Failed to parse game_rules.json");
let mut package = Package::new(PackageName::new(
"pumpkin",
"plugin",
Some(Version::new(0, 1, 0)),
));
let mut interface = Interface::new("game-rules");
let mut rule_enum = Enum::empty();
for raw_name in game_rules.keys() {
let name = raw_name.replace('_', "-");
rule_enum.case(name);
}
interface.type_def(TypeDef::new("game-rule", TypeDefKind::Enum(rule_enum)));
let mut value_variant = Variant::empty();
value_variant.case(VariantCase::value("int", Type::S32));
value_variant.case(VariantCase::value("bool", Type::Bool));
interface.type_def(TypeDef::new(
"game-rule-value",
TypeDefKind::Variant(value_variant),
));
package.interface(interface);
package.to_string()
}

View File

@@ -1,13 +1,17 @@
pub mod attribute;
pub mod bedrock_packet;
pub mod biome;
pub mod damage_type;
pub mod data_component;
pub mod enchantment;
pub mod entity_type;
pub mod game_rules;
pub mod java_packet;
pub mod packet_mapping;
pub mod particle;
pub mod screen;
pub mod sound;
pub mod statistic;
pub mod utils;
use std::{
@@ -34,6 +38,10 @@ pub fn main() {
(enchantment::build, "enchantments.wit"),
(biome::build, "biomes.wit"),
(attribute::build, "attributes.wit"),
(damage_type::build, "damage-types.wit"),
(screen::build, "screens.wit"),
(statistic::build, "statistics.wit"),
(game_rules::build, "game-rules.wit"),
];
for (build_fn, file) in build_functions {

View File

@@ -0,0 +1,26 @@
use semver::Version;
use std::fs;
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
pub fn build() -> String {
let screens: Vec<String> =
serde_json::from_str(&fs::read_to_string("../../assets/screens.json").unwrap())
.expect("Failed to parse screens.json");
let mut package = Package::new(PackageName::new(
"pumpkin",
"plugin",
Some(Version::new(0, 1, 0)),
));
let mut interface = Interface::new("screens");
let mut screen_enum = Enum::empty();
for screen in screens {
screen_enum.case(screen.replace('_', "-"));
}
interface.type_def(TypeDef::new("screen", TypeDefKind::Enum(screen_enum)));
package.interface(interface);
package.to_string()
}

View File

@@ -0,0 +1,73 @@
use indexmap::IndexMap;
use semver::Version;
use serde::Deserialize;
use std::fs;
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
#[derive(Deserialize)]
struct CustomStatisticEntry {
id: i32,
}
#[derive(Deserialize)]
struct StatisticData {
id: i32,
registry: String,
entries: IndexMap<String, CustomStatisticEntry>,
}
pub fn build() -> String {
let stats_json =
fs::read_to_string("../../assets/stats.json").expect("Failed to read stats.json");
let stats_data: IndexMap<String, StatisticData> =
serde_json::from_str(&stats_json).expect("Failed to parse stats.json");
let mut package = Package::new(PackageName::new(
"pumpkin",
"plugin",
Some(Version::new(0, 1, 0)),
));
let mut interface = Interface::new("statistics");
// Category enum
let mut category_enum = Enum::empty();
let mut category_entries: Vec<(&String, &StatisticData)> = stats_data.iter().collect();
category_entries.sort_by_key(|(_, data)| data.id);
for (raw_name, _) in category_entries {
let name = raw_name
.strip_prefix("minecraft:")
.unwrap_or(raw_name)
.replace('_', "-");
category_enum.case(name);
}
interface.type_def(TypeDef::new(
"statistic-category",
TypeDefKind::Enum(category_enum),
));
// Custom statistics enum
if let Some(custom_data) = stats_data.get("minecraft:custom") {
let mut custom_enum = Enum::empty();
let mut entries: Vec<(&String, &CustomStatisticEntry)> =
custom_data.entries.iter().collect();
entries.sort_by_key(|(_, entry)| entry.id);
for (raw_name, _) in entries {
let name = raw_name
.strip_prefix("minecraft:")
.unwrap_or(raw_name)
.replace('_', "-");
custom_enum.case(name);
}
interface.type_def(TypeDef::new(
"custom-statistic",
TypeDefKind::Enum(custom_enum),
));
}
package.interface(interface);
package.to_string()
}