feat: more plugin features
This commit is contained in:
parent
4ec5eb8a45
commit
8ce513ff89
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5434,6 +5434,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"bytes",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
@ -5692,6 +5693,7 @@ dependencies = [
|
|||||||
"rustls",
|
"rustls",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
"wasmtime-wasi",
|
"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"] }
|
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 = { 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 = { version = "47.0", default-features = false, features = ["p2", "p3"] }
|
||||||
wasmtime-wasi-http = { version = "47.0", default-features = false, features = ["p2", "default-send-request"] }
|
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
|
# needed for interacting with wasmtime-wasi-http - keep in sync
|
||||||
hyper = { version = "^1.11", default-features = false }
|
hyper = { version = "^1.11", default-features = false }
|
||||||
axum = { version = "0.8.9", default-features = false, features = ["http1", "tokio"] }
|
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::java::JavaConfig;
|
||||||
pub use networking::lan_broadcast::LANBroadcastConfig;
|
pub use networking::lan_broadcast::LANBroadcastConfig;
|
||||||
pub use networking::rcon::RCONConfig;
|
pub use networking::rcon::RCONConfig;
|
||||||
pub use plugins::PluginsConfig;
|
pub use plugins::{PluginOverride, PluginsConfig};
|
||||||
pub use pvp::PVPConfig;
|
pub use pvp::PVPConfig;
|
||||||
pub use server_links::ServerLinksConfig;
|
pub use server_links::ServerLinksConfig;
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,167 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Plugin system configuration.
|
/// Plugin system configuration.
|
||||||
#[derive(Deserialize, Serialize, Default)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct PluginsConfig {
|
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.
|
/// List of permissions that are globally blocked for all plugins.
|
||||||
pub blocked_permissions: Vec<String>,
|
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,
|
_ => 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 {
|
impl Taggable for DamageType {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -67639,7 +67639,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const ARCHER_POTTERY_SHERD: Self = Self {
|
||||||
id: 671,
|
id: 671,
|
||||||
registry_key: "minecraft:archer_pottery_sherd",
|
registry_key: "minecraft:archer_pottery_sherd",
|
||||||
@ -67724,7 +67724,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BALLOON: Self = Self {
|
||||||
id: 612,
|
id: 612,
|
||||||
registry_key: "minecraft:balloon",
|
registry_key: "minecraft:balloon",
|
||||||
@ -67977,7 +67977,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BEEHIVE: Self = Self {
|
||||||
id: -219,
|
id: -219,
|
||||||
registry_key: "minecraft:beehive",
|
registry_key: "minecraft:beehive",
|
||||||
@ -67985,9 +67985,9 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 : 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\x08\x0Bcrop_result\x12minecraft:beetroot\t\x08plant_at\x08\x02\x12minecraft:farmland\x08\rplant_at_face\x02up\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\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\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_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 {
|
pub const BELL: Self = Self {
|
||||||
id: -206,
|
id: -206,
|
||||||
registry_key: "minecraft:bell",
|
registry_key: "minecraft:bell",
|
||||||
@ -68149,7 +68149,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BLACK_CANDLE: Self = Self {
|
||||||
id: -428,
|
id: -428,
|
||||||
registry_key: "minecraft:black_candle",
|
registry_key: "minecraft:black_candle",
|
||||||
@ -68325,7 +68325,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BLUE_CANDLE: Self = Self {
|
||||||
id: -424,
|
id: -424,
|
||||||
registry_key: "minecraft:blue_candle",
|
registry_key: "minecraft:blue_candle",
|
||||||
@ -68564,8 +68564,8 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 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\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 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 {
|
pub const BREEZE_SPAWN_EGG: Self = Self {
|
||||||
id: 506,
|
id: 506,
|
||||||
registry_key: "minecraft:breeze_spawn_egg",
|
registry_key: "minecraft:breeze_spawn_egg",
|
||||||
@ -68629,7 +68629,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BROWN_CANDLE: Self = Self {
|
||||||
id: -425,
|
id: -425,
|
||||||
registry_key: "minecraft:brown_candle",
|
registry_key: "minecraft:brown_candle",
|
||||||
@ -68805,7 +68805,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const BURN_POTTERY_SHERD: Self = Self {
|
||||||
id: 675,
|
id: 675,
|
||||||
registry_key: "minecraft:burn_pottery_sherd",
|
registry_key: "minecraft:burn_pottery_sherd",
|
||||||
@ -68869,7 +68869,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const CAMPFIRE: Self = Self {
|
||||||
id: 601,
|
id: 601,
|
||||||
registry_key: "minecraft:campfire",
|
registry_key: "minecraft:campfire",
|
||||||
@ -68898,7 +68898,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const CARROT_ON_A_STICK: Self = Self {
|
||||||
id: 527,
|
id: 527,
|
||||||
registry_key: "minecraft:carrot_on_a_stick",
|
registry_key: "minecraft:carrot_on_a_stick",
|
||||||
@ -69200,7 +69200,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const CHICKEN_SPAWN_EGG: Self = Self {
|
||||||
id: 439,
|
id: 439,
|
||||||
registry_key: "minecraft:chicken_spawn_egg",
|
registry_key: "minecraft:chicken_spawn_egg",
|
||||||
@ -69320,7 +69320,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const CHORUS_PLANT: Self = Self {
|
||||||
id: 240,
|
id: 240,
|
||||||
registry_key: "minecraft:chorus_plant",
|
registry_key: "minecraft:chorus_plant",
|
||||||
@ -69545,7 +69545,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const COD_BUCKET: Self = Self {
|
||||||
id: 367,
|
id: 367,
|
||||||
registry_key: "minecraft:cod_bucket",
|
registry_key: "minecraft:cod_bucket",
|
||||||
@ -69672,14 +69672,14 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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_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\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_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\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_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\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_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\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_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\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_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\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 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\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 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 {
|
pub const COPPER_AXE: Self = Self {
|
||||||
id: 750,
|
id: 750,
|
||||||
registry_key: "minecraft:copper_axe",
|
registry_key: "minecraft:copper_axe",
|
||||||
@ -69841,7 +69841,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const COPPER_SWORD: Self = Self {
|
||||||
id: 747,
|
id: 747,
|
||||||
registry_key: "minecraft:copper_sword",
|
registry_key: "minecraft:copper_sword",
|
||||||
@ -70206,7 +70206,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const CYAN_CANDLE: Self = Self {
|
||||||
id: -422,
|
id: -422,
|
||||||
registry_key: "minecraft:cyan_candle",
|
registry_key: "minecraft:cyan_candle",
|
||||||
@ -70928,7 +70928,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const DIAMOND_SWORD: Self = Self {
|
||||||
id: 318,
|
id: 318,
|
||||||
registry_key: "minecraft:diamond_sword",
|
registry_key: "minecraft:diamond_sword",
|
||||||
@ -71083,7 +71083,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const DRIED_KELP_BLOCK: Self = Self {
|
||||||
id: -139,
|
id: -139,
|
||||||
registry_key: "minecraft:dried_kelp_block",
|
registry_key: "minecraft:dried_kelp_block",
|
||||||
@ -72029,7 +72029,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const ENCHANTING_TABLE: Self = Self {
|
||||||
id: 116,
|
id: 116,
|
||||||
registry_key: "minecraft:enchanting_table",
|
registry_key: "minecraft:enchanting_table",
|
||||||
@ -72604,7 +72604,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GLOW_FRAME: Self = Self {
|
||||||
id: 636,
|
id: 636,
|
||||||
registry_key: "minecraft:glow_frame",
|
registry_key: "minecraft:glow_frame",
|
||||||
@ -72703,7 +72703,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GOLDEN_AXE: Self = Self {
|
||||||
id: 328,
|
id: 328,
|
||||||
registry_key: "minecraft:golden_axe",
|
registry_key: "minecraft:golden_axe",
|
||||||
@ -72718,7 +72718,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GOLDEN_CHESTPLATE: Self = Self {
|
||||||
id: 355,
|
id: 355,
|
||||||
registry_key: "minecraft:golden_chestplate",
|
registry_key: "minecraft:golden_chestplate",
|
||||||
@ -72789,7 +72789,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GOLDEN_SWORD: Self = Self {
|
||||||
id: 325,
|
id: 325,
|
||||||
registry_key: "minecraft:golden_sword",
|
registry_key: "minecraft:golden_sword",
|
||||||
@ -72853,7 +72853,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GRAY_CANDLE: Self = Self {
|
||||||
id: -420,
|
id: -420,
|
||||||
registry_key: "minecraft:gray_candle",
|
registry_key: "minecraft:gray_candle",
|
||||||
@ -72952,7 +72952,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const GREEN_CANDLE: Self = Self {
|
||||||
id: -426,
|
id: -426,
|
||||||
registry_key: "minecraft:green_candle",
|
registry_key: "minecraft:green_candle",
|
||||||
@ -73415,7 +73415,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const HONEYCOMB: Self = Self {
|
||||||
id: 603,
|
id: 603,
|
||||||
registry_key: "minecraft:honeycomb",
|
registry_key: "minecraft:honeycomb",
|
||||||
@ -73717,7 +73717,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const IRON_SWORD: Self = Self {
|
||||||
id: 309,
|
id: 309,
|
||||||
registry_key: "minecraft:iron_sword",
|
registry_key: "minecraft:iron_sword",
|
||||||
@ -74299,7 +74299,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const LIGHT_BLUE_CANDLE: Self = Self {
|
||||||
id: -416,
|
id: -416,
|
||||||
registry_key: "minecraft:light_blue_candle",
|
registry_key: "minecraft:light_blue_candle",
|
||||||
@ -74398,7 +74398,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const LIGHT_GRAY_CANDLE: Self = Self {
|
||||||
id: -421,
|
id: -421,
|
||||||
registry_key: "minecraft:light_gray_candle",
|
registry_key: "minecraft:light_gray_candle",
|
||||||
@ -74518,7 +74518,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const LIME_CANDLE: Self = Self {
|
||||||
id: -418,
|
id: -418,
|
||||||
registry_key: "minecraft:lime_candle",
|
registry_key: "minecraft:lime_candle",
|
||||||
@ -74722,7 +74722,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const MAGENTA_CANDLE: Self = Self {
|
||||||
id: -415,
|
id: -415,
|
||||||
registry_key: "minecraft:magenta_candle",
|
registry_key: "minecraft:magenta_candle",
|
||||||
@ -75024,8 +75024,8 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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_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\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_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 {
|
pub const MELON_STEM: Self = Self {
|
||||||
id: 105,
|
id: 105,
|
||||||
registry_key: "minecraft:melon_stem",
|
registry_key: "minecraft:melon_stem",
|
||||||
@ -75243,7 +75243,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const MUSIC_DISC_11: Self = Self {
|
||||||
id: 554,
|
id: 554,
|
||||||
registry_key: "minecraft:music_disc_11",
|
registry_key: "minecraft:music_disc_11",
|
||||||
@ -75398,7 +75398,7 @@ impl BedrockItem {
|
|||||||
component_based: true,
|
component_based: true,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const MYCELIUM: Self = Self {
|
||||||
id: 110,
|
id: 110,
|
||||||
registry_key: "minecraft:mycelium",
|
registry_key: "minecraft:mycelium",
|
||||||
@ -75490,7 +75490,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const NETHER_WART_BLOCK: Self = Self {
|
||||||
id: 214,
|
id: 214,
|
||||||
registry_key: "minecraft:nether_wart_block",
|
registry_key: "minecraft:nether_wart_block",
|
||||||
@ -75596,7 +75596,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const NETHERITE_SWORD: Self = Self {
|
||||||
id: 617,
|
id: 617,
|
||||||
registry_key: "minecraft:netherite_sword",
|
registry_key: "minecraft:netherite_sword",
|
||||||
@ -75793,7 +75793,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const OPEN_EYEBLOSSOM: Self = Self {
|
||||||
id: -1018,
|
id: -1018,
|
||||||
registry_key: "minecraft:open_eyeblossom",
|
registry_key: "minecraft:open_eyeblossom",
|
||||||
@ -75801,7 +75801,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const ORANGE_CANDLE: Self = Self {
|
||||||
id: -414,
|
id: -414,
|
||||||
registry_key: "minecraft:orange_candle",
|
registry_key: "minecraft:orange_candle",
|
||||||
@ -76334,7 +76334,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const PINK_CANDLE: Self = Self {
|
||||||
id: -419,
|
id: -419,
|
||||||
registry_key: "minecraft:pink_candle",
|
registry_key: "minecraft:pink_candle",
|
||||||
@ -76475,7 +76475,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const PLANKS: Self = Self {
|
||||||
id: 814,
|
id: 814,
|
||||||
registry_key: "minecraft:planks",
|
registry_key: "minecraft:planks",
|
||||||
@ -76511,7 +76511,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const POLAR_BEAR_SPAWN_EGG: Self = Self {
|
||||||
id: 477,
|
id: 477,
|
||||||
registry_key: "minecraft:polar_bear_spawn_egg",
|
registry_key: "minecraft:polar_bear_spawn_egg",
|
||||||
@ -76988,7 +76988,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const PORTAL: Self = Self {
|
||||||
id: 90,
|
id: 90,
|
||||||
registry_key: "minecraft:portal",
|
registry_key: "minecraft:portal",
|
||||||
@ -76996,7 +76996,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const POTATOES: Self = Self {
|
||||||
id: 142,
|
id: 142,
|
||||||
registry_key: "minecraft:potatoes",
|
registry_key: "minecraft:potatoes",
|
||||||
@ -77130,7 +77130,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const PUFFERFISH_BUCKET: Self = Self {
|
||||||
id: 370,
|
id: 370,
|
||||||
registry_key: "minecraft:pufferfish_bucket",
|
registry_key: "minecraft:pufferfish_bucket",
|
||||||
@ -77152,8 +77152,8 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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_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\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_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 {
|
pub const PUMPKIN_STEM: Self = Self {
|
||||||
id: 104,
|
id: 104,
|
||||||
registry_key: "minecraft:pumpkin_stem",
|
registry_key: "minecraft:pumpkin_stem",
|
||||||
@ -77161,7 +77161,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const PURPLE_CANDLE: Self = Self {
|
||||||
id: -423,
|
id: -423,
|
||||||
registry_key: "minecraft:purple_candle",
|
registry_key: "minecraft:purple_candle",
|
||||||
@ -77351,7 +77351,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const RABBIT_FOOT: Self = Self {
|
||||||
id: 538,
|
id: 538,
|
||||||
registry_key: "minecraft:rabbit_foot",
|
registry_key: "minecraft:rabbit_foot",
|
||||||
@ -77373,7 +77373,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const RAIL: Self = Self {
|
||||||
id: 66,
|
id: 66,
|
||||||
registry_key: "minecraft:rail",
|
registry_key: "minecraft:rail",
|
||||||
@ -77451,7 +77451,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const RED_CANDLE: Self = Self {
|
||||||
id: -427,
|
id: -427,
|
||||||
registry_key: "minecraft:red_candle",
|
registry_key: "minecraft:red_candle",
|
||||||
@ -77809,7 +77809,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const SADDLE: Self = Self {
|
||||||
id: 374,
|
id: 374,
|
||||||
registry_key: "minecraft:saddle",
|
registry_key: "minecraft:saddle",
|
||||||
@ -77817,7 +77817,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const SALMON_BUCKET: Self = Self {
|
||||||
id: 368,
|
id: 368,
|
||||||
registry_key: "minecraft:salmon_bucket",
|
registry_key: "minecraft:salmon_bucket",
|
||||||
@ -78378,7 +78378,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const SPIDER_SPAWN_EGG: Self = Self {
|
||||||
id: 450,
|
id: 450,
|
||||||
registry_key: "minecraft:spider_spawn_egg",
|
registry_key: "minecraft:spider_spawn_egg",
|
||||||
@ -78743,7 +78743,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const STONE_STAIRS: Self = Self {
|
||||||
id: 67,
|
id: 67,
|
||||||
registry_key: "minecraft:stone_stairs",
|
registry_key: "minecraft:stone_stairs",
|
||||||
@ -79122,8 +79122,8 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 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: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 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 {
|
pub const SWEET_BERRY_BUSH: Self = Self {
|
||||||
id: -207,
|
id: -207,
|
||||||
registry_key: "minecraft:sweet_berry_bush",
|
registry_key: "minecraft:sweet_berry_bush",
|
||||||
@ -79222,7 +79222,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const TOTEM_OF_UNDYING: Self = Self {
|
||||||
id: 578,
|
id: 578,
|
||||||
registry_key: "minecraft:totem_of_undying",
|
registry_key: "minecraft:totem_of_undying",
|
||||||
@ -79251,7 +79251,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const TRIAL_SPAWNER: Self = Self {
|
||||||
id: -315,
|
id: -315,
|
||||||
registry_key: "minecraft:trial_spawner",
|
registry_key: "minecraft:trial_spawner",
|
||||||
@ -79280,7 +79280,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const TROPICAL_FISH_BUCKET: Self = Self {
|
||||||
id: 369,
|
id: 369,
|
||||||
registry_key: "minecraft:tropical_fish_bucket",
|
registry_key: "minecraft:tropical_fish_bucket",
|
||||||
@ -80331,8 +80331,8 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 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\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 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 {
|
pub const WHITE_CANDLE: Self = Self {
|
||||||
id: -413,
|
id: -413,
|
||||||
registry_key: "minecraft:white_candle",
|
registry_key: "minecraft:white_candle",
|
||||||
@ -80452,7 +80452,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const WITCH_SPAWN_EGG: Self = Self {
|
||||||
id: 456,
|
id: 456,
|
||||||
registry_key: "minecraft:witch_spawn_egg",
|
registry_key: "minecraft:witch_spawn_egg",
|
||||||
@ -80565,7 +80565,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const WOODEN_SWORD: Self = Self {
|
||||||
id: 310,
|
id: 310,
|
||||||
registry_key: "minecraft:wooden_sword",
|
registry_key: "minecraft:wooden_sword",
|
||||||
@ -80594,7 +80594,7 @@ impl BedrockItem {
|
|||||||
component_based: false,
|
component_based: false,
|
||||||
definition_components: b"\n\0\0",
|
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 {
|
pub const YELLOW_CANDLE: Self = Self {
|
||||||
id: -417,
|
id: -417,
|
||||||
registry_key: "minecraft:yellow_candle",
|
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>()
|
self.get_data_component::<CustomDataImpl>()
|
||||||
.map(|custom_data| &custom_data.data)
|
.map(|custom_data| &custom_data.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,7 +181,7 @@ pub mod dimension;
|
|||||||
#[cfg(feature = "enchantment")]
|
#[cfg(feature = "enchantment")]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[path = "generated/enchantment.rs"]
|
#[path = "generated/enchantment.rs"]
|
||||||
mod enchantment;
|
pub mod enchantment;
|
||||||
|
|
||||||
#[cfg(feature = "enchantment")]
|
#[cfg(feature = "enchantment")]
|
||||||
pub use enchantment::*;
|
pub use enchantment::*;
|
||||||
|
|||||||
@ -56,7 +56,10 @@ impl EnderChestInventory {
|
|||||||
///
|
///
|
||||||
/// Used to animate the ender chest lid based on viewers.
|
/// Used to animate the ender chest lid based on viewers.
|
||||||
pub async fn set_tracker(&self, tracker: Arc<ViewerCountTracker>) {
|
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.
|
/// Checks if this inventory has a tracker set.
|
||||||
@ -88,21 +91,28 @@ impl Inventory for EnderChestInventory {
|
|||||||
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let items = self.items.read().await;
|
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> {
|
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut items = self.items.write().await;
|
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> {
|
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut items = self.items.write().await;
|
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)
|
items[slot].split(amount)
|
||||||
} else {
|
} else {
|
||||||
ItemStack::EMPTY.clone()
|
ItemStack::EMPTY.clone()
|
||||||
@ -113,7 +123,9 @@ impl Inventory for EnderChestInventory {
|
|||||||
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
|
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut items = self.items.write().await;
|
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<'_, ()> {
|
fn on_close(&self) -> InventoryFuture<'_, ()> {
|
||||||
Box::pin(async move {
|
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();
|
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.
|
//! WIT-generated plugin type extensions belong in here. For example [Display](std::fmt::Display) implementations.
|
||||||
|
|
||||||
|
mod advancement;
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod player;
|
mod game_rules;
|
||||||
|
pub mod player;
|
||||||
mod server_list_ping;
|
mod server_list_ping;
|
||||||
mod uuid;
|
mod uuid;
|
||||||
|
|||||||
@ -1,9 +1,33 @@
|
|||||||
|
use crate::wit::pumpkin::plugin::item_stack::ItemStack;
|
||||||
use crate::wit::pumpkin::plugin::player::{
|
use crate::wit::pumpkin::plugin::player::{
|
||||||
BanIpOptions, BanPlayerOptions, BedrockDisconnectReason, BedrockKickOptions, JavaKickOptions,
|
BanIpOptions, BanPlayerOptions, BedrockDisconnectReason, BedrockKickOptions, JavaKickOptions,
|
||||||
SocketTeardownPolicy,
|
Player, SocketTeardownPolicy,
|
||||||
};
|
};
|
||||||
use crate::wit::pumpkin::plugin::text::TextComponent;
|
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 {
|
impl JavaKickOptions {
|
||||||
/// Creates a new `JavaKickOptions` with the given reason and default settings.
|
/// Creates a new `JavaKickOptions` with the given reason and default settings.
|
||||||
#[must_use]
|
#[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.
|
/// Plugin command registration and handling utilities.
|
||||||
pub mod commands;
|
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.
|
/// Event system and event handlers.
|
||||||
pub mod events;
|
pub mod events;
|
||||||
mod ext;
|
mod ext;
|
||||||
@ -82,8 +86,14 @@ pub mod forms;
|
|||||||
///
|
///
|
||||||
/// Use these in your `PluginMetadata` to request access to specific host features.
|
/// Use these in your `PluginMetadata` to request access to specific host features.
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
|
/// Custom recipe registration and builder utilities.
|
||||||
|
pub mod recipe;
|
||||||
/// Scheduler utilities.
|
/// Scheduler utilities.
|
||||||
pub mod scheduler;
|
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.
|
/// Command WIT API re-exports.
|
||||||
pub mod command {
|
pub mod command {
|
||||||
@ -94,19 +104,53 @@ pub mod command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use wit::pumpkin::plugin::{
|
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},
|
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,
|
entity_types::EntityType,
|
||||||
event::{self as events_wit, EventType},
|
event::{self as events_wit, EventType},
|
||||||
gui, i18n, ipc, item_stack, java_dialogs, java_packets, particles, permission, player,
|
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
|
// 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`).
|
// 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 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::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.
|
/// Java dialog WIT API re-exports.
|
||||||
pub mod java_dialog {
|
pub mod java_dialog {
|
||||||
@ -270,6 +314,33 @@ impl wit::Guest for Component {
|
|||||||
) -> Result<wit::IpcMessage, String> {
|
) -> Result<wit::IpcMessage, String> {
|
||||||
plugin().handle_ipc_message(sender, message)
|
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.
|
/// 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.
|
/// AI and mob goal utilities.
|
||||||
pub mod ai;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::Cursor;
|
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_nbt::{Nbt, compound::NbtCompound, deserializer::NbtReadHelperBedrock};
|
||||||
use pumpkin_world::{
|
use pumpkin_world::chunk::ChunkData;
|
||||||
chunk::{ChunkData, ChunkHeightmaps, ChunkLight, ChunkSections},
|
|
||||||
tick::scheduler::ChunkTickScheduler,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::CLevelChunk;
|
use super::CLevelChunk;
|
||||||
use crate::serial::PacketWrite;
|
use crate::serial::PacketWrite;
|
||||||
@ -214,21 +206,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn empty_chunk() -> ChunkData {
|
fn empty_chunk() -> ChunkData {
|
||||||
ChunkData {
|
ChunkData::empty(0, 0)
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -37,6 +37,7 @@ pub struct OwnedRecipeResult {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum OwnedCraftingRecipe {
|
pub enum OwnedCraftingRecipe {
|
||||||
Shaped {
|
Shaped {
|
||||||
|
recipe_id: Option<String>,
|
||||||
category: RecipeCategoryTypes,
|
category: RecipeCategoryTypes,
|
||||||
group: Option<String>,
|
group: Option<String>,
|
||||||
show_notification: bool,
|
show_notification: bool,
|
||||||
@ -45,6 +46,7 @@ pub enum OwnedCraftingRecipe {
|
|||||||
result: OwnedRecipeResult,
|
result: OwnedRecipeResult,
|
||||||
},
|
},
|
||||||
Shapeless {
|
Shapeless {
|
||||||
|
recipe_id: Option<String>,
|
||||||
category: RecipeCategoryTypes,
|
category: RecipeCategoryTypes,
|
||||||
group: Option<String>,
|
group: Option<String>,
|
||||||
ingredients: Vec<OwnedRecipeIngredient>,
|
ingredients: Vec<OwnedRecipeIngredient>,
|
||||||
|
|||||||
@ -372,6 +372,12 @@ impl ChunkData {
|
|||||||
_ => ChunkStatus::Empty,
|
_ => ChunkStatus::Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let custom_data = root_tag
|
||||||
|
.get_compound("PumpkinCustomData")
|
||||||
|
.or_else(|| root_tag.get_compound("BukkitValues"))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
section,
|
section,
|
||||||
heightmap: std::sync::Mutex::new(heightmaps),
|
heightmap: std::sync::Mutex::new(heightmaps),
|
||||||
@ -387,6 +393,7 @@ impl ChunkData {
|
|||||||
status,
|
status,
|
||||||
blending_data: None,
|
blending_data: None,
|
||||||
inhabited_time: AtomicU64::new(root_tag.get_long("InhabitedTime").unwrap_or(0) as u64),
|
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,
|
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);
|
let nbt = pumpkin_nbt::Nbt::from(root_compound);
|
||||||
nbt.write()
|
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 {
|
impl PathFromLevelFolder for ChunkEntityData {
|
||||||
|
|||||||
@ -82,6 +82,7 @@ pub struct ChunkData {
|
|||||||
pub blending_data: Option<crate::generation::blender::blending_data::BlendingData>,
|
pub blending_data: Option<crate::generation::blender::blending_data::BlendingData>,
|
||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
pub inhabited_time: AtomicU64,
|
pub inhabited_time: AtomicU64,
|
||||||
|
pub custom_data: std::sync::Mutex<NbtCompound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunkEntityData {
|
pub struct ChunkEntityData {
|
||||||
@ -614,6 +615,7 @@ impl ChunkData {
|
|||||||
blending_data: None,
|
blending_data: None,
|
||||||
dirty: std::sync::atomic::AtomicBool::new(false),
|
dirty: std::sync::atomic::AtomicBool::new(false),
|
||||||
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
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(leaves)); // Excludes leaves
|
||||||
assert!(ChunkHeightmapType::MotionBlockingNoLeaves.is_opaque(water)); // Water is liquid
|
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 crate::level::SyncChunk;
|
||||||
|
|
||||||
use pumpkin_data::chunk::ChunkStatus;
|
use pumpkin_data::chunk::ChunkStatus;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -278,6 +279,7 @@ impl Chunk {
|
|||||||
blending_data: None,
|
blending_data: None,
|
||||||
dirty: AtomicBool::new(false),
|
dirty: AtomicBool::new(false),
|
||||||
inhabited_time: AtomicU64::new(0),
|
inhabited_time: AtomicU64::new(0),
|
||||||
|
custom_data: Mutex::new(NbtCompound::new()),
|
||||||
})),
|
})),
|
||||||
) {
|
) {
|
||||||
Self::Proto(proto) => proto,
|
Self::Proto(proto) => proto,
|
||||||
@ -324,6 +326,7 @@ impl Chunk {
|
|||||||
status: proto_chunk.stage.into(),
|
status: proto_chunk.stage.into(),
|
||||||
blending_data: proto_chunk.blending_data,
|
blending_data: proto_chunk.blending_data,
|
||||||
inhabited_time: AtomicU64::new(0),
|
inhabited_time: AtomicU64::new(0),
|
||||||
|
custom_data: Mutex::new(NbtCompound::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
*self = Self::Level(Arc::new(chunk));
|
*self = Self::Level(Arc::new(chunk));
|
||||||
|
|||||||
@ -381,6 +381,9 @@ impl Cache {
|
|||||||
.set_structure_starts(noise_gen);
|
.set_structure_starts(noise_gen);
|
||||||
}
|
}
|
||||||
generator::WorldGenerator::Flat(_) => {}
|
generator::WorldGenerator::Flat(_) => {}
|
||||||
|
generator::WorldGenerator::Custom(custom_gen) => {
|
||||||
|
custom_gen.set_structure_starts(self.chunks[index].get_proto_chunk_mut());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
StagedChunkEnum::StructureReferences => match generator {
|
StagedChunkEnum::StructureReferences => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -389,6 +392,9 @@ impl Cache {
|
|||||||
.set_structure_references(noise_gen);
|
.set_structure_references(noise_gen);
|
||||||
}
|
}
|
||||||
generator::WorldGenerator::Flat(_) => {}
|
generator::WorldGenerator::Flat(_) => {}
|
||||||
|
generator::WorldGenerator::Custom(custom_gen) => {
|
||||||
|
custom_gen.set_structure_references(self.chunks[index].get_proto_chunk_mut());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
StagedChunkEnum::Biomes => match generator {
|
StagedChunkEnum::Biomes => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -399,6 +405,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(flat_gen) => {
|
generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
flat_gen.step_to_biomes(self.chunks[index].get_proto_chunk_mut());
|
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),
|
chunks: Vec::with_capacity((size * size) as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn advance(
|
pub fn advance(
|
||||||
&mut self,
|
&mut self,
|
||||||
stage: StagedChunkEnum,
|
stage: StagedChunkEnum,
|
||||||
@ -435,6 +445,9 @@ impl Cache {
|
|||||||
.set_structure_starts(noise_gen);
|
.set_structure_starts(noise_gen);
|
||||||
}
|
}
|
||||||
generator::WorldGenerator::Flat(_) => {}
|
generator::WorldGenerator::Flat(_) => {}
|
||||||
|
generator::WorldGenerator::Custom(custom_gen) => {
|
||||||
|
custom_gen.set_structure_starts(self.chunks[mid].get_proto_chunk_mut());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
StagedChunkEnum::StructureReferences => match generator {
|
StagedChunkEnum::StructureReferences => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -443,6 +456,9 @@ impl Cache {
|
|||||||
.set_structure_references(noise_gen);
|
.set_structure_references(noise_gen);
|
||||||
}
|
}
|
||||||
generator::WorldGenerator::Flat(_) => {}
|
generator::WorldGenerator::Flat(_) => {}
|
||||||
|
generator::WorldGenerator::Custom(custom_gen) => {
|
||||||
|
custom_gen.set_structure_references(self.chunks[mid].get_proto_chunk_mut());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
StagedChunkEnum::Biomes => match generator {
|
StagedChunkEnum::Biomes => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -453,6 +469,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(flat_gen) => {
|
generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
flat_gen.step_to_biomes(self.chunks[mid].get_proto_chunk_mut());
|
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 {
|
StagedChunkEnum::Noise => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -463,6 +482,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(flat_gen) => {
|
generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
flat_gen.step_to_noise(self.chunks[mid].get_proto_chunk_mut());
|
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 {
|
StagedChunkEnum::Surface => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -473,6 +495,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(flat_gen) => {
|
generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
flat_gen.step_to_surface(self.chunks[mid].get_proto_chunk_mut());
|
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 {
|
StagedChunkEnum::Carvers => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -483,6 +508,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(flat_gen) => {
|
generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
flat_gen.step_to_carvers(self.chunks[mid].get_proto_chunk_mut());
|
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 {
|
StagedChunkEnum::Features => match generator {
|
||||||
generator::WorldGenerator::Noise(noise_gen) => {
|
generator::WorldGenerator::Noise(noise_gen) => {
|
||||||
@ -495,6 +523,9 @@ impl Cache {
|
|||||||
generator::WorldGenerator::Flat(_) => {
|
generator::WorldGenerator::Flat(_) => {
|
||||||
self.chunks[mid].get_proto_chunk_mut().stage = StagedChunkEnum::Features;
|
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 => {
|
StagedChunkEnum::Lighting => {
|
||||||
let mut engine = crate::lighting::LightEngine::new();
|
let mut engine = crate::lighting::LightEngine::new();
|
||||||
|
|||||||
@ -1430,8 +1430,9 @@ impl GenerationSchedule {
|
|||||||
let stage = node.stage;
|
let stage = node.stage;
|
||||||
let send_chunk = self.send_chunk.clone();
|
let send_chunk = self.send_chunk.clone();
|
||||||
let level = level.clone();
|
let level = level.clone();
|
||||||
let settings =
|
let settings = GenerationSettings::from_dimension(
|
||||||
GenerationSettings::from_dimension(level.world_gen.dimension());
|
level.world_gen.load().dimension(),
|
||||||
|
);
|
||||||
|
|
||||||
pool.spawn(move || {
|
pool.spawn(move || {
|
||||||
let result = crate::chunk_system::worker_logic::run_generation(
|
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
|
// 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
|
// Clear all lighting data
|
||||||
let section_count = proto.light.sky_light.len();
|
let section_count = proto.light.sky_light.len();
|
||||||
@ -144,7 +145,7 @@ pub async fn io_read_work(
|
|||||||
} else {
|
} else {
|
||||||
// Standard ProtoChunk handling for non-full chunks
|
// Standard ProtoChunk handling for non-full chunks
|
||||||
let val = RecvChunk::IO(Chunk::Proto(Box::new(
|
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() {
|
if send.send((pos, val)).is_err() {
|
||||||
break;
|
break;
|
||||||
@ -158,7 +159,7 @@ pub async fn io_read_work(
|
|||||||
RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::new(
|
RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::new(
|
||||||
pos.x,
|
pos.x,
|
||||||
pos.y,
|
pos.y,
|
||||||
&level.world_gen,
|
&level.world_gen.load(),
|
||||||
)))),
|
)))),
|
||||||
))
|
))
|
||||||
.is_err()
|
.is_err()
|
||||||
@ -188,7 +189,7 @@ pub async fn io_write_work(recv: AsyncRx<Vec<(ChunkPos, Chunk)>>, level: Arc<Lev
|
|||||||
Chunk::Proto(chunk) => {
|
Chunk::Proto(chunk) => {
|
||||||
let mut temp = Chunk::Proto(chunk);
|
let mut temp = Chunk::Proto(chunk);
|
||||||
temp.upgrade_to_level_chunk(
|
temp.upgrade_to_level_chunk(
|
||||||
level.world_gen.dimension(),
|
level.world_gen.load().dimension(),
|
||||||
&level.lighting_config,
|
&level.lighting_config,
|
||||||
);
|
);
|
||||||
let Chunk::Level(chunk) = temp else { panic!() };
|
let Chunk::Level(chunk) = temp else { panic!() };
|
||||||
@ -250,7 +251,12 @@ pub fn run_generation(
|
|||||||
};
|
};
|
||||||
// Run generation with panic catching
|
// Run generation with panic catching
|
||||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
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
|
cache // Return cache on success
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -283,7 +289,7 @@ pub fn generation_work(
|
|||||||
send: &crossfire::compat::MTx<(ChunkPos, RecvChunk)>,
|
send: &crossfire::compat::MTx<(ChunkPos, RecvChunk)>,
|
||||||
level: &Arc<Level>,
|
level: &Arc<Level>,
|
||||||
) {
|
) {
|
||||||
let settings = GenerationSettings::from_dimension(level.world_gen.dimension());
|
let settings = GenerationSettings::from_dimension(level.world_gen.load().dimension());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let Ok((pos, cache, stage)) = recv.recv() else {
|
let Ok((pos, cache, stage)) = recv.recv() else {
|
||||||
|
|||||||
@ -18,6 +18,11 @@ pub trait GeneratorInit {
|
|||||||
use pumpkin_data::structures::{StructurePlacementCalculator, StructureSet};
|
use pumpkin_data::structures::{StructurePlacementCalculator, StructureSet};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::chunk_system::StagedChunkEnum;
|
||||||
|
use crate::generation::proto_chunk::ProtoChunk;
|
||||||
|
|
||||||
pub mod flat;
|
pub mod flat;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -26,35 +31,83 @@ pub struct FlatLayer {
|
|||||||
pub height: i32,
|
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 {
|
pub enum WorldGenerator {
|
||||||
Noise(Box<VanillaGenerator>),
|
Noise(Box<VanillaGenerator>),
|
||||||
Flat(flat::FlatGenerator),
|
Flat(flat::FlatGenerator),
|
||||||
|
Custom(Arc<dyn CustomChunkGenerator>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldGenerator {
|
impl WorldGenerator {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn dimension(&self) -> &Dimension {
|
pub fn dimension(&self) -> &Dimension {
|
||||||
match self {
|
match self {
|
||||||
Self::Noise(noise_gen) => &noise_gen.dimension,
|
Self::Noise(noise_gen) => &noise_gen.dimension,
|
||||||
Self::Flat(flat_gen) => &flat_gen.dimension,
|
Self::Flat(flat_gen) => &flat_gen.dimension,
|
||||||
|
Self::Custom(custom_gen) => custom_gen.dimension(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn seed(&self) -> u64 {
|
pub fn seed(&self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
Self::Noise(noise_gen) => noise_gen.random_config.seed,
|
Self::Noise(noise_gen) => noise_gen.random_config.seed,
|
||||||
Self::Flat(flat_gen) => flat_gen.seed,
|
Self::Flat(flat_gen) => flat_gen.seed,
|
||||||
|
Self::Custom(custom_gen) => custom_gen.seed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn global_structure_cache(
|
pub fn global_structure_cache(
|
||||||
&self,
|
&self,
|
||||||
) -> Option<&crate::generation::structure::placement::GlobalStructureCache> {
|
) -> Option<&crate::generation::structure::placement::GlobalStructureCache> {
|
||||||
match self {
|
match self {
|
||||||
Self::Noise(noise_gen) => Some(&noise_gen.global_structure_cache),
|
Self::Noise(noise_gen) => Some(&noise_gen.global_structure_cache),
|
||||||
Self::Flat(_) => None,
|
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);
|
.trim_height(bottom_y, (dimension.min_y + dimension.height) as u16);
|
||||||
(shape.height, shape.min_y)
|
(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 {
|
let default_block = match generator {
|
||||||
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.default_block,
|
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.default_block,
|
||||||
super::generator::WorldGenerator::Flat(_) => Block::AIR.default_state,
|
super::generator::WorldGenerator::Flat(_) => Block::AIR.default_state,
|
||||||
|
super::generator::WorldGenerator::Custom(custom_gen) => custom_gen.default_block(),
|
||||||
};
|
};
|
||||||
let biome_mixer_seed = match generator {
|
let biome_mixer_seed = match generator {
|
||||||
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.biome_mixer_seed,
|
super::generator::WorldGenerator::Noise(noise_gen) => noise_gen.biome_mixer_seed,
|
||||||
super::generator::WorldGenerator::Flat(flat_gen) => {
|
super::generator::WorldGenerator::Flat(flat_gen) => {
|
||||||
crate::biome::hash_seed(flat_gen.seed)
|
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];
|
let default_heightmap = [i16::MIN; CHUNK_AREA];
|
||||||
|
|||||||
@ -89,7 +89,7 @@ pub struct Level {
|
|||||||
pub chunk_saver: Arc<ChunkSaver>,
|
pub chunk_saver: Arc<ChunkSaver>,
|
||||||
entity_saver: Arc<EntitySaver>,
|
entity_saver: Arc<EntitySaver>,
|
||||||
|
|
||||||
pub world_gen: Arc<WorldGenerator>,
|
pub world_gen: ArcSwap<WorldGenerator>,
|
||||||
|
|
||||||
/// Handles runtime lighting updates
|
/// Handles runtime lighting updates
|
||||||
pub light_engine: DynamicLightEngine,
|
pub light_engine: DynamicLightEngine,
|
||||||
@ -251,7 +251,7 @@ impl Level {
|
|||||||
let level_ref = Arc::new(Self {
|
let level_ref = Arc::new(Self {
|
||||||
seed,
|
seed,
|
||||||
world_portal: ArcSwap::new(Arc::new(None)),
|
world_portal: ArcSwap::new(Arc::new(None)),
|
||||||
world_gen,
|
world_gen: ArcSwap::new(world_gen),
|
||||||
level_folder,
|
level_folder,
|
||||||
lighting_config: level_config.lighting,
|
lighting_config: level_config.lighting,
|
||||||
light_engine: DynamicLightEngine::new(),
|
light_engine: DynamicLightEngine::new(),
|
||||||
@ -302,6 +302,15 @@ impl Level {
|
|||||||
level_ref
|
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>) {
|
pub fn spawn_entity_generation(self: &Arc<Self>, pos: Vector2<i32>) {
|
||||||
let level = self.clone();
|
let level = self.clone();
|
||||||
if let Some(pool) = &self.gen_pool {
|
if let Some(pool) = &self.gen_pool {
|
||||||
|
|||||||
@ -11,16 +11,20 @@ use pumpkin_data::block_properties::{BlockProperties, LadderLikeProperties};
|
|||||||
use pumpkin_data::translation;
|
use pumpkin_data::translation;
|
||||||
use pumpkin_inventory::{
|
use pumpkin_inventory::{
|
||||||
generic_container_screen_handler::create_generic_9x3,
|
generic_container_screen_handler::create_generic_9x3,
|
||||||
|
player::ender_chest_inventory::EnderChestInventory,
|
||||||
player::player_inventory::PlayerInventory,
|
player::player_inventory::PlayerInventory,
|
||||||
screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler},
|
screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler},
|
||||||
};
|
};
|
||||||
use pumpkin_macros::pumpkin_block;
|
use pumpkin_macros::pumpkin_block;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
use pumpkin_world::inventory::Inventory;
|
use pumpkin_world::block::viewer::ViewerCountTracker;
|
||||||
use tokio::sync::Mutex;
|
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 {
|
impl ScreenHandlerFactory for EnderChestScreenFactory {
|
||||||
fn create_screen_handler<'a>(
|
fn create_screen_handler<'a>(
|
||||||
@ -30,7 +34,11 @@ impl ScreenHandlerFactory for EnderChestScreenFactory {
|
|||||||
_player: &'a dyn InventoryPlayer,
|
_player: &'a dyn InventoryPlayer,
|
||||||
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
|
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
|
||||||
Box::pin(async move {
|
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));
|
let concrete_arc = Arc::new(Mutex::new(handler));
|
||||||
|
|
||||||
Some(concrete_arc as SharedScreenHandler)
|
Some(concrete_arc as SharedScreenHandler)
|
||||||
@ -58,6 +66,7 @@ impl BlockBehaviour for EnderChestBlock {
|
|||||||
.entity
|
.entity
|
||||||
.get_horizontal_facing()
|
.get_horizontal_facing()
|
||||||
.opposite();
|
.opposite();
|
||||||
|
props.waterlogged = args.replacing.water_source();
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -78,13 +87,19 @@ impl BlockBehaviour for EnderChestBlock {
|
|||||||
return BlockActionResult::Success;
|
return BlockActionResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(block_entity) = args.world.get_block_entity(args.position)
|
let block_entity = if let Some(be) = args.world.get_block_entity(args.position) {
|
||||||
&& let Some(block_entity) = block_entity
|
be
|
||||||
.as_any()
|
} else {
|
||||||
.downcast_ref::<EnderChestBlockEntity>()
|
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();
|
let inventory = args.player.ender_chest_inventory();
|
||||||
inventory.set_tracker(block_entity.get_tracker()).await;
|
|
||||||
args.player
|
args.player
|
||||||
.increment_stat(
|
.increment_stat(
|
||||||
pumpkin_data::statistic::StatisticCategory::Custom,
|
pumpkin_data::statistic::StatisticCategory::Custom,
|
||||||
@ -94,7 +109,10 @@ impl BlockBehaviour for EnderChestBlock {
|
|||||||
.await;
|
.await;
|
||||||
args.player
|
args.player
|
||||||
.open_handled_screen(
|
.open_handled_screen(
|
||||||
&EnderChestScreenFactory(inventory.clone()),
|
&EnderChestScreenFactory {
|
||||||
|
inventory: inventory.clone(),
|
||||||
|
tracker: Some(block_entity.get_tracker()),
|
||||||
|
},
|
||||||
Some(*args.position),
|
Some(*args.position),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -200,7 +200,7 @@ impl CommandExecutor for PlaceJigsawExecutor {
|
|||||||
let (_piece_count, placer) = {
|
let (_piece_count, placer) = {
|
||||||
let seed = hash_block_pos(block_pos.0.x, block_pos.0.y, block_pos.0.z) as u64;
|
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 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 settings = GenerationSettings::from_dimension(world_gen.dimension());
|
||||||
let mut structure_context = StructureGeneratorContext {
|
let mut structure_context = StructureGeneratorContext {
|
||||||
seed: seed as i64,
|
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 seed = hash_block_pos(block_pos.0.x, block_pos.0.y, block_pos.0.z) as u64;
|
||||||
|
|
||||||
let (_piece_count, placer) = {
|
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());
|
let settings = GenerationSettings::from_dimension(world_gen.dimension());
|
||||||
|
|
||||||
if structure.structure_type == StructureType::Jigsaw {
|
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)
|
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 cx = block_pos.0.x >> 4;
|
||||||
let cz = block_pos.0.z >> 4;
|
let cz = block_pos.0.z >> 4;
|
||||||
let mut chunk = ProtoChunk::new(cx, cz, &world_gen);
|
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 {
|
fn get_recipe_id(recipe: &DynamicRecipe) -> String {
|
||||||
match recipe {
|
match recipe {
|
||||||
DynamicRecipe::Crafting(crafting) => match crafting {
|
DynamicRecipe::Crafting(crafting) => match crafting {
|
||||||
pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shaped { result, .. }
|
pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shaped {
|
||||||
| pumpkin_protocol::codec::recipe::OwnedCraftingRecipe::Shapeless { result, .. } => {
|
recipe_id,
|
||||||
result.item_id.clone()
|
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 {
|
DynamicRecipe::Cooking(cooking) => match cooking {
|
||||||
pumpkin_protocol::codec::recipe::OwnedCookingRecipeType::Smelting(r)
|
pumpkin_protocol::codec::recipe::OwnedCookingRecipeType::Smelting(r)
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
use std::sync::atomic::Ordering::Relaxed;
|
|
||||||
|
|
||||||
use pumpkin_data::translation;
|
use pumpkin_data::translation;
|
||||||
use pumpkin_util::PermissionLvl;
|
use pumpkin_util::PermissionLvl;
|
||||||
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
|
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
|
||||||
@ -40,27 +38,11 @@ impl CommandExecutor for SaveAllExecutor {
|
|||||||
|
|
||||||
let server = context.server();
|
let server = context.server();
|
||||||
|
|
||||||
if let Err(err) = server.player_data_storage.save_all_players(server).await {
|
if let Err(err) = server.save_all().await {
|
||||||
error!("Failed to save player data: {err}");
|
error!("Failed to save server data: {err}");
|
||||||
return Err(SAVE_FAILED_ERROR_TYPE.create_without_context());
|
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
|
context
|
||||||
.source
|
.source
|
||||||
.send_feedback(
|
.send_feedback(
|
||||||
|
|||||||
@ -48,6 +48,7 @@ pub struct DisplayEntity {
|
|||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub interpolation_start_delta_ticks: AtomicI32,
|
pub interpolation_start_delta_ticks: AtomicI32,
|
||||||
pub interpolation_duration: AtomicI32,
|
pub interpolation_duration: AtomicI32,
|
||||||
|
pub teleport_duration: AtomicI32,
|
||||||
pub view_range: Mutex<f32>,
|
pub view_range: Mutex<f32>,
|
||||||
pub shadow_radius: Mutex<f32>,
|
pub shadow_radius: Mutex<f32>,
|
||||||
pub shadow_strength: Mutex<f32>,
|
pub shadow_strength: Mutex<f32>,
|
||||||
@ -69,6 +70,7 @@ impl DisplayEntity {
|
|||||||
entity,
|
entity,
|
||||||
interpolation_start_delta_ticks: AtomicI32::new(0),
|
interpolation_start_delta_ticks: AtomicI32::new(0),
|
||||||
interpolation_duration: AtomicI32::new(0),
|
interpolation_duration: AtomicI32::new(0),
|
||||||
|
teleport_duration: AtomicI32::new(0),
|
||||||
view_range: Mutex::new(1.0),
|
view_range: Mutex::new(1.0),
|
||||||
shadow_radius: Mutex::new(0.0),
|
shadow_radius: Mutex::new(0.0),
|
||||||
shadow_strength: Mutex::new(1.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)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn init_display_data_tracker(&self) {
|
pub async fn init_display_data_tracker(&self) {
|
||||||
let view_range = *self.view_range.lock().await;
|
let view_range = *self.view_range.lock().await;
|
||||||
@ -210,6 +464,22 @@ impl DisplayEntity {
|
|||||||
)],
|
)],
|
||||||
None,
|
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) {
|
pub async fn write_display_nbt(&self, nbt: &mut NbtCompound) {
|
||||||
@ -367,6 +637,22 @@ impl BlockDisplayEntity {
|
|||||||
block_state: AtomicI32::new(0),
|
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 {
|
impl NBTStorage for BlockDisplayEntity {
|
||||||
@ -475,6 +761,38 @@ impl ItemDisplayEntity {
|
|||||||
item_display: AtomicU8::new(0),
|
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 {
|
impl NBTStorage for ItemDisplayEntity {
|
||||||
@ -619,6 +937,165 @@ impl TextDisplayEntity {
|
|||||||
flags: AtomicU8::new(0),
|
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 {
|
impl NBTStorage for TextDisplayEntity {
|
||||||
|
|||||||
@ -2271,7 +2271,7 @@ impl EntityBase for LivingEntity {
|
|||||||
let mut damage_event =
|
let mut damage_event =
|
||||||
crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new(
|
crate::plugin::api::events::entity::entity_damage::EntityDamageEvent::new(
|
||||||
self.entity.entity_id,
|
self.entity.entity_id,
|
||||||
damage_type.id.to_string(),
|
damage_type,
|
||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
if let Some(server) = self.entity.world.load().server.upgrade() {
|
if let Some(server) = self.entity.world.load().server.upgrade() {
|
||||||
|
|||||||
@ -892,6 +892,8 @@ pub struct Entity {
|
|||||||
pub last_sent_pos: AtomicCell<Vector3<f64>>,
|
pub last_sent_pos: AtomicCell<Vector3<f64>>,
|
||||||
/// Cache for the last sent head yaw byte
|
/// Cache for the last sent head yaw byte
|
||||||
pub last_sent_head_yaw: AtomicU8,
|
pub last_sent_head_yaw: AtomicU8,
|
||||||
|
/// Persistent custom data container for plugins (matching Bukkit's `PersistentDataHolder`)
|
||||||
|
pub custom_data: Mutex<NbtCompound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity {
|
impl Entity {
|
||||||
@ -1014,6 +1016,7 @@ impl Entity {
|
|||||||
last_sent_pitch: AtomicU8::new(0),
|
last_sent_pitch: AtomicU8::new(0),
|
||||||
last_sent_head_yaw: AtomicU8::new(0),
|
last_sent_head_yaw: AtomicU8::new(0),
|
||||||
last_sent_pos: AtomicCell::new(position),
|
last_sent_pos: AtomicCell::new(position),
|
||||||
|
custom_data: Mutex::new(NbtCompound::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3700,6 +3703,53 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
self.movement_multiplier.store(multiplier);
|
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 {
|
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...
|
// 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...
|
// 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]
|
#[must_use]
|
||||||
pub fn client_data(&self) -> Option<Arc<pumpkin_protocol::bedrock::server::login::ClientData>> {
|
pub fn client_data(&self) -> Option<Arc<pumpkin_protocol::bedrock::server::login::ClientData>> {
|
||||||
if let ClientPlatform::Bedrock(client) = self.0.client.as_ref() {
|
if let ClientPlatform::Bedrock(client) = self.0.client.as_ref() {
|
||||||
@ -1193,8 +1197,39 @@ impl Player {
|
|||||||
&self.ender_chest_inventory
|
&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`].
|
/// Removes the [`Player`] out of the current [`World`].
|
||||||
pub async fn remove(self: &Arc<Self>) {
|
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();
|
let vehicle = self.living_entity.entity.vehicle.lock().await.clone();
|
||||||
if let Some(vehicle) = vehicle {
|
if let Some(vehicle) = vehicle {
|
||||||
self.root_vehicle_uuid
|
self.root_vehicle_uuid
|
||||||
@ -2782,6 +2817,25 @@ impl Player {
|
|||||||
self.stats.lock().await.set(category, stat, value);
|
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 {
|
pub async fn get_movement_statistic(&self) -> statistics::CustomStatistic {
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
if entity.has_vehicle().await {
|
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) {
|
pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) {
|
||||||
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
|
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
|
||||||
self.compass_target.store(Some(pos));
|
self.compass_target.store(Some(pos));
|
||||||
@ -5710,6 +5776,7 @@ impl NBTStorage for EnderChestInventory {
|
|||||||
for tag in item_list {
|
for tag in item_list {
|
||||||
if let Some(item_compound) = tag.extract_compound()
|
if let Some(item_compound) = tag.extract_compound()
|
||||||
&& let Some(slot_byte) = item_compound.get_byte("Slot")
|
&& let Some(slot_byte) = item_compound.get_byte("Slot")
|
||||||
|
&& (0..Self::INVENTORY_SIZE as i8).contains(&slot_byte)
|
||||||
{
|
{
|
||||||
let slot = slot_byte as usize;
|
let slot = slot_byte as usize;
|
||||||
if let Some(item_stack) = ItemStack::read_item_stack(item_compound) {
|
if let Some(item_stack) = ItemStack::read_item_stack(item_compound) {
|
||||||
|
|||||||
@ -432,7 +432,18 @@ impl PlayerAdvancement {
|
|||||||
result = true;
|
result = true;
|
||||||
self.progress_changed.insert(advancement);
|
self.progress_changed.insert(advancement);
|
||||||
if !was_done && progress.is_done() {
|
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);
|
Self::grant_reward(player.clone(), advancement.reward);
|
||||||
if let Some(display) = advancement.display
|
if let Some(display) = advancement.display
|
||||||
&& display.announce_to_chat
|
&& display.announce_to_chat
|
||||||
|
|||||||
@ -147,7 +147,7 @@ impl ItemBehaviour for EnderEyeItem {
|
|||||||
|
|
||||||
fn find_stronghold(world: &Arc<World>, origin: BlockPos) -> Option<BlockPos> {
|
fn find_stronghold(world: &Arc<World>, origin: BlockPos) -> Option<BlockPos> {
|
||||||
let level = &world.level;
|
let level = &world.level;
|
||||||
let generator = &level.world_gen;
|
let generator = level.world_gen();
|
||||||
let seed = level.seed.0;
|
let seed = level.seed.0;
|
||||||
|
|
||||||
let global_cache = generator.global_structure_cache()?;
|
let global_cache = generator.global_structure_cache()?;
|
||||||
|
|||||||
@ -386,13 +386,28 @@ impl PumpkinServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_plugins(&self) -> std::time::Duration {
|
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,
|
Ok(duration) => duration,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("{err}");
|
error!("{err}");
|
||||||
std::time::Duration::ZERO
|
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) {
|
pub async fn unload_plugins(&self) {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use pumpkin_data::damage::DamageType;
|
||||||
use pumpkin_macros::{Event, cancellable};
|
use pumpkin_macros::{Event, cancellable};
|
||||||
|
|
||||||
/// An event that occurs when an entity takes damage.
|
/// An event that occurs when an entity takes damage.
|
||||||
@ -10,17 +11,17 @@ pub struct EntityDamageEvent {
|
|||||||
/// The amount of damage taken.
|
/// The amount of damage taken.
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
|
|
||||||
/// The cause or type of damage as a string.
|
/// The damage type.
|
||||||
pub cause: String,
|
pub damage_type: DamageType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EntityDamageEvent {
|
impl EntityDamageEvent {
|
||||||
#[must_use]
|
#[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 {
|
Self {
|
||||||
entity_id,
|
entity_id,
|
||||||
damage: damage_amount,
|
damage: damage_amount,
|
||||||
cause: damage_type,
|
damage_type,
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,17 @@ pub struct PlayerAdvancementDoneEvent {
|
|||||||
pub advancement_id: String,
|
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 {
|
impl PlayerEvent for PlayerAdvancementDoneEvent {
|
||||||
fn get_player(&self) -> &Arc<Player> {
|
fn get_player(&self) -> &Arc<Player> {
|
||||||
&self.player
|
&self.player
|
||||||
|
|||||||
@ -60,6 +60,7 @@ impl PluginRuntime {
|
|||||||
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, PluginInitError> {
|
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, PluginInitError> {
|
||||||
let mut config = wasmtime::Config::new();
|
let mut config = wasmtime::Config::new();
|
||||||
config.wasm_component_model(true);
|
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");
|
let mut path = std::path::absolute(path.as_ref()).expect("Failed to get absolute path");
|
||||||
path.pop();
|
path.pop();
|
||||||
path.push("cache");
|
path.push("cache");
|
||||||
@ -119,6 +120,8 @@ fn setup_linker(engine: &Engine) -> wasmtime::Result<Linker<PluginHostState>> {
|
|||||||
let mut linker = Linker::<PluginHostState>::new(engine);
|
let mut linker = Linker::<PluginHostState>::new(engine);
|
||||||
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
|
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
|
||||||
wasmtime_wasi_http::p2::add_only_http_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)?;
|
wit::v0_1::add_to_linker(&mut linker)?;
|
||||||
Ok(linker)
|
Ok(linker)
|
||||||
}
|
}
|
||||||
@ -155,6 +158,7 @@ fn load_component(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WasmPlugin {
|
impl WasmPlugin {
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn on_load(
|
pub async fn on_load(
|
||||||
&self,
|
&self,
|
||||||
context: Arc<Context>,
|
context: Arc<Context>,
|
||||||
@ -167,12 +171,18 @@ impl WasmPlugin {
|
|||||||
builder.inherit_stderr();
|
builder.inherit_stderr();
|
||||||
|
|
||||||
let metadata = context.get_metadata();
|
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
|
let filtered_permissions: Vec<String> = metadata
|
||||||
.permissions
|
.permissions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| !blocked_permissions.iter().any(|blocked| blocked == *p))
|
.filter(|p| !is_blocked(p))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -191,7 +201,10 @@ impl WasmPlugin {
|
|||||||
let udp_outgoing_datagram =
|
let udp_outgoing_datagram =
|
||||||
udp_allowed || has_permission(permissions::NETWORK_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_tcp(tcp_connect || tcp_bind);
|
||||||
builder.allow_udp(udp_connect || udp_bind);
|
builder.allow_udp(udp_connect || udp_bind);
|
||||||
@ -218,10 +231,10 @@ impl WasmPlugin {
|
|||||||
builder.inherit_network();
|
builder.inherit_network();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- System Permissions ---
|
// --- System Permissions & Environment Variables ---
|
||||||
|
|
||||||
// Environment Variables
|
// Environment Variables
|
||||||
if has_permission(permissions::SYS_ENV) {
|
if plugin_config.inherit_env || has_permission(permissions::SYS_ENV) {
|
||||||
builder.inherit_env();
|
builder.inherit_env();
|
||||||
} else {
|
} else {
|
||||||
for (key, value) in std::env::vars() {
|
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(
|
builder.preopened_dir(
|
||||||
context.get_data_folder(),
|
context.get_data_folder(),
|
||||||
"data",
|
"data",
|
||||||
@ -257,8 +277,15 @@ impl WasmPlugin {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if has_permission(permissions::HTTP_OUTBOUND) {
|
let max_memory_mb = plugin_override
|
||||||
store.data_mut().wasi_http_hooks.allow_outbound = true;
|
.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;
|
store.data_mut().permissions = filtered_permissions;
|
||||||
@ -270,8 +297,16 @@ impl WasmPlugin {
|
|||||||
|
|
||||||
match self.plugin_instance {
|
match self.plugin_instance {
|
||||||
PluginInstance::V0_1(ref plugin) => {
|
PluginInstance::V0_1(ref plugin) => {
|
||||||
let context = store.data_mut().add_context(context)?;
|
let context_res = store.data_mut().add_context(context)?;
|
||||||
plugin.call_on_load(&mut *store, context).await
|
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 {
|
match self.plugin_instance {
|
||||||
PluginInstance::V0_1(ref plugin) => {
|
PluginInstance::V0_1(ref plugin) => {
|
||||||
let context = store.data_mut().add_context(context)?;
|
let context_res = store.data_mut().add_context(context)?;
|
||||||
plugin.call_on_unload(&mut *store, context).await
|
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.
|
/// 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 public_key = fetch_market_public_key().unwrap_or_default();
|
||||||
let result = verify_pumpkin_wasm(wasm_bytes, &public_key);
|
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.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)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -65,6 +65,8 @@ pub type ConsumedArgsResource = WasmResource<OwnedConsumedArgs>;
|
|||||||
pub type CommandNodeResource = WasmResource<NonLeafNodeBuilder>;
|
pub type CommandNodeResource = WasmResource<NonLeafNodeBuilder>;
|
||||||
pub type ItemStackResource = WasmResource<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>>;
|
pub type ItemStackResource = WasmResource<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>>;
|
||||||
pub type RecipeManagerResource = WasmResource<Arc<RecipeManager>>;
|
pub type RecipeManagerResource = WasmResource<Arc<RecipeManager>>;
|
||||||
|
pub type EnchantmentManagerResource =
|
||||||
|
WasmResource<Arc<crate::server::enchantment::EnchantmentManager>>;
|
||||||
pub type OpManagerResource = WasmResource<Arc<Server>>;
|
pub type OpManagerResource = WasmResource<Arc<Server>>;
|
||||||
pub type BanManagerResource = WasmResource<Arc<Server>>;
|
pub type BanManagerResource = WasmResource<Arc<Server>>;
|
||||||
pub type WhitelistManagerResource = WasmResource<Arc<Server>>;
|
pub type WhitelistManagerResource = WasmResource<Arc<Server>>;
|
||||||
@ -78,6 +80,28 @@ pub struct ContainerBlockEntity {
|
|||||||
|
|
||||||
pub type ContainerBlockEntityResource = WasmResource<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 type OwnedConsumedArgs = HashMap<String, OwnedArg>;
|
||||||
|
|
||||||
pub struct PluginHostState {
|
pub struct PluginHostState {
|
||||||
@ -85,6 +109,7 @@ pub struct PluginHostState {
|
|||||||
pub wasi_http_ctx: WasiHttpCtx,
|
pub wasi_http_ctx: WasiHttpCtx,
|
||||||
pub wasi_http_hooks: PluginHttpHooks,
|
pub wasi_http_hooks: PluginHttpHooks,
|
||||||
pub resource_table: ResourceTable,
|
pub resource_table: ResourceTable,
|
||||||
|
pub limits: wasmtime::StoreLimits,
|
||||||
pub plugin: Option<Weak<WasmPlugin>>,
|
pub plugin: Option<Weak<WasmPlugin>>,
|
||||||
pub server: Option<Arc<Server>>,
|
pub server: Option<Arc<Server>>,
|
||||||
pub permissions: Vec<String>,
|
pub permissions: Vec<String>,
|
||||||
@ -109,6 +134,7 @@ impl PluginHostState {
|
|||||||
wasi_http_ctx: WasiHttpCtx::new(),
|
wasi_http_ctx: WasiHttpCtx::new(),
|
||||||
wasi_http_hooks: PluginHttpHooks::new(),
|
wasi_http_hooks: PluginHttpHooks::new(),
|
||||||
resource_table,
|
resource_table,
|
||||||
|
limits: wasmtime::StoreLimitsBuilder::new().build(),
|
||||||
plugin: None,
|
plugin: None,
|
||||||
server: None,
|
server: None,
|
||||||
permissions: Vec::new(),
|
permissions: Vec::new(),
|
||||||
@ -297,6 +323,16 @@ impl PluginHostState {
|
|||||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
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>(
|
pub fn add_op_manager<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider: Arc<Server>,
|
provider: Arc<Server>,
|
||||||
@ -344,6 +380,118 @@ impl PluginHostState {
|
|||||||
})?;
|
})?;
|
||||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
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 {
|
pub struct PluginHttpHooks {
|
||||||
@ -379,6 +527,8 @@ impl WasiHttpHooks for PluginHttpHooks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl wasmtime_wasi_http::p3::WasiHttpHooks for PluginHttpHooks {}
|
||||||
|
|
||||||
impl WasiView for PluginHostState {
|
impl WasiView for PluginHostState {
|
||||||
fn ctx(&mut self) -> WasiCtxView<'_> {
|
fn ctx(&mut self) -> WasiCtxView<'_> {
|
||||||
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(())
|
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<()> {
|
async fn drop(&mut self, rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
||||||
let _ = self
|
let _ = self
|
||||||
.resource_table
|
.resource_table
|
||||||
|
|||||||
@ -43,6 +43,10 @@ impl CommandExecutor for WasmCommandExecutor {
|
|||||||
.add_consumed_args(args)
|
.add_consumed_args(args)
|
||||||
.expect("valid consumed 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 {
|
match self.plugin.plugin_instance {
|
||||||
PluginInstance::V0_1(ref plugin) => {
|
PluginInstance::V0_1(ref plugin) => {
|
||||||
let result = plugin
|
let result = plugin
|
||||||
@ -53,15 +57,35 @@ impl CommandExecutor for WasmCommandExecutor {
|
|||||||
server_resource,
|
server_resource,
|
||||||
args_resource,
|
args_resource,
|
||||||
)
|
)
|
||||||
.await
|
.await;
|
||||||
.map_err(|e| {
|
|
||||||
CommandError::CommandFailed(
|
let _ = store
|
||||||
TextComponent::text(format!(
|
.data_mut()
|
||||||
"Wasm command failed with following error: {e}"
|
.resource_table
|
||||||
))
|
.delete::<crate::plugin::loader::wasm::wasm_host::state::CommandSenderResource>(
|
||||||
.color(Color::Named(NamedColor::Red)),
|
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 {
|
match result {
|
||||||
Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
|
|||||||
@ -1,3 +1,94 @@
|
|||||||
use crate::plugin::loader::wasm::wasm_host::{state::PluginHostState, wit::v0_1::pumpkin};
|
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 {}
|
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,
|
Attribute, AttributeModifier as WitAttributeModifier,
|
||||||
ModifierOperation as WitModifierOperation,
|
ModifierOperation as WitModifierOperation,
|
||||||
},
|
},
|
||||||
common::{EntityPose, Position},
|
common::{EntityPose, NbtTree as WitNbtTree, Position},
|
||||||
|
damage_types::DamageType as WitDamageType,
|
||||||
entity::Host,
|
entity::Host,
|
||||||
entity_types,
|
entity_types,
|
||||||
item_stack::ItemStack as WitHostItemStack,
|
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 {
|
impl HostEntity for PluginHostState {
|
||||||
async fn get_id(&mut self, entity: Resource<Entity>) -> wasmtime::Result<u32> {
|
async fn get_id(&mut self, entity: Resource<Entity>) -> wasmtime::Result<u32> {
|
||||||
let entity = entity_from_resource(self, &entity)?;
|
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))
|
.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)?;
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
entity
|
entity
|
||||||
.damage(&*entity, amount, pumpkin_data::damage::DamageType::GENERIC)
|
.damage(&*entity, amount, from_wit_damage_type(damage_type))
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
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<()> {
|
async fn drop(&mut self, rep: Resource<Entity>) -> wasmtime::Result<()> {
|
||||||
let _ = self
|
let _ = self
|
||||||
.resource_table
|
.resource_table
|
||||||
@ -1328,9 +1395,17 @@ impl Goal for CustomWasmGoal {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
|
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;
|
return false;
|
||||||
};
|
};
|
||||||
plugin
|
let server_rep = server_res.rep();
|
||||||
|
let entity_rep = entity_res.rep();
|
||||||
|
let result = plugin
|
||||||
.call_handle_ai_goal_can_start(
|
.call_handle_ai_goal_can_start(
|
||||||
&mut *store,
|
&mut *store,
|
||||||
self.goal_id,
|
self.goal_id,
|
||||||
@ -1338,7 +1413,20 @@ impl Goal for CustomWasmGoal {
|
|||||||
entity_res,
|
entity_res,
|
||||||
)
|
)
|
||||||
.await
|
.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 {
|
} else {
|
||||||
@ -1360,9 +1448,17 @@ impl Goal for CustomWasmGoal {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
|
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;
|
return false;
|
||||||
};
|
};
|
||||||
plugin
|
let server_rep = server_res.rep();
|
||||||
|
let entity_rep = entity_res.rep();
|
||||||
|
let result = plugin
|
||||||
.call_handle_ai_goal_should_continue(
|
.call_handle_ai_goal_should_continue(
|
||||||
&mut *store,
|
&mut *store,
|
||||||
self.goal_id,
|
self.goal_id,
|
||||||
@ -1370,7 +1466,20 @@ impl Goal for CustomWasmGoal {
|
|||||||
entity_res,
|
entity_res,
|
||||||
)
|
)
|
||||||
.await
|
.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 {
|
} else {
|
||||||
@ -1392,8 +1501,16 @@ impl Goal for CustomWasmGoal {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
let server_rep = server_res.rep();
|
||||||
|
let entity_rep = entity_res.rep();
|
||||||
let _ = plugin
|
let _ = plugin
|
||||||
.call_handle_ai_goal_start(
|
.call_handle_ai_goal_start(
|
||||||
&mut *store,
|
&mut *store,
|
||||||
@ -1402,6 +1519,18 @@ impl Goal for CustomWasmGoal {
|
|||||||
entity_res,
|
entity_res,
|
||||||
)
|
)
|
||||||
.await;
|
.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;
|
return;
|
||||||
};
|
};
|
||||||
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
let server_rep = server_res.rep();
|
||||||
|
let entity_rep = entity_res.rep();
|
||||||
let _ = plugin
|
let _ = plugin
|
||||||
.call_handle_ai_goal_tick(
|
.call_handle_ai_goal_tick(
|
||||||
&mut *store,
|
&mut *store,
|
||||||
@ -1431,6 +1568,18 @@ impl Goal for CustomWasmGoal {
|
|||||||
entity_res,
|
entity_res,
|
||||||
)
|
)
|
||||||
.await;
|
.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;
|
return;
|
||||||
};
|
};
|
||||||
let Ok(entity_res) = store.data_mut().add_entity(entity_arc) else {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
let server_rep = server_res.rep();
|
||||||
|
let entity_rep = entity_res.rep();
|
||||||
let _ = plugin
|
let _ = plugin
|
||||||
.call_handle_ai_goal_stop(
|
.call_handle_ai_goal_stop(
|
||||||
&mut *store,
|
&mut *store,
|
||||||
@ -1460,6 +1617,18 @@ impl Goal for CustomWasmGoal {
|
|||||||
entity_res,
|
entity_res,
|
||||||
)
|
)
|
||||||
.await;
|
.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,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
events::{
|
events::{
|
||||||
ToFromWasmEvent, consume_player, consume_world, from_wasm_block_name,
|
ToFromWasmEvent, cleanup_event, consume_player, consume_world,
|
||||||
from_wasm_block_position, to_wasm_block_name, to_wasm_block_position,
|
from_wasm_block_name, from_wasm_block_position, to_wasm_block_name,
|
||||||
|
to_wasm_block_position,
|
||||||
},
|
},
|
||||||
pumpkin::plugin::event::{
|
pumpkin::plugin::event::{
|
||||||
BellResonateEventData, BellRingEventData, BlockBreakEventData, BlockBrushEventData,
|
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 {
|
if let Event::BellResonateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BellRingEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockBrushEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockCookEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockDispenseArmorEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockDispenseLootEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockDropItemEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockExpEvent(data) = event {
|
||||||
self.exp = data.exp;
|
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 {
|
if let Event::BlockFertilizeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockMultiPlaceEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockReceiveGameEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockShearEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BlockSpreadEvent(data) = event {
|
||||||
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::BrewingStartEvent(data) = event {
|
||||||
self.brewing_time = data.brewing_time;
|
self.brewing_time = data.brewing_time;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::CampfireStartEvent(data) = event {
|
||||||
self.cooking_time = data.cooking_time;
|
self.cooking_time = data.cooking_time;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::CauldronLevelChangeEvent(data) = event {
|
||||||
self.new_level = data.new_level;
|
self.new_level = data.new_level;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::CrafterCraftEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityBlockFormEvent(data) = event {
|
||||||
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::FluidLevelChangeEvent(data) = event {
|
||||||
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
self.new_state_id = BlockStateId::new_or_air(data.new_state_id);
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::LeavesDecayEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::MoistureChangeEvent(data) = event {
|
||||||
self.new_moisture = data.new_moisture;
|
self.new_moisture = data.new_moisture;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::SculkBloomEvent(data) = event {
|
||||||
self.charge = data.charge;
|
self.charge = data.charge;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VaultDisplayItemEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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::{
|
loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
|
entity::{from_wit_damage_type, to_wit_damage_type},
|
||||||
events::{
|
events::{
|
||||||
ToFromWasmEvent, consume_player, consume_text_component, consume_world,
|
ToFromWasmEvent, cleanup_event, consume_player, consume_text_component,
|
||||||
from_wasm_block_position, from_wasm_position, to_wasm_block_position,
|
consume_world, from_wasm_block_position, from_wasm_position,
|
||||||
to_wasm_position,
|
to_wasm_block_position, to_wasm_position,
|
||||||
},
|
},
|
||||||
pumpkin::plugin::event::{
|
pumpkin::plugin::event::{
|
||||||
AreaEffectCloudApplyEventData, ArrowBodyCountChangeEventData,
|
AreaEffectCloudApplyEventData, ArrowBodyCountChangeEventData,
|
||||||
@ -112,7 +113,7 @@ impl ToFromWasmEvent for EntityDamageEvent {
|
|||||||
Event::EntityDamageEvent(EntityDamageEventData {
|
Event::EntityDamageEvent(EntityDamageEventData {
|
||||||
entity_id: self.entity_id,
|
entity_id: self.entity_id,
|
||||||
damage: self.damage,
|
damage: self.damage,
|
||||||
cause: self.cause.clone(),
|
damage_type: to_wit_damage_type(&self.damage_type),
|
||||||
cancelled: self.cancelled,
|
cancelled: self.cancelled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -122,7 +123,7 @@ impl ToFromWasmEvent for EntityDamageEvent {
|
|||||||
Event::EntityDamageEvent(data) => Self {
|
Event::EntityDamageEvent(data) => Self {
|
||||||
entity_id: data.entity_id,
|
entity_id: data.entity_id,
|
||||||
damage: data.damage,
|
damage: data.damage,
|
||||||
cause: data.cause,
|
damage_type: from_wit_damage_type(data.damage_type),
|
||||||
cancelled: data.cancelled,
|
cancelled: data.cancelled,
|
||||||
},
|
},
|
||||||
_ => panic!("unexpected event type"),
|
_ => 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 {
|
if let Event::CreatureSpawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EnderDragonChangePhaseEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_phase = data.new_phase;
|
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 {
|
if let Event::EntityBreakDoorEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityChangeBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_block = data.new_block;
|
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 {
|
if let Event::EntityDamageByBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.damage = data.damage;
|
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 {
|
if let Event::EntityDamageByEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.damage = data.damage;
|
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 {
|
if let Event::EntityDropItemEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityEnterBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityExhaustionEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.exhaustion = data.exhaustion;
|
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 {
|
if let Event::EntityInteractEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityKnockbackEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.knockback = from_wasm_position(data.knockback);
|
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 {
|
if let Event::EntityPlaceEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityPoseChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.pose = data.pose;
|
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 {
|
if let Event::EntityPotionEffectEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.duration = data.duration;
|
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 {
|
if let Event::EntitySpellCastEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.spell = data.spell;
|
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 {
|
if let Event::EntityTargetLivingEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.target_id = data.target_id;
|
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 {
|
if let Event::EntityToggleSwimEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.is_swimming = data.is_swimming;
|
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 {
|
if let Event::ExplosionPrimeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.radius = data.radius;
|
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 {
|
if let Event::FireworkExplodeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::FoodLevelChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.food_level = data.food_level;
|
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 {
|
if let Event::ItemDespawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ItemMergeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ItemSpawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PiglinBarterEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ProjectileHitEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ProjectileLaunchEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::SheepDyeWoolEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.dye_color = data.dye_color;
|
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 {
|
if let Event::SheepRegrowWoolEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::SlimeSplitEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.count = data.count;
|
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 {
|
if let Event::StriderTemperatureChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.is_shivering = data.is_shivering;
|
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 {
|
if let Event::VillagerAcquireTradeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.recipe_index = data.recipe_index;
|
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 {
|
if let Event::VillagerCareerChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.profession = data.profession;
|
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 {
|
if let Event::VillagerReplenishTradeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.restock_quantity = data.restock_quantity;
|
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 {
|
if let Event::WardenAngerChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_anger = data.new_anger;
|
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 {
|
if let Event::AreaEffectCloudApplyEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ArrowBodyCountChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_amount = data.new_amount;
|
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 {
|
if let Event::BatToggleSleepEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::CreeperPowerEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityCombustByBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.duration = data.duration;
|
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 {
|
if let Event::EntityCombustByEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.duration = data.duration;
|
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 {
|
if let Event::EntityKnockbackByEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.force = data.force;
|
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 {
|
if let Event::EntityPortalEnterEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityPortalExitEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.to_pos = data.to_pos.map(from_wasm_block_position);
|
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 {
|
if let Event::EntityRemoveEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.cause = data.cause;
|
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 {
|
if let Event::EntityTargetBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntityUnleashEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.reason = data.reason;
|
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 {
|
if let Event::ExpBottleEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.experience = data.experience;
|
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 {
|
if let Event::HorseJumpEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.power = data.power;
|
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 {
|
if let Event::LingeringPotionSplashEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PigZapEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PigZombieAngerEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_anger = data.new_anger;
|
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 {
|
if let Event::PotionSplashEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::SpawnerSpawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::TrialSpawnerSpawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VillagerReputationChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.reputation_change = data.reputation_change;
|
self.reputation_change = data.reputation_change;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::plugin::{
|
|||||||
loader::wasm::wasm_host::{
|
loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
events::{ToFromWasmEvent, to_wasm_block_position},
|
events::{ToFromWasmEvent, cleanup_event, to_wasm_block_position},
|
||||||
pumpkin::plugin::event::{
|
pumpkin::plugin::event::{
|
||||||
Event, HangingBreakByEntityEventData, HangingBreakEventData, HangingPlaceEventData,
|
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 {
|
if let Event::HangingBreakEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::HangingBreakByEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::HangingPlaceEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod cleanup;
|
||||||
pub mod enchantment;
|
pub mod enchantment;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod hanging;
|
pub mod hanging;
|
||||||
@ -34,6 +35,8 @@ pub mod server;
|
|||||||
pub mod vehicle;
|
pub mod vehicle;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
||||||
|
pub use cleanup::*;
|
||||||
|
|
||||||
impl pumpkin::plugin::event::Host for PluginHostState {}
|
impl pumpkin::plugin::event::Host for PluginHostState {}
|
||||||
|
|
||||||
pub struct WasmPluginEventHandler {
|
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, ()> {
|
fn handle<'a>(&'a self, server: &'a Arc<Server>, event: &'a E) -> BoxFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
let mut store = self.plugin.store.lock().await;
|
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 {
|
match self.plugin.plugin_instance {
|
||||||
PluginInstance::V0_1(ref plugin) => {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
let _ = plugin
|
let server_rep = server_res.rep();
|
||||||
.call_handle_event(&mut *store, self.handler_id, server, &event)
|
let result = plugin
|
||||||
|
.call_handle_event(&mut *store, self.handler_id, server_res, &wasm_event)
|
||||||
.await;
|
.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());
|
let wasm_event = event.to_wasm_event(store.data_mut());
|
||||||
match self.plugin.plugin_instance {
|
match self.plugin.plugin_instance {
|
||||||
PluginInstance::V0_1(ref plugin) => {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
if let Ok(returned_event) = plugin
|
let server_rep = server_res.rep();
|
||||||
.call_handle_event(&mut *store, self.handler_id, server, &wasm_event)
|
let result = plugin
|
||||||
.await
|
.call_handle_event(&mut *store, self.handler_id, server_res, &wasm_event)
|
||||||
{
|
.await;
|
||||||
event.apply_wasm_event(returned_event, store.data_mut());
|
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,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
events::{
|
events::{
|
||||||
ToFromWasmEvent, consume_player, consume_text_component, consume_world,
|
ToFromWasmEvent, cleanup_event, consume_player, consume_text_component,
|
||||||
from_wasm_block_name, from_wasm_block_position, from_wasm_click_type,
|
consume_world, from_wasm_block_name, from_wasm_block_position,
|
||||||
from_wasm_entity_interaction_action, from_wasm_entity_type, from_wasm_game_mode,
|
from_wasm_click_type, from_wasm_entity_interaction_action, from_wasm_entity_type,
|
||||||
from_wasm_hand, from_wasm_position, to_wasm_block_position, to_wasm_click_type,
|
from_wasm_game_mode, from_wasm_hand, from_wasm_position, to_wasm_block_position,
|
||||||
to_wasm_entity_interaction_action, to_wasm_entity_type, to_wasm_game_mode,
|
to_wasm_click_type, to_wasm_entity_interaction_action, to_wasm_entity_type,
|
||||||
to_wasm_hand, to_wasm_position,
|
to_wasm_game_mode, to_wasm_hand, to_wasm_position,
|
||||||
},
|
},
|
||||||
|
gui::{from_wit_screen, to_wit_screen},
|
||||||
pumpkin::plugin::event::{
|
pumpkin::plugin::event::{
|
||||||
AsyncPlayerChatEventData, AsyncPlayerPreLoginEventData,
|
AsyncPlayerChatEventData, AsyncPlayerPreLoginEventData,
|
||||||
BedrockFormResponseEventData, CustomClickActionEventData, Event,
|
BedrockFormResponseEventData, CustomClickActionEventData, Event,
|
||||||
@ -159,7 +160,7 @@ impl ToFromWasmEvent for InventoryCloseEvent {
|
|||||||
|
|
||||||
Event::InventoryCloseEvent(InventoryCloseEventData {
|
Event::InventoryCloseEvent(InventoryCloseEventData {
|
||||||
player,
|
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 {
|
match event {
|
||||||
Event::InventoryCloseEvent(data) => Self {
|
Event::InventoryCloseEvent(data) => Self {
|
||||||
player: consume_player(state, &data.player),
|
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"),
|
_ => panic!("unexpected event type"),
|
||||||
}
|
}
|
||||||
@ -182,7 +183,7 @@ impl ToFromWasmEvent for InventoryClickEvent {
|
|||||||
|
|
||||||
Event::InventoryClickEvent(InventoryClickEventData {
|
Event::InventoryClickEvent(InventoryClickEventData {
|
||||||
player,
|
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),
|
click_type: to_wasm_click_type(self.click_type),
|
||||||
slot: self.slot,
|
slot: self.slot,
|
||||||
raw_slot: self.raw_slot,
|
raw_slot: self.raw_slot,
|
||||||
@ -205,7 +206,7 @@ impl ToFromWasmEvent for InventoryClickEvent {
|
|||||||
match event {
|
match event {
|
||||||
Event::InventoryClickEvent(data) => Self {
|
Event::InventoryClickEvent(data) => Self {
|
||||||
player: consume_player(state, &data.player),
|
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),
|
click_type: from_wasm_click_type(data.click_type),
|
||||||
slot: data.slot,
|
slot: data.slot,
|
||||||
raw_slot: data.raw_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) {
|
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
||||||
|
cleanup_event(&event, state);
|
||||||
if let Event::AsyncPlayerChatEvent(data) = event {
|
if let Event::AsyncPlayerChatEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.message = data.message;
|
self.message = data.message;
|
||||||
@ -1188,6 +1190,7 @@ impl ToFromWasmEvent for AsyncPlayerPreLoginEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
||||||
|
cleanup_event(&event, state);
|
||||||
if let Event::AsyncPlayerPreLoginEvent(data) = event {
|
if let Event::AsyncPlayerPreLoginEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.kick_message = consume_text_component(state, &data.kick_message);
|
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 {
|
if let Event::PlayerAdvancementDoneEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerAnimationEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerArmorStandManipulateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerBucketEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerChangedWorldEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerChannelEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerCommandPreprocessEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.command = data.command;
|
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 {
|
if let Event::PlayerEditBookEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.pages = data.pages;
|
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 {
|
if let Event::PlayerElytraBoostEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerExpCooldownChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_cooldown = data.new_cooldown;
|
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 {
|
if let Event::PlayerHarvestBlockEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerHideEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerItemMendEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.repair_amount = data.repair_amount;
|
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 {
|
if let Event::PlayerKickEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.reason = data.reason;
|
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 {
|
if let Event::PlayerLeashEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerLocaleChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
}
|
}
|
||||||
@ -1804,6 +1823,7 @@ impl ToFromWasmEvent for PlayerNameEntityEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
||||||
|
cleanup_event(&event, state);
|
||||||
if let Event::PlayerNameEntityEvent(data) = event {
|
if let Event::PlayerNameEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.name = consume_text_component(state, &data.name);
|
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 {
|
if let Event::PlayerOpenSignEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerPortalEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.to_pos = data.to_pos.map(from_wasm_block_position);
|
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) {
|
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
||||||
|
cleanup_event(&event, state);
|
||||||
if let Event::PlayerPreLoginEvent(data) = event {
|
if let Event::PlayerPreLoginEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.kick_message = consume_text_component(state, &data.kick_message);
|
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 {
|
if let Event::PlayerRiptideEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerShearEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerShowEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerSpawnChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.new_spawn = data.new_spawn.map(from_wasm_block_position);
|
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 {
|
if let Event::PlayerStatisticIncrementEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.amount = data.amount;
|
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 {
|
if let Event::PlayerSwapHandsEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerTakeLecternBookEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerUnleashEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerVelocityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.velocity = from_wasm_position(data.velocity);
|
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 {
|
if let Event::PlayerInputEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.input = data.input;
|
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 {
|
if let Event::PlayerInteractAtEntityEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerLinksSendEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.links = data.links;
|
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 {
|
if let Event::PlayerPickupArrowEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PlayerRecipeBookClickEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.recipe_id = data.recipe_id;
|
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 {
|
if let Event::PlayerRecipeBookSettingsChangeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.book_type = data.book_type;
|
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 {
|
if let Event::PlayerRecipeDiscoverEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.recipe_id = data.recipe_id;
|
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 {
|
if let Event::PlayerRegisterChannelEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.channel = data.channel;
|
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 {
|
if let Event::PlayerResourcePackStatusEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.status = data.status;
|
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 {
|
if let Event::PlayerSpawnLocationEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.spawn_pos = from_wasm_position(data.spawn_pos);
|
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 {
|
if let Event::PlayerUnregisterChannelEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.channel = data.channel;
|
self.channel = data.channel;
|
||||||
|
|||||||
@ -2,7 +2,9 @@ use crate::plugin::{
|
|||||||
loader::wasm::wasm_host::{
|
loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
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::{
|
pumpkin::plugin::event::{
|
||||||
Event, RaidFinishEventData, RaidSpawnWaveEventData, RaidStopEventData,
|
Event, RaidFinishEventData, RaidSpawnWaveEventData, RaidStopEventData,
|
||||||
RaidTriggerEventData,
|
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 {
|
if let Event::RaidFinishEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::RaidSpawnWaveEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::RaidStopEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
self.reason = data.reason;
|
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 {
|
if let Event::RaidTriggerEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use crate::plugin::{
|
|||||||
loader::wasm::wasm_host::{
|
loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
events::{ToFromWasmEvent, consume_text_component},
|
events::{ToFromWasmEvent, cleanup_event, consume_text_component},
|
||||||
generated_packets,
|
generated_packets,
|
||||||
pumpkin::plugin::event::{
|
pumpkin::plugin::event::{
|
||||||
ClientboundPacket, Event, MapInitializeEventData, PacketReceivedEventData,
|
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 {
|
if let Event::PacketReceivedEvent(data) = event {
|
||||||
self.packet_id = data.packet_id;
|
self.packet_id = data.packet_id;
|
||||||
self.payload = data.raw_payload.into();
|
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 {
|
if let Event::PacketSentEvent(data) = event {
|
||||||
self.payload = data.raw_payload.into();
|
self.payload = data.raw_payload.into();
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
@ -207,6 +209,7 @@ impl ToFromWasmEvent for ServerListPingEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
fn apply_wasm_event(&mut self, event: Event, state: &mut PluginHostState) {
|
||||||
|
cleanup_event(&event, state);
|
||||||
match event {
|
match event {
|
||||||
Event::ServerListPingEvent(data) => {
|
Event::ServerListPingEvent(data) => {
|
||||||
self.motd = consume_text_component(state, &data.motd);
|
self.motd = consume_text_component(state, &data.motd);
|
||||||
|
|||||||
@ -2,7 +2,9 @@ use crate::plugin::{
|
|||||||
loader::wasm::wasm_host::{
|
loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
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::{
|
pumpkin::plugin::event::{
|
||||||
Event, VehicleBlockCollisionEventData, VehicleCollisionEventData,
|
Event, VehicleBlockCollisionEventData, VehicleCollisionEventData,
|
||||||
VehicleCreateEventData, VehicleDamageEventData, VehicleDestroyEventData,
|
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 {
|
if let Event::VehicleBlockCollisionEvent(data) = event {
|
||||||
self.block_pos = from_wasm_block_position(data.block_pos);
|
self.block_pos = from_wasm_block_position(data.block_pos);
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleCollisionEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleCreateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleDamageEvent(data) = event {
|
||||||
self.damage = data.damage;
|
self.damage = data.damage;
|
||||||
self.attacker_id = data.attacker_id;
|
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 {
|
if let Event::VehicleDestroyEvent(data) = event {
|
||||||
self.attacker_id = data.attacker_id;
|
self.attacker_id = data.attacker_id;
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleEnterEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleEntityCollisionEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleExitEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::VehicleMoveEvent(data) = event {
|
||||||
self.from = Vector3::new(
|
self.from = Vector3::new(
|
||||||
data.from_position.0,
|
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 {
|
if let Event::VehicleUpdateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@ use crate::plugin::{
|
|||||||
state::PluginHostState,
|
state::PluginHostState,
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
events::{
|
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::{
|
pumpkin::plugin::event::{
|
||||||
ChunkLoadEventData, ChunkSaveEventData, ChunkSendEventData, Event,
|
ChunkLoadEventData, ChunkSaveEventData, ChunkSendEventData, Event,
|
||||||
@ -90,6 +91,7 @@ impl ToFromWasmEvent for ChunkLoad {
|
|||||||
blending_data: None,
|
blending_data: None,
|
||||||
dirty: std::sync::atomic::AtomicBool::new(false),
|
dirty: std::sync::atomic::AtomicBool::new(false),
|
||||||
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
||||||
|
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
@ -139,6 +141,7 @@ impl ToFromWasmEvent for ChunkSave {
|
|||||||
blending_data: None,
|
blending_data: None,
|
||||||
dirty: std::sync::atomic::AtomicBool::new(false),
|
dirty: std::sync::atomic::AtomicBool::new(false),
|
||||||
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
||||||
|
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
@ -187,6 +190,7 @@ impl ToFromWasmEvent for ChunkSend {
|
|||||||
blending_data: None,
|
blending_data: None,
|
||||||
dirty: std::sync::atomic::AtomicBool::new(false),
|
dirty: std::sync::atomic::AtomicBool::new(false),
|
||||||
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
inhabited_time: std::sync::atomic::AtomicU64::new(0),
|
||||||
|
custom_data: std::sync::Mutex::new(pumpkin_nbt::compound::NbtCompound::new()),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
world,
|
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 {
|
if let Event::AsyncStructureGenerateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::AsyncStructureSpawnEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ChunkPopulateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::ChunkUnloadEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntitiesLoadEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::EntitiesUnloadEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::GenericGameEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::LootGenerateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::PortalCreateEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::StructureGrowEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::TimeSkipEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::WorldSaveEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
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 {
|
if let Event::LightningStrikeEvent(data) = event {
|
||||||
self.cancelled = data.cancelled;
|
self.cancelled = data.cancelled;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,10 +6,74 @@ use crate::plugin::api::gui::{PluginGui, PluginInventory};
|
|||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::{GuiResource, PluginHostState},
|
state::{GuiResource, PluginHostState},
|
||||||
wit::v0_1::pumpkin::plugin::{
|
wit::v0_1::pumpkin::plugin::{
|
||||||
gui::{self, Gui, GuiType},
|
gui::{self, Gui},
|
||||||
item_stack::ItemStack as WitHostItemStack,
|
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 {
|
impl PluginHostState {
|
||||||
fn get_gui_res(&self, res: &Resource<Gui>) -> wasmtime::Result<&GuiResource> {
|
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 {
|
impl gui::HostGui for PluginHostState {
|
||||||
async fn new(
|
async fn new(
|
||||||
&mut self,
|
&mut self,
|
||||||
gui_type: GuiType,
|
screen: WitScreen,
|
||||||
title: Resource<
|
title: Resource<
|
||||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::text::TextComponent,
|
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::text::TextComponent,
|
||||||
>,
|
>,
|
||||||
) -> wasmtime::Result<Resource<Gui>> {
|
) -> wasmtime::Result<Resource<Gui>> {
|
||||||
let title = self.get_text_provider(&title)?;
|
let title = self.get_text_provider(&title)?;
|
||||||
let window_type = match gui_type {
|
let window_type = from_wit_screen(screen);
|
||||||
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 size = match window_type {
|
let size = match window_type {
|
||||||
pumpkin_data::screen::WindowType::Generic9x2 => 18,
|
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;
|
let gui = self.get_gui_res(&res)?.provider.lock().await;
|
||||||
Ok(match gui.window_type {
|
Ok(to_wit_screen(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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_title(
|
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::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::enchantments::Enchantment as WitEnchantment;
|
||||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::{
|
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::item_stack::{
|
||||||
|
CustomEnchantmentValue as WitCustomEnchantmentValue,
|
||||||
DataComponentValue as WitDataComponentValue, EnchantmentValue as WitEnchantmentValue,
|
DataComponentValue as WitDataComponentValue, EnchantmentValue as WitEnchantmentValue,
|
||||||
Host as ItemStackInterfaceHost, HostItemStack, ItemStack as ItemStackHandle,
|
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 crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::text::TextComponent as WitTextComponent;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use wasmtime::component::Resource;
|
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 crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::text_component_from_resource;
|
||||||
use pumpkin_data::Enchantment;
|
use pumpkin_data::Enchantment;
|
||||||
use pumpkin_data::data_component::DataComponent;
|
use pumpkin_data::data_component::DataComponent;
|
||||||
use pumpkin_data::data_component_impl::{CustomNameImpl, EnchantmentsImpl};
|
use pumpkin_data::data_component_impl::{CustomNameImpl, EnchantmentsImpl};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
|
||||||
use pumpkin_nbt::tag::NbtTag;
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_protocol::codec::data_component::{deserialize, serialize};
|
use pumpkin_protocol::codec::data_component::{deserialize, serialize};
|
||||||
use std::borrow::Cow;
|
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")
|
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 {
|
impl PluginHostState {
|
||||||
pub fn get_item_stack(
|
pub fn get_item_stack(
|
||||||
&self,
|
&self,
|
||||||
@ -289,6 +204,182 @@ impl HostItemStack for PluginHostState {
|
|||||||
Ok(())
|
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(
|
async fn get_lore(
|
||||||
&mut self,
|
&mut self,
|
||||||
res: Resource<ItemStackHandle>,
|
res: Resource<ItemStackHandle>,
|
||||||
|
|||||||
@ -8,11 +8,14 @@ use tokio::sync::Mutex;
|
|||||||
use wasmtime::component::{HasSelf, InstancePre, Linker, bindgen};
|
use wasmtime::component::{HasSelf, InstancePre, Linker, bindgen};
|
||||||
use wasmtime::{Engine, Store};
|
use wasmtime::{Engine, Store};
|
||||||
|
|
||||||
|
pub mod advancement;
|
||||||
pub mod block_entity;
|
pub mod block_entity;
|
||||||
pub mod boss_bar;
|
pub mod boss_bar;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod display;
|
||||||
|
pub mod enchantment;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod forms;
|
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::enchantments::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::biomes::Host for PluginHostState {}
|
impl pumpkin::plugin::biomes::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::attributes::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<()> {
|
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
|
||||||
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
||||||
@ -64,6 +72,7 @@ pub async fn init_plugin(
|
|||||||
plugin_pre: PluginPre<PluginHostState>,
|
plugin_pre: PluginPre<PluginHostState>,
|
||||||
) -> Result<(WasmPlugin, PluginMetadata), PluginInitError> {
|
) -> Result<(WasmPlugin, PluginMetadata), PluginInitError> {
|
||||||
let mut store = Store::new(engine, PluginHostState::new());
|
let mut store = Store::new(engine, PluginHostState::new());
|
||||||
|
store.limiter(|state| &mut state.limits);
|
||||||
let plugin = plugin_pre
|
let plugin = plugin_pre
|
||||||
.instantiate_async(&mut store)
|
.instantiate_async(&mut store)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -17,12 +17,18 @@ use crate::{
|
|||||||
GuiResource, PlayerResource, PluginHostState, TextComponentResource, WorldResource,
|
GuiResource, PlayerResource, PluginHostState, TextComponentResource, WorldResource,
|
||||||
},
|
},
|
||||||
wit::v0_1::{
|
wit::v0_1::{
|
||||||
|
entity::from_wit_damage_type,
|
||||||
events::{
|
events::{
|
||||||
from_wasm_game_mode, from_wasm_position, to_wasm_game_mode, to_wasm_position,
|
from_wasm_game_mode, from_wasm_position, to_wasm_game_mode, to_wasm_position,
|
||||||
},
|
},
|
||||||
pumpkin::{
|
pumpkin::{
|
||||||
self,
|
self,
|
||||||
|
plugin::damage_types::DamageType as WitDamageType,
|
||||||
plugin::player::{Player, PlayerSkin, SkinParts},
|
plugin::player::{Player, PlayerSkin, SkinParts},
|
||||||
|
plugin::statistics::{
|
||||||
|
CustomStatistic as WitCustomStatistic,
|
||||||
|
StatisticCategory as WitStatisticCategory,
|
||||||
|
},
|
||||||
plugin::uuid::Uuid,
|
plugin::uuid::Uuid,
|
||||||
plugin::world::World,
|
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 {
|
const fn to_wasm_bedrock_device_os(os: i32) -> pumpkin::plugin::player::BedrockDeviceOs {
|
||||||
match os {
|
match os {
|
||||||
1 => pumpkin::plugin::player::BedrockDeviceOs::Android,
|
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 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::codec::item_stack_seralizer::ItemStackSerializer;
|
||||||
use pumpkin_protocol::java::client::play::CSetContainerSlot;
|
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;
|
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(
|
async fn get_item_in_hand(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: Resource<Player>,
|
player: Resource<Player>,
|
||||||
@ -1434,9 +1574,16 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(())
|
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)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
player.damage_generic(amount).await;
|
player
|
||||||
|
.damage(&*player, amount, from_wit_damage_type(damage_type))
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1446,6 +1593,95 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(())
|
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(
|
async fn start_cooldown(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: Resource<Player>,
|
player: Resource<Player>,
|
||||||
@ -2313,6 +2549,214 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(())
|
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(
|
async fn as_java(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: Resource<Player>,
|
player: Resource<Player>,
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use crate::plugin::loader::wasm::wasm_host::state::PluginHostState;
|
use crate::plugin::loader::wasm::wasm_host::state::PluginHostState;
|
||||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::recipe::{
|
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::recipe::{
|
||||||
CookingRecipe as WitCookingRecipe, CookingType as WitCookingType, Host as RecipeHost,
|
CookingRecipe as WitCookingRecipe, CookingType as WitCookingType, Host as RecipeHost,
|
||||||
HostRecipeManager, Ingredient as WitIngredient, RecipeManager as WitRecipeManager,
|
HostRecipeManager, Ingredient as WitIngredient, RecipeCategory as WitRecipeCategory,
|
||||||
ShapedRecipe as WitShapedRecipe, ShapelessRecipe as WitShapelessRecipe,
|
RecipeManager as WitRecipeManager, ShapedRecipe as WitShapedRecipe,
|
||||||
|
ShapelessRecipe as WitShapelessRecipe,
|
||||||
};
|
};
|
||||||
use pumpkin_data::recipes::RecipeCategoryTypes;
|
use pumpkin_data::recipes::RecipeCategoryTypes;
|
||||||
use pumpkin_protocol::codec::recipe::{
|
use pumpkin_protocol::codec::recipe::{
|
||||||
@ -17,16 +18,21 @@ impl HostRecipeManager for PluginHostState {
|
|||||||
async fn register_shaped(
|
async fn register_shaped(
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<WitRecipeManager>,
|
_res: Resource<WitRecipeManager>,
|
||||||
_id: String,
|
id: String,
|
||||||
recipe: WitShapedRecipe,
|
recipe: WitShapedRecipe,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let result_stack = self.get_item_stack(&recipe.output)?;
|
let result_stack = self.get_item_stack(&recipe.output)?;
|
||||||
let result_stack = result_stack.lock().await;
|
let result_stack = result_stack.lock().await;
|
||||||
|
|
||||||
|
let category = recipe
|
||||||
|
.category
|
||||||
|
.map_or(RecipeCategoryTypes::Misc, to_data_category);
|
||||||
|
|
||||||
let owned_recipe = OwnedCraftingRecipe::Shaped {
|
let owned_recipe = OwnedCraftingRecipe::Shaped {
|
||||||
category: RecipeCategoryTypes::Misc, // TODO: Allow specifying category
|
recipe_id: Some(id),
|
||||||
|
category,
|
||||||
group: recipe.group,
|
group: recipe.group,
|
||||||
show_notification: true,
|
show_notification: recipe.show_notification.unwrap_or(true),
|
||||||
key: recipe
|
key: recipe
|
||||||
.key
|
.key
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -53,14 +59,19 @@ impl HostRecipeManager for PluginHostState {
|
|||||||
async fn register_shapeless(
|
async fn register_shapeless(
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<WitRecipeManager>,
|
_res: Resource<WitRecipeManager>,
|
||||||
_id: String,
|
id: String,
|
||||||
recipe: WitShapelessRecipe,
|
recipe: WitShapelessRecipe,
|
||||||
) -> wasmtime::Result<()> {
|
) -> wasmtime::Result<()> {
|
||||||
let result_stack = self.get_item_stack(&recipe.output)?;
|
let result_stack = self.get_item_stack(&recipe.output)?;
|
||||||
let result_stack = result_stack.lock().await;
|
let result_stack = result_stack.lock().await;
|
||||||
|
|
||||||
|
let category = recipe
|
||||||
|
.category
|
||||||
|
.map_or(RecipeCategoryTypes::Misc, to_data_category);
|
||||||
|
|
||||||
let owned_recipe = OwnedCraftingRecipe::Shapeless {
|
let owned_recipe = OwnedCraftingRecipe::Shapeless {
|
||||||
category: RecipeCategoryTypes::Misc,
|
recipe_id: Some(id),
|
||||||
|
category,
|
||||||
group: recipe.group,
|
group: recipe.group,
|
||||||
ingredients: recipe
|
ingredients: recipe
|
||||||
.ingredients
|
.ingredients
|
||||||
@ -94,9 +105,13 @@ impl HostRecipeManager for PluginHostState {
|
|||||||
let result_stack = self.get_item_stack(&recipe.output)?;
|
let result_stack = self.get_item_stack(&recipe.output)?;
|
||||||
let result_stack = result_stack.lock().await;
|
let result_stack = result_stack.lock().await;
|
||||||
|
|
||||||
|
let category = recipe
|
||||||
|
.category
|
||||||
|
.map_or(RecipeCategoryTypes::Misc, to_data_category);
|
||||||
|
|
||||||
let owned_cooking = OwnedCookingRecipe {
|
let owned_cooking = OwnedCookingRecipe {
|
||||||
recipe_id: id,
|
recipe_id: id,
|
||||||
category: RecipeCategoryTypes::Misc,
|
category,
|
||||||
group: recipe.group,
|
group: recipe.group,
|
||||||
ingredient: to_owned_ingredient(recipe.ingredient),
|
ingredient: to_owned_ingredient(recipe.ingredient),
|
||||||
cooking_time: recipe.cooking_time as i32,
|
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 {
|
fn to_owned_ingredient(ing: WitIngredient) -> OwnedRecipeIngredient {
|
||||||
match ing {
|
match ing {
|
||||||
WitIngredient::Item(id) => OwnedRecipeIngredient::Simple(id),
|
WitIngredient::Item(id) => OwnedRecipeIngredient::Simple(id),
|
||||||
WitIngredient::Tag(tag) => OwnedRecipeIngredient::Tagged(tag),
|
WitIngredient::Tag(tag) => OwnedRecipeIngredient::Tagged(tag),
|
||||||
|
WitIngredient::OneOf(items) => OwnedRecipeIngredient::OneOf(items),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -534,6 +534,120 @@ impl scoreboard::HostScoreboard for PluginHostState {
|
|||||||
Ok(())
|
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<()> {
|
async fn drop(&mut self, rep: Resource<scoreboard::Scoreboard>) -> wasmtime::Result<()> {
|
||||||
self.resource_table
|
self.resource_table
|
||||||
.delete::<ScoreboardResource>(Resource::new_own(rep.rep()))
|
.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 {
|
impl HostBedrockScoreboard for PluginHostState {
|
||||||
async fn add_objective(
|
async fn add_objective(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@ -2,6 +2,9 @@ use pumpkin_util::text::TextComponent;
|
|||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::command::CommandSender;
|
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 crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::recipe::RecipeManager as WitRecipeManager;
|
||||||
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
|
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
|
||||||
|
|
||||||
@ -196,13 +199,26 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
.worlds
|
.worlds
|
||||||
.load()
|
.load()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|world| world.dimension.minecraft_name == name)
|
.find(|world| world.get_world_name() == name || world.dimension.minecraft_name == name)
|
||||||
.map(|world| {
|
.map(|world| {
|
||||||
self.add_world(world.clone())
|
self.add_world(world.clone())
|
||||||
.expect("failed to add world resource")
|
.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(
|
async fn create_world(
|
||||||
&mut self,
|
&mut self,
|
||||||
_rep: Resource<Server>,
|
_rep: Resource<Server>,
|
||||||
@ -225,6 +241,52 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
.map_err(|_| wasmtime::Error::msg("failed to add world resource"))
|
.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<()> {
|
async fn broadcast(&mut self, _rep: Resource<Server>, message: String) -> wasmtime::Result<()> {
|
||||||
let server = self
|
let server = self
|
||||||
.server
|
.server
|
||||||
@ -438,6 +500,118 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
self.add_whitelist_manager(server.clone())
|
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<()> {
|
async fn drop(&mut self, rep: Resource<Server>) -> wasmtime::Result<()> {
|
||||||
self.resource_table
|
self.resource_table
|
||||||
.delete::<ServerResource>(Resource::new_own(rep.rep()))
|
.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::trial_spawner::TrialSpawnerBlockEntity as InternalTrialSpawnerBlockEntity;
|
||||||
use crate::block::entities::vault::VaultBlockEntity as InternalVaultBlockEntity;
|
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::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::{
|
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
|
||||||
BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags,
|
BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags,
|
||||||
BlockPos as WitBlockPos, BlockState as WitBlockState, BlockStateInfo as WitBlockStateInfo,
|
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},
|
wit::v0_1::pumpkin::{self, plugin::world::World},
|
||||||
};
|
};
|
||||||
use crate::world::explosion::Explosion;
|
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 {
|
pub(crate) const fn to_wasm_block_direction(dir: InternalBlockDirection) -> WitBlockDirection {
|
||||||
match dir {
|
match dir {
|
||||||
@ -131,7 +156,7 @@ pub(crate) const fn to_wit_bounding_box(
|
|||||||
|
|
||||||
// --- Trapping Helpers ---
|
// --- Trapping Helpers ---
|
||||||
impl PluginHostState {
|
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
|
self.resource_table
|
||||||
.get::<WorldResource>(&Resource::new_own(res.rep()))
|
.get::<WorldResource>(&Resource::new_own(res.rep()))
|
||||||
.map_err(wasmtime::Error::from)
|
.map_err(wasmtime::Error::from)
|
||||||
@ -892,6 +917,117 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
|||||||
Ok(Ok(()))
|
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<()> {
|
async fn drop(&mut self, rep: Resource<World>) -> wasmtime::Result<()> {
|
||||||
self.resource_table
|
self.resource_table
|
||||||
.delete::<WorldResource>(Resource::new_own(rep.rep()))
|
.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<()> {
|
async fn drop(&mut self, rep: Resource<WitChunk>) -> wasmtime::Result<()> {
|
||||||
self.resource_table
|
self.resource_table
|
||||||
.delete::<ChunkResource>(Resource::new_own(rep.rep()))
|
.delete::<ChunkResource>(Resource::new_own(rep.rep()))
|
||||||
@ -1245,3 +1442,317 @@ impl pumpkin::plugin::world::HostWorldBorder for PluginHostState {
|
|||||||
Ok(())
|
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) {
|
if loader.can_load(&path) {
|
||||||
match loader.load(&path).await {
|
match loader.load(&path).await {
|
||||||
Ok((instance, metadata, loader_data)) => {
|
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((
|
prepared_plugins.push((
|
||||||
instance,
|
instance,
|
||||||
metadata,
|
metadata,
|
||||||
@ -710,7 +742,7 @@ impl PluginManager {
|
|||||||
{
|
{
|
||||||
let (allowed, wait_time) = self
|
let (allowed, wait_time) = self
|
||||||
.clone()
|
.clone()
|
||||||
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path)
|
.check_permissions_cached(&path, &metadata, &mut cache, &cache_path, server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
total_wait_time += wait_time;
|
total_wait_time += wait_time;
|
||||||
@ -754,7 +786,33 @@ impl PluginManager {
|
|||||||
metadata: &PluginMetadata,
|
metadata: &PluginMetadata,
|
||||||
cache: &mut cache::PermissionCache,
|
cache: &mut cache::PermissionCache,
|
||||||
cache_path: &Path,
|
cache_path: &Path,
|
||||||
|
server: &Arc<Server>,
|
||||||
) -> (bool, std::time::Duration) {
|
) -> (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();
|
let hash = cache::calculate_hash(path).await.unwrap_or_default();
|
||||||
|
|
||||||
if let Some(entry) = cache.entries.get(&hash)
|
if let Some(entry) = cache.entries.get(&hash)
|
||||||
@ -767,6 +825,22 @@ impl PluginManager {
|
|||||||
return (entry.approved, std::time::Duration::ZERO);
|
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);
|
let (allowed, wait_time) = Self::ask_permission_confirmation(metadata);
|
||||||
cache.entries.insert(
|
cache.entries.insert(
|
||||||
hash,
|
hash,
|
||||||
@ -785,15 +859,51 @@ impl PluginManager {
|
|||||||
server: &Arc<Server>,
|
server: &Arc<Server>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<tokio::task::JoinHandle<()>, ManagerError> {
|
) -> 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() {
|
for loader in self.loaders.read().await.iter() {
|
||||||
if loader.can_load(path) {
|
if loader.can_load(path) {
|
||||||
let (instance, metadata, loader_data) = loader.load(path).await?;
|
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 cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
|
||||||
let mut cache = cache::PermissionCache::load(&cache_path).await;
|
let mut cache = cache::PermissionCache::load(&cache_path).await;
|
||||||
|
|
||||||
let (allowed, _) = self
|
let (allowed, _) = self
|
||||||
.check_permissions_cached(path, &metadata, &mut cache, &cache_path)
|
.check_permissions_cached(path, &metadata, &mut cache, &cache_path, server)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if !allowed {
|
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;
|
use tokio_util::task::TaskTracker;
|
||||||
|
|
||||||
mod connection_cache;
|
mod connection_cache;
|
||||||
|
pub mod enchantment;
|
||||||
mod key_store;
|
mod key_store;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
@ -105,6 +106,7 @@ pub struct Server {
|
|||||||
/// Assigns unique IDs to containers.
|
/// Assigns unique IDs to containers.
|
||||||
container_id: AtomicU32,
|
container_id: AtomicU32,
|
||||||
pub recipe_manager: Arc<recipe::RecipeManager>,
|
pub recipe_manager: Arc<recipe::RecipeManager>,
|
||||||
|
pub enchantment_manager: Arc<enchantment::EnchantmentManager>,
|
||||||
/// Assigns unique IDs to maps.
|
/// Assigns unique IDs to maps.
|
||||||
map_id: AtomicI32,
|
map_id: AtomicI32,
|
||||||
/// Mojang's public keys, used for chat session signing
|
/// Mojang's public keys, used for chat session signing
|
||||||
@ -283,6 +285,7 @@ impl Server {
|
|||||||
permission_registry,
|
permission_registry,
|
||||||
container_id: 0.into(),
|
container_id: 0.into(),
|
||||||
recipe_manager: Arc::new(recipe::RecipeManager::new()),
|
recipe_manager: Arc::new(recipe::RecipeManager::new()),
|
||||||
|
enchantment_manager: Arc::new(enchantment::EnchantmentManager::new()),
|
||||||
map_id: level_info.load().map_id.into(),
|
map_id: level_info.load().map_id.into(),
|
||||||
worlds: ArcSwap::from_pointee(vec![]),
|
worlds: ArcSwap::from_pointee(vec![]),
|
||||||
dimensions,
|
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.
|
/// Adds a new player to the server.
|
||||||
///
|
///
|
||||||
/// This function takes an `Arc<Client>` representing the connected client and performs the following actions:
|
/// 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 {
|
match plugin.plugin_instance {
|
||||||
crate::plugin::loader::wasm::wasm_host::PluginInstance::V0_1(ref instance) => {
|
crate::plugin::loader::wasm::wasm_host::PluginInstance::V0_1(ref instance) => {
|
||||||
if let Ok(server_res) = store.data_mut().add_server(server_clone) {
|
if let Ok(server_res) = store.data_mut().add_server(server_clone) {
|
||||||
|
let server_rep = server_res.rep();
|
||||||
let _ = instance
|
let _ = instance
|
||||||
.call_handle_task(&mut *store, handler_id, server_res)
|
.call_handle_task(&mut *store, handler_id, server_res)
|
||||||
.await;
|
.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::dimension::Dimension;
|
||||||
use pumpkin_data::entity::MobCategory;
|
use pumpkin_data::entity::MobCategory;
|
||||||
use pumpkin_data::fluid::{Falling, FluidProperties, FluidState};
|
use pumpkin_data::fluid::{Falling, FluidProperties, FluidState};
|
||||||
|
use pumpkin_data::game_rules::{GameRule, GameRuleValue};
|
||||||
use pumpkin_data::meta_data_type::MetaDataType;
|
use pumpkin_data::meta_data_type::MetaDataType;
|
||||||
use pumpkin_data::tracked_data::TrackedData;
|
use pumpkin_data::tracked_data::TrackedData;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
@ -274,6 +275,10 @@ pub struct World {
|
|||||||
/// Block entities indexed by chunk, so ticking only visits the currently
|
/// Block entities indexed by chunk, so ticking only visits the currently
|
||||||
/// active chunks instead of scanning every loaded block entity each tick.
|
/// active chunks instead of scanning every loaded block entity each tick.
|
||||||
pub block_entities: DashMap<Vector2<i32>, FxHashMap<BlockPos, Arc<dyn BlockEntity>>>,
|
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)]
|
#[derive(Clone, Copy)]
|
||||||
@ -346,6 +351,23 @@ impl World {
|
|||||||
let portal_poi = portal::PortalPoiStorage::new(level.level_folder.poi_folder.clone());
|
let portal_poi = portal::PortalPoiStorage::new(level.level_folder.poi_folder.clone());
|
||||||
let dragon_fight = (dimension.minecraft_name == Dimension::THE_END.minecraft_name)
|
let dragon_fight = (dimension.minecraft_name == Dimension::THE_END.minecraft_name)
|
||||||
.then(|| Mutex::new(dragon_fight::DragonFight::new()));
|
.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 {
|
Self {
|
||||||
uuid: Uuid::new_v4(),
|
uuid: Uuid::new_v4(),
|
||||||
level,
|
level,
|
||||||
@ -369,6 +391,8 @@ impl World {
|
|||||||
forced_chunks: std::sync::Mutex::new(FxHashSet::default()),
|
forced_chunks: std::sync::Mutex::new(FxHashSet::default()),
|
||||||
server,
|
server,
|
||||||
block_entities: DashMap::new(),
|
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 {
|
for block_entity in block_entities {
|
||||||
let mut nbt = NbtCompound::new();
|
let mut nbt = NbtCompound::new();
|
||||||
block_entity.write_internal(&mut nbt).await;
|
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);
|
self.add_block_entity_nbt(block_entity.get_position(), &nbt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -566,6 +597,29 @@ impl World {
|
|||||||
self.level_info.store(Arc::new(new_info));
|
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) {
|
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;
|
let mut queue = self.synced_block_event_queue.lock().await;
|
||||||
queue.push(BlockEvent { pos, r#type, data });
|
queue.push(BlockEvent { pos, r#type, data });
|
||||||
@ -5522,6 +5576,13 @@ impl World {
|
|||||||
.remove(block_pos)
|
.remove(block_pos)
|
||||||
})
|
})
|
||||||
.flatten()?;
|
.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)?;
|
let entity = block_entity_from_nbt(&nbt)?;
|
||||||
self.block_entities
|
self.block_entities
|
||||||
.entry(chunk_pos)
|
.entry(chunk_pos)
|
||||||
@ -5646,6 +5707,7 @@ impl World {
|
|||||||
chunk_block_entities.remove(block_pos).is_some()
|
chunk_block_entities.remove(block_pos).is_some()
|
||||||
});
|
});
|
||||||
if removed {
|
if removed {
|
||||||
|
self.custom_block_entity_data.remove(block_pos);
|
||||||
// Drop the chunk's map once its last block entity is gone.
|
// Drop the chunk's map once its last block entity is gone.
|
||||||
self.block_entities
|
self.block_entities
|
||||||
.remove_if(&chunk_pos, |_, entities| entities.is_empty());
|
.remove_if(&chunk_pos, |_, entities| entities.is_empty());
|
||||||
@ -6040,6 +6102,44 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self) {
|
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(
|
let mut save_event = crate::plugin::api::events::world::world_save::WorldSaveEvent::new(
|
||||||
format!("{:?}", self.dimension),
|
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>) {
|
pub async fn populate_chunk(&self, chunk_pos: Vector2<i32>) {
|
||||||
let mut populate_event =
|
let mut populate_event =
|
||||||
crate::plugin::api::events::world::chunk_populate::ChunkPopulateEvent::new(chunk_pos);
|
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_int("pairz"), Some(7));
|
||||||
assert_eq!(actor.get_bool("pairlead"), Some(true));
|
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 constants = Vec::new();
|
||||||
let mut type_from_name = TokenStream::new();
|
let mut type_from_name = TokenStream::new();
|
||||||
|
let mut type_from_id = TokenStream::new();
|
||||||
|
|
||||||
for (name, entry) in damage_types {
|
for (name, entry) in damage_types {
|
||||||
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
|
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
|
||||||
@ -76,6 +77,11 @@ pub fn build() -> TokenStream {
|
|||||||
#resource_name => Some(Self::#const_ident),
|
#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 data = &entry.components;
|
||||||
let death_message_type = if let Some(msg) = &data.death_message_type {
|
let death_message_type = if let Some(msg) = &data.death_message_type {
|
||||||
let msg_ident = Ident::new(&format!("{msg:?}"), proc_macro2::Span::call_site());
|
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(),
|
proc_macro2::Span::call_site(),
|
||||||
);
|
);
|
||||||
let scaling = quote! {DamageScaling::#scaling_ident};
|
let scaling = quote! {DamageScaling::#scaling_ident};
|
||||||
let id_lit = LitInt::new(&entry.id.to_string(), proc_macro2::Span::call_site());
|
|
||||||
|
|
||||||
constants.push(quote! {
|
constants.push(quote! {
|
||||||
pub const #const_ident: DamageType = DamageType {
|
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 {
|
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::collections::BTreeMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
let enchantments: BTreeMap<String, serde_json::Value> =
|
let enchantments: BTreeMap<String, serde_json::Value> =
|
||||||
serde_json::from_str(&fs::read_to_string("../../assets/enchantments.json").unwrap())
|
serde_json::from_str(&fs::read_to_string("../../assets/enchantments.json").unwrap())
|
||||||
.expect("Failed to parse enchantments.json");
|
.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<_>>();
|
let mut enchantment_vec = enchantments.keys().collect::<Vec<_>>();
|
||||||
enchantment_vec.sort();
|
enchantment_vec.sort();
|
||||||
|
|
||||||
|
let mut cases = String::new();
|
||||||
for raw_name in enchantment_vec {
|
for raw_name in enchantment_vec {
|
||||||
let name = raw_name
|
let name = raw_name
|
||||||
.strip_prefix("minecraft:")
|
.strip_prefix("minecraft:")
|
||||||
.unwrap_or(raw_name)
|
.unwrap_or(raw_name)
|
||||||
.replace('_', "-");
|
.replace('_', "-");
|
||||||
enchantment_enum.case(name);
|
cases.push_str(&format!(" {name},\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface.type_def(TypeDef::new(
|
format!(
|
||||||
"enchantment",
|
r##"package pumpkin:plugin@0.1.0;
|
||||||
TypeDefKind::Enum(enchantment_enum),
|
|
||||||
));
|
|
||||||
package.interface(interface);
|
|
||||||
|
|
||||||
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 attribute;
|
||||||
pub mod bedrock_packet;
|
pub mod bedrock_packet;
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
|
pub mod damage_type;
|
||||||
pub mod data_component;
|
pub mod data_component;
|
||||||
pub mod enchantment;
|
pub mod enchantment;
|
||||||
pub mod entity_type;
|
pub mod entity_type;
|
||||||
|
pub mod game_rules;
|
||||||
pub mod java_packet;
|
pub mod java_packet;
|
||||||
pub mod packet_mapping;
|
pub mod packet_mapping;
|
||||||
pub mod particle;
|
pub mod particle;
|
||||||
|
pub mod screen;
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
|
pub mod statistic;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -34,6 +38,10 @@ pub fn main() {
|
|||||||
(enchantment::build, "enchantments.wit"),
|
(enchantment::build, "enchantments.wit"),
|
||||||
(biome::build, "biomes.wit"),
|
(biome::build, "biomes.wit"),
|
||||||
(attribute::build, "attributes.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 {
|
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()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user