feat(plugin-api): add entity api
This commit is contained in:
parent
9012a92287
commit
b31f8a0fdd
@ -43,9 +43,9 @@ pub mod command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use wit::pumpkin::plugin::{
|
pub use wit::pumpkin::plugin::{
|
||||||
command as command_wit, common,
|
block_entity, command as command_wit, common,
|
||||||
context::{Context, Server},
|
context::{Context, Server},
|
||||||
gui, permission, scoreboard, server, text, world,
|
entity, gui, permission, scoreboard, server, text, world,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
|||||||
36
pumpkin-plugin-wit/v0.1.0/block-entity.wit
Normal file
36
pumpkin-plugin-wit/v0.1.0/block-entity.wit
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
interface block-entity {
|
||||||
|
use common.{block-position};
|
||||||
|
|
||||||
|
resource block-entity {
|
||||||
|
resource-location: func() -> string;
|
||||||
|
get-position: func() -> block-position;
|
||||||
|
get-id: func() -> u32;
|
||||||
|
// write-nbt: func(nbt: nbt-compound);
|
||||||
|
// from-nbt: func(nbt: nbt-compound, position: block-position) -> entity;
|
||||||
|
// tick: func(world: simple-world);
|
||||||
|
// write-internal: func(nbt: nbt-compound);
|
||||||
|
// chunk-data-nbt: func() -> option<nbt-compound;
|
||||||
|
// get-inventory: func() -> option<inventory>;
|
||||||
|
// set-block-state: func(block-state: block-state-id);
|
||||||
|
// on-block-replaced: func(world: simple-world, position: block-position);
|
||||||
|
is-dirty: func() -> bool;
|
||||||
|
clear-dirty: func();
|
||||||
|
// to-property-delegate: func() -> option<property-delegate>;
|
||||||
|
// to-experience-container: func() -> option<experience-container>;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource command-block-entity {
|
||||||
|
get-block-entity: func () -> block-entity;
|
||||||
|
last-output: func() -> string;
|
||||||
|
track-output: func() -> bool;
|
||||||
|
success-count: func() -> u32;
|
||||||
|
command: func() -> string;
|
||||||
|
auto: func() -> bool;
|
||||||
|
condition-met: func() -> bool;
|
||||||
|
powered: func() -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
variant block-entity-type {
|
||||||
|
command-block-entity(command-block-entity),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
interface command {
|
interface command {
|
||||||
use player.{player};
|
|
||||||
use %world.{%world};
|
use %world.{%world};
|
||||||
use entity.{command-block-entity, entity};
|
use entity.{entity};
|
||||||
|
use player.{player};
|
||||||
|
use block-entity.{command-block-entity};
|
||||||
use common.{position, block-position, locale};
|
use common.{position, block-position, locale};
|
||||||
use server.{server, difficulty};
|
use server.{server, difficulty};
|
||||||
use text.{text-component};
|
use text.{text-component};
|
||||||
|
|||||||
@ -67,6 +67,28 @@ interface common {
|
|||||||
count: u8,
|
count: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the various poses an entity can be in.
|
||||||
|
enum entity-pose {
|
||||||
|
standing,
|
||||||
|
fall-flying,
|
||||||
|
sleeping,
|
||||||
|
swimming,
|
||||||
|
spin-attack,
|
||||||
|
crouching,
|
||||||
|
long-jumping,
|
||||||
|
dying,
|
||||||
|
croaking,
|
||||||
|
using-tongue,
|
||||||
|
sitting,
|
||||||
|
roaring,
|
||||||
|
sniffing,
|
||||||
|
emerging,
|
||||||
|
digging,
|
||||||
|
sliding,
|
||||||
|
shooting,
|
||||||
|
inhaling,
|
||||||
|
}
|
||||||
|
|
||||||
/// All Minecraft-supported locales.
|
/// All Minecraft-supported locales.
|
||||||
enum locale {
|
enum locale {
|
||||||
af-za,
|
af-za,
|
||||||
|
|||||||
@ -1,36 +1,3 @@
|
|||||||
interface entity {
|
interface entity {
|
||||||
use common.{block-position};
|
use %world.{entity};
|
||||||
|
|
||||||
resource block-entity {
|
|
||||||
resource-location: func() -> string;
|
|
||||||
get-position: func() -> block-position;
|
|
||||||
get-id: func() -> u32;
|
|
||||||
// write-nbt: func(nbt: nbt-compound);
|
|
||||||
// from-nbt: func(nbt: nbt-compound, position: block-position) -> entity;
|
|
||||||
// tick: func(world: simple-world);
|
|
||||||
// write-internal: func(nbt: nbt-compound);
|
|
||||||
// chunk-data-nbt: func() -> option<nbt-compound;
|
|
||||||
// get-inventory: func() -> option<inventory>;
|
|
||||||
// set-block-state: func(block-state: block-state-id);
|
|
||||||
// on-block-replaced: func(world: simple-world, position: block-position);
|
|
||||||
is-dirty: func() -> bool;
|
|
||||||
clear-dirty: func();
|
|
||||||
// to-property-delegate: func() -> option<property-delegate>;
|
|
||||||
// to-experience-container: func() -> option<experience-container>;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource command-block-entity {
|
|
||||||
get-block-entity: func () -> block-entity;
|
|
||||||
last-output: func() -> string;
|
|
||||||
track-output: func() -> bool;
|
|
||||||
success-count: func() -> u32;
|
|
||||||
command: func() -> string;
|
|
||||||
auto: func() -> bool;
|
|
||||||
condition-met: func() -> bool;
|
|
||||||
powered: func() -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
variant entity {
|
|
||||||
command-block-entity(command-block-entity),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
interface player {
|
interface player {
|
||||||
use common.{hand, item-stack, position, game-mode};
|
use common.{hand, item-stack, position, game-mode};
|
||||||
use %world.{%world};
|
use %world.{%world};
|
||||||
|
use entity.{entity};
|
||||||
use permission.{permission-level};
|
use permission.{permission-level};
|
||||||
use text.{text-component};
|
use text.{text-component};
|
||||||
use gui.{gui};
|
use gui.{gui};
|
||||||
@ -26,6 +27,8 @@ interface player {
|
|||||||
|
|
||||||
/// A handle to a player on the server.
|
/// A handle to a player on the server.
|
||||||
resource player {
|
resource player {
|
||||||
|
/// Returns the player as an entity.
|
||||||
|
as-entity: func () -> entity;
|
||||||
/// Returns the player's unique UUID as a string.
|
/// Returns the player's unique UUID as a string.
|
||||||
get-id: func () -> string;
|
get-id: func () -> string;
|
||||||
/// Returns the player's username.
|
/// Returns the player's username.
|
||||||
|
|||||||
@ -29,6 +29,10 @@ world plugin {
|
|||||||
import i18n;
|
import i18n;
|
||||||
/// Provides task scheduling for delayed or repeating logic.
|
/// Provides task scheduling for delayed or repeating logic.
|
||||||
import scheduler;
|
import scheduler;
|
||||||
|
/// Provides world information and block manipulation.
|
||||||
|
import %world;
|
||||||
|
/// Provides entity types and manipulation.
|
||||||
|
import entity;
|
||||||
|
|
||||||
// --- Plugin-provided exports ---
|
// --- Plugin-provided exports ---
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
interface %world {
|
interface %world {
|
||||||
use common.{position};
|
use common.{position, entity-pose};
|
||||||
use text.{text-component};
|
use text.{text-component};
|
||||||
use scoreboard.{scoreboard};
|
use scoreboard.{scoreboard};
|
||||||
|
|
||||||
@ -101,6 +101,44 @@ interface %world {
|
|||||||
z: s32,
|
z: s32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource entity {
|
||||||
|
get-id: func() -> u32;
|
||||||
|
get-uuid: func() -> string;
|
||||||
|
get-type: func() -> string;
|
||||||
|
|
||||||
|
get-position: func() -> position;
|
||||||
|
get-world: func() -> %world;
|
||||||
|
|
||||||
|
get-yaw: func() -> f32;
|
||||||
|
get-pitch: func() -> f32;
|
||||||
|
get-head-yaw: func() -> f32;
|
||||||
|
|
||||||
|
is-on-ground: func() -> bool;
|
||||||
|
is-sneaking: func() -> bool;
|
||||||
|
is-sprinting: func() -> bool;
|
||||||
|
is-invisible: func() -> bool;
|
||||||
|
is-glowing: func() -> bool;
|
||||||
|
|
||||||
|
teleport: func(pos: position, world-ref: %world);
|
||||||
|
set-velocity: func(velocity: position);
|
||||||
|
get-velocity: func() -> position;
|
||||||
|
|
||||||
|
get-pose: func() -> entity-pose;
|
||||||
|
get-name: func() -> text-component;
|
||||||
|
set-custom-name: func(name: text-component);
|
||||||
|
get-custom-name: func() -> option<text-component>;
|
||||||
|
set-custom-name-visible: func(visible: bool);
|
||||||
|
is-custom-name-visible: func() -> bool;
|
||||||
|
|
||||||
|
is-invulnerable: func() -> bool;
|
||||||
|
set-invulnerable: func(invulnerable: bool);
|
||||||
|
|
||||||
|
get-fire-ticks: func() -> s32;
|
||||||
|
set-fire-ticks: func(ticks: s32);
|
||||||
|
|
||||||
|
remove: func();
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a Minecraft world (dimension).
|
/// Represents a Minecraft world (dimension).
|
||||||
resource %world {
|
resource %world {
|
||||||
/// Returns the unique identifier of this world.
|
/// Returns the unique identifier of this world.
|
||||||
@ -165,5 +203,8 @@ interface %world {
|
|||||||
|
|
||||||
/// Returns the minimum Y coordinate (bottom of the world).
|
/// Returns the minimum Y coordinate (bottom of the world).
|
||||||
get-min-y: func() -> s32;
|
get-min-y: func() -> s32;
|
||||||
|
|
||||||
|
/// Returns a list of all entities in this world.
|
||||||
|
get-entities: func() -> list<entity>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -304,6 +304,8 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
|||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
entity
|
entity
|
||||||
.custom_name
|
.custom_name
|
||||||
|
.load()
|
||||||
|
.as_ref()
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or(TextComponent::translate(
|
.unwrap_or(TextComponent::translate(
|
||||||
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
||||||
@ -315,8 +317,11 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// TODO: team color
|
// TODO: team color
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
let mut name = entity
|
let mut name =
|
||||||
|
entity
|
||||||
.custom_name
|
.custom_name
|
||||||
|
.load()
|
||||||
|
.as_ref()
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or(TextComponent::translate(
|
.unwrap_or(TextComponent::translate(
|
||||||
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
||||||
@ -474,9 +479,9 @@ pub struct Entity {
|
|||||||
|
|
||||||
pub portal_manager: Mutex<Option<Mutex<PortalManager>>>,
|
pub portal_manager: Mutex<Option<Mutex<PortalManager>>>,
|
||||||
/// Custom name for the entity
|
/// Custom name for the entity
|
||||||
pub custom_name: Option<TextComponent>,
|
pub custom_name: ArcSwap<Option<TextComponent>>,
|
||||||
/// Indicates whether the entity's custom name is visible
|
/// Indicates whether the entity's custom name is visible
|
||||||
pub custom_name_visible: bool,
|
pub custom_name_visible: AtomicBool,
|
||||||
/// The data send in the Entity Spawn packet
|
/// The data send in the Entity Spawn packet
|
||||||
pub data: AtomicI32,
|
pub data: AtomicI32,
|
||||||
/// Stores entity boolean flags (on fire, sneaking, invisible, glowing, etc.)
|
/// Stores entity boolean flags (on fire, sneaking, invisible, glowing, etc.)
|
||||||
@ -600,8 +605,8 @@ impl Entity {
|
|||||||
age: AtomicI32::new(0),
|
age: AtomicI32::new(0),
|
||||||
portal_cooldown: AtomicU32::new(0),
|
portal_cooldown: AtomicU32::new(0),
|
||||||
portal_manager: Mutex::new(None),
|
portal_manager: Mutex::new(None),
|
||||||
custom_name: None,
|
custom_name: ArcSwap::new(Arc::new(None)),
|
||||||
custom_name_visible: false,
|
custom_name_visible: AtomicBool::new(false),
|
||||||
no_clip: AtomicBool::new(false),
|
no_clip: AtomicBool::new(false),
|
||||||
movement_multiplier: AtomicCell::new(Vector3::default()),
|
movement_multiplier: AtomicCell::new(Vector3::default()),
|
||||||
velocity_dirty: AtomicBool::new(true),
|
velocity_dirty: AtomicBool::new(true),
|
||||||
@ -636,6 +641,7 @@ impl Entity {
|
|||||||
|
|
||||||
/// Sets a custom name for the entity, typically used with nametags
|
/// Sets a custom name for the entity, typically used with nametags
|
||||||
pub async fn set_custom_name(&self, name: TextComponent) {
|
pub async fn set_custom_name(&self, name: TextComponent) {
|
||||||
|
self.custom_name.store(Arc::new(Some(name.clone())));
|
||||||
self.send_meta_data(&[Metadata::new(
|
self.send_meta_data(&[Metadata::new(
|
||||||
TrackedData::CUSTOM_NAME,
|
TrackedData::CUSTOM_NAME,
|
||||||
MetaDataType::OPTIONAL_TEXT_COMPONENT,
|
MetaDataType::OPTIONAL_TEXT_COMPONENT,
|
||||||
@ -644,6 +650,16 @@ impl Entity {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_custom_name_visible(&self, visible: bool) {
|
||||||
|
self.custom_name_visible.store(visible, Ordering::Relaxed);
|
||||||
|
self.send_meta_data(&[Metadata::new(
|
||||||
|
TrackedData::CUSTOM_NAME_VISIBLE,
|
||||||
|
MetaDataType::BOOLEAN,
|
||||||
|
visible,
|
||||||
|
)])
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_velocity(&self) {
|
pub async fn send_velocity(&self) {
|
||||||
let velocity = self.velocity.load();
|
let velocity = self.velocity.load();
|
||||||
let chunk_pos = self.chunk_pos.load();
|
let chunk_pos = self.chunk_pos.load();
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use crate::{
|
|||||||
args::ConsumedArgs,
|
args::ConsumedArgs,
|
||||||
tree::{CommandTree, builder::NonLeafNodeBuilder},
|
tree::{CommandTree, builder::NonLeafNodeBuilder},
|
||||||
},
|
},
|
||||||
|
entity::EntityBase,
|
||||||
entity::player::Player,
|
entity::player::Player,
|
||||||
plugin::{
|
plugin::{
|
||||||
Context,
|
Context,
|
||||||
@ -31,6 +32,7 @@ pub struct WasmResource<T> {
|
|||||||
pub type ServerResource = WasmResource<Arc<Server>>;
|
pub type ServerResource = WasmResource<Arc<Server>>;
|
||||||
pub type ContextResource = WasmResource<Arc<Context>>;
|
pub type ContextResource = WasmResource<Arc<Context>>;
|
||||||
pub type PlayerResource = WasmResource<Arc<Player>>;
|
pub type PlayerResource = WasmResource<Arc<Player>>;
|
||||||
|
pub type EntityResource = WasmResource<Arc<dyn EntityBase>>;
|
||||||
pub type WorldResource = WasmResource<Arc<World>>;
|
pub type WorldResource = WasmResource<Arc<World>>;
|
||||||
pub type ScoreboardResource = WasmResource<Arc<World>>;
|
pub type ScoreboardResource = WasmResource<Arc<World>>;
|
||||||
pub type GuiResource = WasmResource<Arc<Mutex<PluginGui>>>;
|
pub type GuiResource = WasmResource<Arc<Mutex<PluginGui>>>;
|
||||||
@ -91,6 +93,14 @@ impl PluginHostState {
|
|||||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_entity<T>(
|
||||||
|
&mut self,
|
||||||
|
provider: Arc<dyn EntityBase>,
|
||||||
|
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
||||||
|
let resource = self.resource_table.push(EntityResource { provider })?;
|
||||||
|
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_world<T>(
|
pub fn add_world<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider: Arc<World>,
|
provider: Arc<World>,
|
||||||
|
|||||||
@ -0,0 +1,90 @@
|
|||||||
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
|
state::PluginHostState,
|
||||||
|
wit::v0_1_0::pumpkin::{
|
||||||
|
self,
|
||||||
|
plugin::{
|
||||||
|
block_entity::{BlockEntity, CommandBlockEntity},
|
||||||
|
common::BlockPosition,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl pumpkin::plugin::block_entity::Host for PluginHostState {}
|
||||||
|
|
||||||
|
impl pumpkin::plugin::block_entity::HostBlockEntity for PluginHostState {
|
||||||
|
async fn resource_location(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<String> {
|
||||||
|
todo!("Implement resource_location lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_position(
|
||||||
|
&mut self,
|
||||||
|
_res: Resource<BlockEntity>,
|
||||||
|
) -> wasmtime::Result<BlockPosition> {
|
||||||
|
todo!("Implement get_position lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_id(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<u32> {
|
||||||
|
todo!("Implement get_id lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<bool> {
|
||||||
|
todo!("Implement is_dirty lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
||||||
|
todo!("Implement clear_dirty logic")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop(&mut self, _rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl pumpkin::plugin::block_entity::HostCommandBlockEntity for PluginHostState {
|
||||||
|
async fn get_block_entity(
|
||||||
|
&mut self,
|
||||||
|
_res: Resource<CommandBlockEntity>,
|
||||||
|
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||||
|
todo!("Implement upcasting/mapping to BlockEntity")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn last_output(
|
||||||
|
&mut self,
|
||||||
|
_res: Resource<CommandBlockEntity>,
|
||||||
|
) -> wasmtime::Result<String> {
|
||||||
|
todo!("Implement last_output lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn track_output(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||||
|
todo!("Implement track_output lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn success_count(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
|
||||||
|
todo!("Implement success_count lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn command(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
|
||||||
|
todo!("Implement command lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auto(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||||
|
todo!("Implement auto lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn condition_met(
|
||||||
|
&mut self,
|
||||||
|
_res: Resource<CommandBlockEntity>,
|
||||||
|
) -> wasmtime::Result<bool> {
|
||||||
|
todo!("Implement condition_met lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn powered(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||||
|
todo!("Implement powered lookup")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,90 +1,304 @@
|
|||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::PluginHostState,
|
state::{EntityResource, PluginHostState},
|
||||||
wit::v0_1_0::pumpkin::{
|
wit::v0_1_0::events::to_wasm_position,
|
||||||
self,
|
wit::v0_1_0::pumpkin::plugin::{
|
||||||
plugin::{
|
common::{EntityPose, Position},
|
||||||
common::BlockPosition,
|
entity::Host,
|
||||||
entity::{BlockEntity, CommandBlockEntity},
|
text::TextComponent,
|
||||||
},
|
world::{Entity, HostEntity, World},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use pumpkin_data::entity::EntityPose as InternalEntityPose;
|
||||||
|
|
||||||
impl pumpkin::plugin::entity::Host for PluginHostState {}
|
impl Host for PluginHostState {}
|
||||||
|
|
||||||
impl pumpkin::plugin::entity::HostBlockEntity for PluginHostState {
|
fn entity_from_resource(
|
||||||
async fn resource_location(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<String> {
|
state: &PluginHostState,
|
||||||
todo!("Implement resource_location lookup")
|
entity: &Resource<Entity>,
|
||||||
|
) -> wasmtime::Result<std::sync::Arc<dyn crate::entity::EntityBase>> {
|
||||||
|
state
|
||||||
|
.resource_table
|
||||||
|
.get::<EntityResource>(&Resource::new_own(entity.rep()))
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid entity resource handle"))
|
||||||
|
.map(|resource| resource.provider.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_position(
|
const fn map_entity_pose(pose: InternalEntityPose) -> EntityPose {
|
||||||
|
match pose {
|
||||||
|
InternalEntityPose::Standing => EntityPose::Standing,
|
||||||
|
InternalEntityPose::FallFlying => EntityPose::FallFlying,
|
||||||
|
InternalEntityPose::Sleeping => EntityPose::Sleeping,
|
||||||
|
InternalEntityPose::Swimming => EntityPose::Swimming,
|
||||||
|
InternalEntityPose::SpinAttack => EntityPose::SpinAttack,
|
||||||
|
InternalEntityPose::Crouching => EntityPose::Crouching,
|
||||||
|
InternalEntityPose::LongJumping => EntityPose::LongJumping,
|
||||||
|
InternalEntityPose::Dying => EntityPose::Dying,
|
||||||
|
InternalEntityPose::Croaking => EntityPose::Croaking,
|
||||||
|
InternalEntityPose::UsingTongue => EntityPose::UsingTongue,
|
||||||
|
InternalEntityPose::Sitting => EntityPose::Sitting,
|
||||||
|
InternalEntityPose::Roaring => EntityPose::Roaring,
|
||||||
|
InternalEntityPose::Sniffing => EntityPose::Sniffing,
|
||||||
|
InternalEntityPose::Emerging => EntityPose::Emerging,
|
||||||
|
InternalEntityPose::Digging => EntityPose::Digging,
|
||||||
|
InternalEntityPose::Sliding => EntityPose::Sliding,
|
||||||
|
InternalEntityPose::Shooting => EntityPose::Shooting,
|
||||||
|
InternalEntityPose::Inhaling => EntityPose::Inhaling,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HostEntity for PluginHostState {
|
||||||
|
async fn get_id(&mut self, entity: Resource<Entity>) -> wasmtime::Result<u32> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().entity_id as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_uuid(&mut self, entity: Resource<Entity>) -> wasmtime::Result<String> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().entity_uuid.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_type(&mut self, entity: Resource<Entity>) -> wasmtime::Result<String> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().entity_type.resource_name.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(to_wasm_position(entity.get_entity().pos.load()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_world(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Resource<World>> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
let world = entity.get_entity().world.load_full();
|
||||||
|
self.add_world(world)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("failed to add world resource"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_yaw(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().yaw.load())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_pitch(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().pitch.load())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_head_yaw(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity.get_entity().head_yaw.load())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_on_ground(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.on_ground
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_sneaking(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.sneaking
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_sprinting(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.sprinting
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_invisible(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.invisible
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_glowing(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.glowing
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn teleport(
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<BlockEntity>,
|
entity: Resource<Entity>,
|
||||||
) -> wasmtime::Result<BlockPosition> {
|
pos: Position,
|
||||||
todo!("Implement get_position lookup")
|
world_ref: Resource<World>,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity_base = entity_from_resource(self, &entity)?;
|
||||||
|
let world = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::WorldResource>(
|
||||||
|
&Resource::new_own(world_ref.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid world resource handle"))?;
|
||||||
|
let world = world.provider.clone();
|
||||||
|
entity_base
|
||||||
|
.teleport(
|
||||||
|
pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
world,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_id(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<u32> {
|
async fn set_velocity(
|
||||||
todo!("Implement get_id lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<bool> {
|
|
||||||
todo!("Implement is_dirty lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn clear_dirty(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
|
||||||
todo!("Implement clear_dirty logic")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drop(&mut self, _rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl pumpkin::plugin::entity::HostCommandBlockEntity for PluginHostState {
|
|
||||||
async fn get_block_entity(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<CommandBlockEntity>,
|
entity: Resource<Entity>,
|
||||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
velocity: Position,
|
||||||
todo!("Implement upcasting/mapping to BlockEntity")
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity
|
||||||
|
.get_entity()
|
||||||
|
.velocity
|
||||||
|
.store(pumpkin_util::math::vector3::Vector3::new(
|
||||||
|
velocity.0, velocity.1, velocity.2,
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn last_output(
|
async fn get_velocity(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(to_wasm_position(entity.get_entity().velocity.load()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_pose(&mut self, entity: Resource<Entity>) -> wasmtime::Result<EntityPose> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(map_entity_pose(entity.get_entity().pose.load()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_name(
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<CommandBlockEntity>,
|
entity: Resource<Entity>,
|
||||||
) -> wasmtime::Result<String> {
|
) -> wasmtime::Result<Resource<TextComponent>> {
|
||||||
todo!("Implement last_output lookup")
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
let name = entity.get_name();
|
||||||
|
self.add_text_component(name)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("failed to add text component resource"))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn track_output(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
async fn set_custom_name(
|
||||||
todo!("Implement track_output lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn success_count(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
|
|
||||||
todo!("Implement success_count lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn command(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
|
|
||||||
todo!("Implement command lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn auto(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
|
||||||
todo!("Implement auto lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn condition_met(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
_res: Resource<CommandBlockEntity>,
|
entity: Resource<Entity>,
|
||||||
) -> wasmtime::Result<bool> {
|
name: Resource<TextComponent>,
|
||||||
todo!("Implement condition_met lookup")
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity_base = entity_from_resource(self, &entity)?;
|
||||||
|
let text_res = self
|
||||||
|
.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::TextComponentResource>(
|
||||||
|
&Resource::new_own(name.rep()),
|
||||||
|
)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("invalid text component resource handle"))?;
|
||||||
|
let text = text_res.provider.clone();
|
||||||
|
entity_base
|
||||||
|
.get_entity()
|
||||||
|
.custom_name
|
||||||
|
.store(std::sync::Arc::new(Some(text)));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn powered(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
async fn get_custom_name(
|
||||||
todo!("Implement powered lookup")
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
) -> wasmtime::Result<Option<Resource<TextComponent>>> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
let name = entity.get_entity().custom_name.load();
|
||||||
|
if let Some(name) = name.as_ref() {
|
||||||
|
Ok(Some(self.add_text_component(name.clone()).map_err(
|
||||||
|
|_| wasmtime::Error::msg("failed to add text component resource"),
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
|
async fn set_custom_name_visible(
|
||||||
todo!();
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
visible: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity
|
||||||
|
.get_entity()
|
||||||
|
.custom_name_visible
|
||||||
|
.store(visible, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_custom_name_visible(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.custom_name_visible
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_invulnerable(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.invulnerable
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_invulnerable(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
invulnerable: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity
|
||||||
|
.get_entity()
|
||||||
|
.invulnerable
|
||||||
|
.store(invulnerable, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_fire_ticks(&mut self, entity: Resource<Entity>) -> wasmtime::Result<i32> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.fire_ticks
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_fire_ticks(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
ticks: i32,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity
|
||||||
|
.get_entity()
|
||||||
|
.fire_ticks
|
||||||
|
.store(ticks, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove(&mut self, entity: Resource<Entity>) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().remove().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop(&mut self, rep: Resource<Entity>) -> wasmtime::Result<()> {
|
||||||
|
let _ = self
|
||||||
|
.resource_table
|
||||||
|
.delete::<EntityResource>(Resource::new_own(rep.rep()));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ use tokio::sync::Mutex;
|
|||||||
use wasmtime::component::{Component, HasData, Linker, bindgen};
|
use wasmtime::component::{Component, HasData, Linker, bindgen};
|
||||||
use wasmtime::{Engine, Store};
|
use wasmtime::{Engine, Store};
|
||||||
|
|
||||||
|
pub mod block_entity;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
|||||||
@ -131,6 +131,15 @@ impl DowncastResourceExt<PlayerResource> for Resource<Player> {
|
|||||||
|
|
||||||
impl pumpkin::plugin::player::Host for PluginHostState {}
|
impl pumpkin::plugin::player::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||||
|
async fn as_entity(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
) -> wasmtime::Result<Resource<pumpkin::plugin::world::Entity>> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
self.add_entity(player as Arc<dyn EntityBase>)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("failed to add entity resource"))
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_id(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
|
async fn get_id(&mut self, player: Resource<Player>) -> wasmtime::Result<String> {
|
||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
Ok(player.gameprofile.id.to_string())
|
Ok(player.gameprofile.id.to_string())
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use pumpkin_data::block_state::PistonBehavior;
|
use pumpkin_data::block_state::PistonBehavior;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::world::{BlockFlags, SimpleWorld};
|
use pumpkin_world::world::{BlockFlags, SimpleWorld};
|
||||||
|
use std::sync::Arc;
|
||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1_0::pumpkin::plugin::world::{
|
use crate::plugin::loader::wasm::wasm_host::wit::v0_1_0::pumpkin::plugin::world::{
|
||||||
@ -343,6 +344,26 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
|||||||
Ok(self.get_world_res(&world)?.provider.min_y)
|
Ok(self.get_world_res(&world)?.provider.min_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_entities(
|
||||||
|
&mut self,
|
||||||
|
world: Resource<World>,
|
||||||
|
) -> wasmtime::Result<Vec<Resource<pumpkin::plugin::entity::Entity>>> {
|
||||||
|
let world_provider = self.get_world_res(&world)?.provider.clone();
|
||||||
|
let mut entities = Vec::new();
|
||||||
|
|
||||||
|
// Add players as entities
|
||||||
|
for player in world_provider.players.load().iter() {
|
||||||
|
entities.push(self.add_entity(player.clone() as Arc<dyn crate::entity::EntityBase>)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other entities
|
||||||
|
for entity in world_provider.entities.load().iter() {
|
||||||
|
entities.push(self.add_entity(entity.clone())?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(entities)
|
||||||
|
}
|
||||||
|
|
||||||
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()))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user