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::{
|
||||
command as command_wit, common,
|
||||
block_entity, command as command_wit, common,
|
||||
context::{Context, Server},
|
||||
gui, permission, scoreboard, server, text, world,
|
||||
entity, gui, permission, scoreboard, server, text, world,
|
||||
};
|
||||
|
||||
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 {
|
||||
use player.{player};
|
||||
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 server.{server, difficulty};
|
||||
use text.{text-component};
|
||||
|
||||
@ -67,6 +67,28 @@ interface common {
|
||||
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.
|
||||
enum locale {
|
||||
af-za,
|
||||
|
||||
@ -1,36 +1,3 @@
|
||||
interface 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 entity {
|
||||
command-block-entity(command-block-entity),
|
||||
}
|
||||
use %world.{entity};
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
interface player {
|
||||
use common.{hand, item-stack, position, game-mode};
|
||||
use %world.{%world};
|
||||
use entity.{entity};
|
||||
use permission.{permission-level};
|
||||
use text.{text-component};
|
||||
use gui.{gui};
|
||||
@ -26,6 +27,8 @@ interface player {
|
||||
|
||||
/// A handle to a player on the server.
|
||||
resource player {
|
||||
/// Returns the player as an entity.
|
||||
as-entity: func () -> entity;
|
||||
/// Returns the player's unique UUID as a string.
|
||||
get-id: func () -> string;
|
||||
/// Returns the player's username.
|
||||
|
||||
@ -29,6 +29,10 @@ world plugin {
|
||||
import i18n;
|
||||
/// Provides task scheduling for delayed or repeating logic.
|
||||
import scheduler;
|
||||
/// Provides world information and block manipulation.
|
||||
import %world;
|
||||
/// Provides entity types and manipulation.
|
||||
import entity;
|
||||
|
||||
// --- Plugin-provided exports ---
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
interface %world {
|
||||
use common.{position};
|
||||
use common.{position, entity-pose};
|
||||
use text.{text-component};
|
||||
use scoreboard.{scoreboard};
|
||||
|
||||
@ -101,6 +101,44 @@ interface %world {
|
||||
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).
|
||||
resource %world {
|
||||
/// Returns the unique identifier of this world.
|
||||
@ -165,5 +203,8 @@ interface %world {
|
||||
|
||||
/// Returns the minimum Y coordinate (bottom of the world).
|
||||
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();
|
||||
entity
|
||||
.custom_name
|
||||
.load()
|
||||
.as_ref()
|
||||
.clone()
|
||||
.unwrap_or(TextComponent::translate(
|
||||
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
||||
@ -315,13 +317,16 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
||||
Box::pin(async move {
|
||||
// TODO: team color
|
||||
let entity = self.get_entity();
|
||||
let mut name = entity
|
||||
.custom_name
|
||||
.clone()
|
||||
.unwrap_or(TextComponent::translate(
|
||||
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
||||
[],
|
||||
));
|
||||
let mut name =
|
||||
entity
|
||||
.custom_name
|
||||
.load()
|
||||
.as_ref()
|
||||
.clone()
|
||||
.unwrap_or(TextComponent::translate(
|
||||
format!("entity.minecraft.{}", entity.entity_type.resource_name),
|
||||
[],
|
||||
));
|
||||
let name_clone = name.clone();
|
||||
name = name.hover_event(HoverEvent::show_entity(
|
||||
entity.entity_uuid.to_string(),
|
||||
@ -474,9 +479,9 @@ pub struct Entity {
|
||||
|
||||
pub portal_manager: Mutex<Option<Mutex<PortalManager>>>,
|
||||
/// 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
|
||||
pub custom_name_visible: bool,
|
||||
pub custom_name_visible: AtomicBool,
|
||||
/// The data send in the Entity Spawn packet
|
||||
pub data: AtomicI32,
|
||||
/// Stores entity boolean flags (on fire, sneaking, invisible, glowing, etc.)
|
||||
@ -600,8 +605,8 @@ impl Entity {
|
||||
age: AtomicI32::new(0),
|
||||
portal_cooldown: AtomicU32::new(0),
|
||||
portal_manager: Mutex::new(None),
|
||||
custom_name: None,
|
||||
custom_name_visible: false,
|
||||
custom_name: ArcSwap::new(Arc::new(None)),
|
||||
custom_name_visible: AtomicBool::new(false),
|
||||
no_clip: AtomicBool::new(false),
|
||||
movement_multiplier: AtomicCell::new(Vector3::default()),
|
||||
velocity_dirty: AtomicBool::new(true),
|
||||
@ -636,6 +641,7 @@ impl Entity {
|
||||
|
||||
/// Sets a custom name for the entity, typically used with nametags
|
||||
pub async fn set_custom_name(&self, name: TextComponent) {
|
||||
self.custom_name.store(Arc::new(Some(name.clone())));
|
||||
self.send_meta_data(&[Metadata::new(
|
||||
TrackedData::CUSTOM_NAME,
|
||||
MetaDataType::OPTIONAL_TEXT_COMPONENT,
|
||||
@ -644,6 +650,16 @@ impl Entity {
|
||||
.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) {
|
||||
let velocity = self.velocity.load();
|
||||
let chunk_pos = self.chunk_pos.load();
|
||||
|
||||
@ -14,6 +14,7 @@ use crate::{
|
||||
args::ConsumedArgs,
|
||||
tree::{CommandTree, builder::NonLeafNodeBuilder},
|
||||
},
|
||||
entity::EntityBase,
|
||||
entity::player::Player,
|
||||
plugin::{
|
||||
Context,
|
||||
@ -31,6 +32,7 @@ pub struct WasmResource<T> {
|
||||
pub type ServerResource = WasmResource<Arc<Server>>;
|
||||
pub type ContextResource = WasmResource<Arc<Context>>;
|
||||
pub type PlayerResource = WasmResource<Arc<Player>>;
|
||||
pub type EntityResource = WasmResource<Arc<dyn EntityBase>>;
|
||||
pub type WorldResource = WasmResource<Arc<World>>;
|
||||
pub type ScoreboardResource = WasmResource<Arc<World>>;
|
||||
pub type GuiResource = WasmResource<Arc<Mutex<PluginGui>>>;
|
||||
@ -91,6 +93,14 @@ impl PluginHostState {
|
||||
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>(
|
||||
&mut self,
|
||||
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 crate::plugin::loader::wasm::wasm_host::{
|
||||
state::PluginHostState,
|
||||
wit::v0_1_0::pumpkin::{
|
||||
self,
|
||||
plugin::{
|
||||
common::BlockPosition,
|
||||
entity::{BlockEntity, CommandBlockEntity},
|
||||
},
|
||||
state::{EntityResource, PluginHostState},
|
||||
wit::v0_1_0::events::to_wasm_position,
|
||||
wit::v0_1_0::pumpkin::plugin::{
|
||||
common::{EntityPose, Position},
|
||||
entity::Host,
|
||||
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 {
|
||||
async fn resource_location(&mut self, _res: Resource<BlockEntity>) -> wasmtime::Result<String> {
|
||||
todo!("Implement resource_location lookup")
|
||||
}
|
||||
fn entity_from_resource(
|
||||
state: &PluginHostState,
|
||||
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(
|
||||
&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!();
|
||||
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 pumpkin::plugin::entity::HostCommandBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
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,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
todo!("Implement upcasting/mapping to BlockEntity")
|
||||
entity: Resource<Entity>,
|
||||
pos: Position,
|
||||
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 last_output(
|
||||
async fn set_velocity(
|
||||
&mut self,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<String> {
|
||||
todo!("Implement last_output lookup")
|
||||
entity: Resource<Entity>,
|
||||
velocity: Position,
|
||||
) -> 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 track_output(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
todo!("Implement track_output lookup")
|
||||
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 success_count(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
|
||||
todo!("Implement success_count lookup")
|
||||
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 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(
|
||||
async fn get_name(
|
||||
&mut self,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<bool> {
|
||||
todo!("Implement condition_met lookup")
|
||||
entity: Resource<Entity>,
|
||||
) -> wasmtime::Result<Resource<TextComponent>> {
|
||||
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 powered(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
todo!("Implement powered lookup")
|
||||
async fn set_custom_name(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
name: Resource<TextComponent>,
|
||||
) -> 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 drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
|
||||
todo!();
|
||||
async fn get_custom_name(
|
||||
&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 set_custom_name_visible(
|
||||
&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::{Engine, Store};
|
||||
|
||||
pub mod block_entity;
|
||||
pub mod commands;
|
||||
pub mod common;
|
||||
pub mod context;
|
||||
|
||||
@ -131,6 +131,15 @@ impl DowncastResourceExt<PlayerResource> for Resource<Player> {
|
||||
|
||||
impl pumpkin::plugin::player::Host 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> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.gameprofile.id.to_string())
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use pumpkin_data::block_state::PistonBehavior;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::{BlockFlags, SimpleWorld};
|
||||
use std::sync::Arc;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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<()> {
|
||||
self.resource_table
|
||||
.delete::<WorldResource>(Resource::new_own(rep.rep()))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user