mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat: more plugin features
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
207
crates/pumpkin-plugin-api/src/display.rs
Normal file
207
crates/pumpkin-plugin-api/src/display.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
377
crates/pumpkin-plugin-api/src/enchantment.rs
Normal file
377
crates/pumpkin-plugin-api/src/enchantment.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
39
crates/pumpkin-plugin-api/src/ext/advancement.rs
Normal file
39
crates/pumpkin-plugin-api/src/ext/advancement.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
30
crates/pumpkin-plugin-api/src/ext/game_rules.rs
Normal file
30
crates/pumpkin-plugin-api/src/ext/game_rules.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
477
crates/pumpkin-plugin-api/src/persistent_data.rs
Normal file
477
crates/pumpkin-plugin-api/src/persistent_data.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
1097
crates/pumpkin-plugin-api/src/recipe.rs
Normal file
1097
crates/pumpkin-plugin-api/src/recipe.rs
Normal file
File diff suppressed because it is too large
Load Diff
371
crates/pumpkin-plugin-api/src/team.rs
Normal file
371
crates/pumpkin-plugin-api/src/team.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
129
crates/pumpkin-plugin-api/src/worldgen.rs
Normal file
129
crates/pumpkin-plugin-api/src/worldgen.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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...
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
1124
crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs
Normal file
1124
crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/display.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
62
crates/pumpkin/src/server/enchantment.rs
Normal file
62
crates/pumpkin/src/server/enchantment.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
43
tools/pumpkin-codegen/src/wit/damage_type.rs
Normal file
43
tools/pumpkin-codegen/src/wit/damage_type.rs
Normal 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()
|
||||
}
|
||||
@@ -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>;
|
||||
}}
|
||||
}}
|
||||
"##
|
||||
)
|
||||
}
|
||||
|
||||
41
tools/pumpkin-codegen/src/wit/game_rules.rs
Normal file
41
tools/pumpkin-codegen/src/wit/game_rules.rs
Normal 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()
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
26
tools/pumpkin-codegen/src/wit/screen.rs
Normal file
26
tools/pumpkin-codegen/src/wit/screen.rs
Normal 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()
|
||||
}
|
||||
73
tools/pumpkin-codegen/src/wit/statistic.rs
Normal file
73
tools/pumpkin-codegen/src/wit/statistic.rs
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user