chore(plugin-api): extend entity api
This commit is contained in:
parent
7467195821
commit
4a4b72c83c
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2988,7 +2988,6 @@ dependencies = [
|
||||
"pumpkin-util",
|
||||
"pumpkin-world",
|
||||
"serde",
|
||||
"take_mut",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"uuid",
|
||||
@ -3760,12 +3759,6 @@ dependencies = [
|
||||
"winx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.13.5"
|
||||
|
||||
@ -169,7 +169,6 @@ tracing-subscriber = { version = "0.3.23", features = [
|
||||
"time",
|
||||
] }
|
||||
slotmap = "1.1"
|
||||
take_mut = "0.2.2"
|
||||
temp-dir = "0.2.0"
|
||||
thread_local = "1.1.9"
|
||||
time = "0.3"
|
||||
|
||||
@ -1,22 +1,12 @@
|
||||
interface block-entity {
|
||||
use common.{block-position};
|
||||
use common.{block-pos};
|
||||
|
||||
resource block-entity {
|
||||
resource-location: func() -> string;
|
||||
get-position: func() -> block-position;
|
||||
get-position: func() -> block-pos;
|
||||
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 {
|
||||
@ -30,7 +20,65 @@ interface block-entity {
|
||||
powered: func() -> bool;
|
||||
}
|
||||
|
||||
enum dye-color {
|
||||
white,
|
||||
orange,
|
||||
magenta,
|
||||
light-blue,
|
||||
yellow,
|
||||
lime,
|
||||
pink,
|
||||
gray,
|
||||
light-gray,
|
||||
cyan,
|
||||
purple,
|
||||
blue,
|
||||
brown,
|
||||
green,
|
||||
red,
|
||||
black,
|
||||
}
|
||||
|
||||
record sign-text {
|
||||
messages: list<string>,
|
||||
color: dye-color,
|
||||
has-glowing-text: bool,
|
||||
}
|
||||
|
||||
resource sign-block-entity {
|
||||
get-block-entity: func() -> block-entity;
|
||||
get-front-text: func() -> sign-text;
|
||||
set-front-text: func(text: sign-text);
|
||||
get-back-text: func() -> sign-text;
|
||||
set-back-text: func(text: sign-text);
|
||||
is-waxed: func() -> bool;
|
||||
set-waxed: func(waxed: bool);
|
||||
}
|
||||
|
||||
resource jukebox-block-entity {
|
||||
get-block-entity: func() -> block-entity;
|
||||
is-playing: func() -> bool;
|
||||
stop-playing: func();
|
||||
start-playing: func(length-in-ticks: u64);
|
||||
}
|
||||
|
||||
resource chest-block-entity {
|
||||
get-block-entity: func() -> block-entity;
|
||||
viewer-count: func() -> u32;
|
||||
}
|
||||
|
||||
resource mob-spawner-block-entity {
|
||||
get-block-entity: func() -> block-entity;
|
||||
get-spawn-count: func() -> s32;
|
||||
get-spawn-range: func() -> s32;
|
||||
get-delay: func() -> s32;
|
||||
}
|
||||
|
||||
variant block-entity-type {
|
||||
command-block-entity(command-block-entity),
|
||||
sign-block-entity(sign-block-entity),
|
||||
jukebox-block-entity(jukebox-block-entity),
|
||||
chest-block-entity(chest-block-entity),
|
||||
mob-spawner-block-entity(mob-spawner-block-entity),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ interface command {
|
||||
use %world.{%world, entity};
|
||||
use player.{player};
|
||||
use block-entity.{command-block-entity};
|
||||
use common.{position, block-position, locale, game-mode};
|
||||
use common.{position, block-pos, locale, game-mode};
|
||||
use item-stack.{item-stack};
|
||||
use server.{server, difficulty};
|
||||
use text.{text-component};
|
||||
@ -145,7 +145,7 @@ interface command {
|
||||
block-predicate(string),
|
||||
time(s32),
|
||||
num(result<number, not-in-bounds>),
|
||||
block-pos(tuple<s32, s32, s32>),
|
||||
block-pos(block-pos),
|
||||
pos3d(tuple<f64, f64, f64>),
|
||||
pos2d(tuple<f64, f64>),
|
||||
rotation(tuple<f32, bool, f32, bool>),
|
||||
|
||||
@ -5,7 +5,11 @@ interface common {
|
||||
type raw-text-component = list<u8>;
|
||||
|
||||
/// A position in the world grid (integers).
|
||||
type block-position = tuple<s32, s32, s32>;
|
||||
record block-pos {
|
||||
x: s32,
|
||||
y: s32,
|
||||
z: s32,
|
||||
}
|
||||
|
||||
/// A precise coordinate in the world (floats).
|
||||
type position = tuple<f64, f64, f64>;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
interface event {
|
||||
use common.{block-position, game-mode, hand, position, click-type};
|
||||
use common.{block-pos, game-mode, hand, position, click-type};
|
||||
use item-stack.{item-stack};
|
||||
use player.{player};
|
||||
use text.{text-component};
|
||||
@ -163,7 +163,7 @@ interface event {
|
||||
record player-interact-event-data {
|
||||
player: player,
|
||||
action: interact-action,
|
||||
clicked-pos: option<block-position>,
|
||||
clicked-pos: option<block-pos>,
|
||||
block: string,
|
||||
cancelled: bool
|
||||
}
|
||||
@ -213,7 +213,7 @@ interface event {
|
||||
record block-redstone-event-data {
|
||||
target-world: %world,
|
||||
state-id: u16,
|
||||
block-position: block-position,
|
||||
block-pos: block-pos,
|
||||
old-current: s32,
|
||||
new-current: s32,
|
||||
cancelled: bool
|
||||
@ -222,7 +222,7 @@ interface event {
|
||||
record block-break-event-data {
|
||||
player: option<player>,
|
||||
block: string,
|
||||
block-position: block-position,
|
||||
block-pos: block-pos,
|
||||
exp: u32,
|
||||
should-drop: bool,
|
||||
cancelled: bool
|
||||
@ -248,7 +248,7 @@ interface event {
|
||||
old-state-id: u16,
|
||||
new-block: string,
|
||||
new-state-id: u16,
|
||||
block-position: block-position,
|
||||
block-pos: block-pos,
|
||||
cancelled: bool
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ interface event {
|
||||
player: player,
|
||||
block-placed: string,
|
||||
block-placed-against: string,
|
||||
block-position: block-position,
|
||||
block-pos: block-pos,
|
||||
can-build: bool,
|
||||
cancelled: bool
|
||||
}
|
||||
@ -280,10 +280,10 @@ interface event {
|
||||
|
||||
record spawn-change-event-data {
|
||||
target-world: %world,
|
||||
previous-position: block-position,
|
||||
previous-position: block-pos,
|
||||
previous-yaw: f32,
|
||||
previous-pitch: f32,
|
||||
new-position: block-position,
|
||||
new-position: block-pos,
|
||||
new-yaw: f32,
|
||||
new-pitch: f32
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
interface %world {
|
||||
use common.{position, entity-pose};
|
||||
use common.{position, entity-pose, block-pos};
|
||||
use block-entity.{block-entity, command-block-entity, block-entity-type};
|
||||
use text.{text-component};
|
||||
use scoreboard.{scoreboard};
|
||||
use particles.{particle};
|
||||
@ -99,13 +100,6 @@ interface %world {
|
||||
id: u16,
|
||||
}
|
||||
|
||||
/// A position in the world grid.
|
||||
record block-pos {
|
||||
x: s32,
|
||||
y: s32,
|
||||
z: s32,
|
||||
}
|
||||
|
||||
resource entity {
|
||||
get-id: func() -> u32;
|
||||
get-uuid: func() -> uuid;
|
||||
@ -118,29 +112,6 @@ interface %world {
|
||||
get-pitch: func() -> f32;
|
||||
get-head-yaw: func() -> f32;
|
||||
|
||||
is-on-ground: func() -> bool;
|
||||
|
||||
is-sneaking: func() -> bool;
|
||||
set-sneaking: func(sneaking: bool);
|
||||
|
||||
is-sprinting: func() -> bool;
|
||||
set-sprinting: func(sprinting: bool);
|
||||
|
||||
is-swimming: func() -> bool;
|
||||
set-swimming: func(swimming: bool);
|
||||
|
||||
is-invisible: func() -> bool;
|
||||
set-invisible: func(invisible: bool);
|
||||
|
||||
is-glowing: func() -> bool;
|
||||
set-glowing: func(glowing: bool);
|
||||
|
||||
is-fall-flying: func() -> bool;
|
||||
set-fall-flying: func(fall-flying: bool);
|
||||
|
||||
is-on-fire: func() -> bool;
|
||||
set-on-fire: func(on-fire: bool);
|
||||
|
||||
teleport: func(pos: position, world-ref: %world);
|
||||
set-velocity: func(velocity: position);
|
||||
get-velocity: func() -> position;
|
||||
@ -158,6 +129,73 @@ interface %world {
|
||||
get-fire-ticks: func() -> s32;
|
||||
set-fire-ticks: func(ticks: s32);
|
||||
|
||||
get-health: func() -> f32;
|
||||
set-health: func(health: f32);
|
||||
get-max-health: func() -> f32;
|
||||
damage: func(amount: f32);
|
||||
is-dead: func() -> bool;
|
||||
|
||||
get-absorption: func() -> f32;
|
||||
set-absorption: func(amount: f32);
|
||||
|
||||
get-age: func() -> s32;
|
||||
set-age: func(age: s32);
|
||||
|
||||
get-fall-distance: func() -> f32;
|
||||
set-fall-distance: func(distance: f32);
|
||||
|
||||
get-ticks-lived: func() -> s32;
|
||||
set-ticks-lived: func(ticks: s32);
|
||||
|
||||
is-sneaking: func() -> bool;
|
||||
set-sneaking: func(sneaking: bool);
|
||||
is-sprinting: func() -> bool;
|
||||
set-sprinting: func(sprinting: bool);
|
||||
is-swimming: func() -> bool;
|
||||
set-swimming: func(swimming: bool);
|
||||
is-invisible: func() -> bool;
|
||||
set-invisible: func(invisible: bool);
|
||||
is-glowing: func() -> bool;
|
||||
set-glowing: func(glowing: bool);
|
||||
is-fall-flying: func() -> bool;
|
||||
set-fall-flying: func(fall-flying: bool);
|
||||
|
||||
get-width: func() -> f32;
|
||||
get-height: func() -> f32;
|
||||
|
||||
set-rotation: func(yaw: f32, pitch: f32);
|
||||
|
||||
is-on-ground: func() -> bool;
|
||||
is-on-fire: func() -> bool;
|
||||
set-on-fire: func(on-fire: bool);
|
||||
has-visual-fire: func() -> bool;
|
||||
set-visual-fire: func(visual-fire: bool);
|
||||
|
||||
get-portal-cooldown: func() -> u32;
|
||||
set-portal-cooldown: func(cooldown: u32);
|
||||
|
||||
get-remaining-air: func() -> s32;
|
||||
set-remaining-air: func(air: s32);
|
||||
get-max-air: func() -> s32;
|
||||
|
||||
send-system-message: func(message: text-component);
|
||||
|
||||
get-eye-height: func() -> f32;
|
||||
get-eye-position: func() -> position;
|
||||
|
||||
get-nearby-entities: func(x: f64, y: f64, z: f64) -> list<entity>;
|
||||
|
||||
get-vehicle: func() -> option<entity>;
|
||||
get-passengers: func() -> list<entity>;
|
||||
add-passenger: func(passenger: entity);
|
||||
remove-passenger: func(passenger: entity);
|
||||
eject-passengers: func();
|
||||
|
||||
get-bounding-box: func() -> bounding-box;
|
||||
|
||||
is-in-water: func() -> bool;
|
||||
is-in-lava: func() -> bool;
|
||||
|
||||
remove: func();
|
||||
|
||||
/// Performs a raycast from the entity's eye position in its looking direction.
|
||||
@ -172,6 +210,12 @@ interface %world {
|
||||
face: block-direction,
|
||||
}
|
||||
|
||||
/// Represents an axis-aligned bounding box.
|
||||
record bounding-box {
|
||||
min: position,
|
||||
max: position,
|
||||
}
|
||||
|
||||
/// Represents a cardinal direction or block face.
|
||||
enum block-direction {
|
||||
down,
|
||||
@ -267,5 +311,8 @@ interface %world {
|
||||
|
||||
/// Returns a list of all entities in this world.
|
||||
get-entities: func() -> list<entity>;
|
||||
|
||||
/// Returns the block entity at the specified position, if any.
|
||||
get-block-entity: func(pos: block-pos) -> option<block-entity-type>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,6 @@ hybrid-array = "0.4"
|
||||
async-compression = { workspace = true, features = ["tokio", "zlib", "deflate"] }
|
||||
flate2.workspace = true
|
||||
|
||||
take_mut.workspace = true
|
||||
bitflags.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@ -82,11 +82,6 @@ impl UDPNetworkDecoder {
|
||||
&mut self,
|
||||
_key: &[u8; 16],
|
||||
) -> Result<(), EncryptionAlreadyEnabledError> {
|
||||
// if matches!(self.reader, DecryptionReader::Decrypt(_)) {
|
||||
// return Err(EncryptionAlreadyEnabledError);
|
||||
// }
|
||||
// let cipher = Aes128Cfb8Dec::new_from_slices(key, key).map_err(|_| ());
|
||||
// take_mut::take(&mut self.reader, |decoder| decoder.upgrade(cipher));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -107,11 +107,6 @@ impl UDPNetworkEncoder {
|
||||
&mut self,
|
||||
_key: &[u8; 16],
|
||||
) -> Result<(), crate::bedrock::packet_decoder::EncryptionAlreadyEnabledError> {
|
||||
// if matches!(self.writer, EncryptionWriter::Encrypt(_)) {
|
||||
// return Err(crate::bedrock::packet_decoder::EncryptionAlreadyEnabledError);
|
||||
// }
|
||||
// let cipher = Aes128Cfb8Enc::new_from_slices(key, key).map_err(|_| ());
|
||||
// take_mut::take(&mut self.writer, |encoder| encoder.upgrade(cipher));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ impl<R: AsyncRead + Unpin> AsyncRead for DecryptionReader<R> {
|
||||
/// Supports `ZLib` decoding/decompression
|
||||
/// Supports Aes128 Encryption
|
||||
pub struct TCPNetworkDecoder<R: AsyncRead + Unpin> {
|
||||
reader: DecryptionReader<R>,
|
||||
reader: Option<DecryptionReader<R>>,
|
||||
compression: Option<CompressionThreshold>,
|
||||
payload_scratch: BytesMut,
|
||||
}
|
||||
@ -82,7 +82,7 @@ pub struct TCPNetworkDecoder<R: AsyncRead + Unpin> {
|
||||
impl<R: AsyncRead + Unpin> TCPNetworkDecoder<R> {
|
||||
pub fn new(reader: R) -> Self {
|
||||
Self {
|
||||
reader: DecryptionReader::None(reader),
|
||||
reader: Some(DecryptionReader::None(reader)),
|
||||
compression: None,
|
||||
payload_scratch: BytesMut::new(),
|
||||
}
|
||||
@ -94,19 +94,27 @@ impl<R: AsyncRead + Unpin> TCPNetworkDecoder<R> {
|
||||
|
||||
/// NOTE: Encryption can only be set; a minecraft stream cannot go back to being unencrypted
|
||||
pub fn set_encryption(&mut self, key: &[u8; 16]) -> Result<(), PacketDecodeError> {
|
||||
if matches!(self.reader, DecryptionReader::Decrypt(_)) {
|
||||
if matches!(self.reader, Some(DecryptionReader::Decrypt(_))) {
|
||||
return Err(PacketDecodeError::Message(
|
||||
"Encryption already enabled".into(),
|
||||
));
|
||||
}
|
||||
let cipher = Aes128Cfb8Dec::new_from_slices(key, key)
|
||||
.map_err(|_| PacketDecodeError::Message("Invalid key".into()))?;
|
||||
take_mut::take(&mut self.reader, |decoder| decoder.upgrade(cipher));
|
||||
|
||||
if let Some(reader) = self.reader.take() {
|
||||
self.reader = Some(reader.upgrade(cipher));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_raw_packet(&mut self) -> Result<RawPacket, PacketDecodeError> {
|
||||
let packet_len = VarInt::decode_async(&mut self.reader)
|
||||
let reader = self
|
||||
.reader
|
||||
.as_mut()
|
||||
.ok_or_else(|| PacketDecodeError::Message("Reader missing".into()))?;
|
||||
|
||||
let packet_len = VarInt::decode_async(reader)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
ReadingError::CleanEOF(_) => PacketDecodeError::ConnectionClosed,
|
||||
@ -119,7 +127,7 @@ impl<R: AsyncRead + Unpin> TCPNetworkDecoder<R> {
|
||||
Err(PacketDecodeError::OutOfBounds)?;
|
||||
}
|
||||
|
||||
let mut bounded_reader = (&mut self.reader).take(packet_len);
|
||||
let mut bounded_reader = reader.take(packet_len);
|
||||
let mut expected_packet_data_len = packet_len as usize;
|
||||
let mut expected_uncompressed_packet_data_len = None;
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ impl<W: AsyncWrite + Unpin> AsyncWrite for EncryptionWriter<W> {
|
||||
/// Supports `ZLib` endecoding/compression
|
||||
/// Supports Aes128 Encryption
|
||||
pub struct TCPNetworkEncoder<W: AsyncWrite + Unpin> {
|
||||
writer: EncryptionWriter<W>,
|
||||
writer: Option<EncryptionWriter<W>>,
|
||||
// compression and compression threshold
|
||||
compression: Option<(CompressionThreshold, CompressionLevel)>,
|
||||
// Reused compressor to avoid constructing zlib state per packet.
|
||||
@ -93,7 +93,7 @@ pub struct TCPNetworkEncoder<W: AsyncWrite + Unpin> {
|
||||
impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
pub const fn new(writer: W) -> Self {
|
||||
Self {
|
||||
writer: EncryptionWriter::None(writer),
|
||||
writer: Some(EncryptionWriter::None(writer)),
|
||||
compression: None,
|
||||
compressor: None,
|
||||
compression_scratch: Vec::new(),
|
||||
@ -109,14 +109,17 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
|
||||
/// NOTE: Encryption can only be set; a minecraft stream cannot go back to being unencrypted
|
||||
pub fn set_encryption(&mut self, key: &[u8; 16]) -> Result<(), PacketEncodeError> {
|
||||
if matches!(self.writer, EncryptionWriter::Encrypt(_)) {
|
||||
if matches!(self.writer, Some(EncryptionWriter::Encrypt(_))) {
|
||||
return Err(PacketEncodeError::Message(
|
||||
"Encryption already enabled".into(),
|
||||
));
|
||||
}
|
||||
let cipher = Aes128Cfb8Enc::new_from_slices(key, key)
|
||||
.map_err(|_| PacketEncodeError::Message("Invalid key".into()))?;
|
||||
take_mut::take(&mut self.writer, |encoder| encoder.upgrade(cipher));
|
||||
|
||||
if let Some(writer) = self.writer.take() {
|
||||
self.writer = Some(writer.upgrade(cipher));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -242,14 +245,16 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
}
|
||||
|
||||
full_packet_len_var_int
|
||||
.encode_async(&mut self.writer)
|
||||
.encode_async(self.writer.as_mut().unwrap())
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
data_len_var_int
|
||||
.encode_async(&mut self.writer)
|
||||
.encode_async(self.writer.as_mut().unwrap())
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
self.writer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&self.compression_scratch)
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
@ -274,14 +279,16 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
}
|
||||
|
||||
full_packet_len_var_int
|
||||
.encode_async(&mut self.writer)
|
||||
.encode_async(self.writer.as_mut().unwrap())
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
data_len_var_int
|
||||
.encode_async(&mut self.writer)
|
||||
.encode_async(self.writer.as_mut().unwrap())
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
self.writer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&packet_data)
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
@ -299,10 +306,12 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
}
|
||||
|
||||
full_packet_len_var_int
|
||||
.encode_async(&mut self.writer)
|
||||
.encode_async(self.writer.as_mut().unwrap())
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
self.writer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&packet_data)
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
|
||||
@ -313,6 +322,8 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
|
||||
|
||||
pub async fn flush(&mut self) -> Result<(), PacketEncodeError> {
|
||||
self.writer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.flush()
|
||||
.await
|
||||
.map_err(|err| PacketEncodeError::Message(err.to_string()))
|
||||
|
||||
@ -11,7 +11,7 @@ use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
|
||||
#[repr(i8)]
|
||||
pub enum DyeColor {
|
||||
White = 0,
|
||||
|
||||
@ -1936,6 +1936,7 @@ impl JavaClient {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn handle_use_item_on(
|
||||
&self,
|
||||
player: &Arc<Player>,
|
||||
@ -1985,6 +1986,26 @@ impl JavaClient {
|
||||
let world = entity.world.load_full();
|
||||
let block = world.get_block(&position);
|
||||
|
||||
let event = PlayerInteractEvent::new(
|
||||
player,
|
||||
InteractAction::RightClickBlock,
|
||||
block,
|
||||
Some(position),
|
||||
);
|
||||
|
||||
send_cancellable! {{
|
||||
server;
|
||||
event;
|
||||
'cancelled: {
|
||||
self.enqueue_packet(&CBlockUpdate::new(
|
||||
position,
|
||||
VarInt(block.id as i32),
|
||||
))
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}}
|
||||
|
||||
let sneaking = player.living_entity.entity.sneaking.load(Ordering::Relaxed);
|
||||
|
||||
// Code based on the java class ServerPlayerInteractionManager
|
||||
|
||||
@ -60,10 +60,10 @@ impl PlayerInteractEvent {
|
||||
/// Enum representing possible player interaction actions.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum InteractAction {
|
||||
/// Left-clicking the air
|
||||
/// Left-clicking a block
|
||||
LeftClickBlock,
|
||||
|
||||
/// Left-clicking a block
|
||||
/// Left-clicking the air
|
||||
LeftClickAir,
|
||||
|
||||
/// Right-clicking the air
|
||||
|
||||
@ -55,6 +55,7 @@ pub type ConsumedArgsResource = WasmResource<OwnedConsumedArgs>;
|
||||
pub type CommandNodeResource = WasmResource<NonLeafNodeBuilder>;
|
||||
pub type ItemStackResource = WasmResource<Arc<Mutex<pumpkin_data::item_stack::ItemStack>>>;
|
||||
pub type RecipeManagerResource = WasmResource<Arc<RecipeManager>>;
|
||||
pub type BlockEntityResource = WasmResource<Arc<dyn crate::block::entities::BlockEntity>>;
|
||||
|
||||
pub type OwnedConsumedArgs = HashMap<String, OwnedArg>;
|
||||
|
||||
@ -240,6 +241,14 @@ impl PluginHostState {
|
||||
.push(RecipeManagerResource { provider })?;
|
||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||
}
|
||||
|
||||
pub fn add_block_entity<T>(
|
||||
&mut self,
|
||||
provider: Arc<dyn crate::block::entities::BlockEntity>,
|
||||
) -> wasmtime::Result<wasmtime::component::Resource<T>> {
|
||||
let resource = self.resource_table.push(BlockEntityResource { provider })?;
|
||||
Ok(wasmtime::component::Resource::new_own(resource.rep()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PluginHttpHooks {
|
||||
|
||||
@ -1,90 +1,539 @@
|
||||
use std::sync::Arc;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
use crate::block::entities::BlockEntity as InternalBlockEntity;
|
||||
use crate::block::entities::chest::ChestBlockEntity as InternalChestBlockEntity;
|
||||
use crate::block::entities::command_block::CommandBlockEntity as InternalCommandBlockEntity;
|
||||
use crate::block::entities::jukebox::JukeboxBlockEntity as InternalJukeboxBlockEntity;
|
||||
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity as InternalMobSpawnerBlockEntity;
|
||||
use crate::block::entities::sign::{
|
||||
DyeColor as InternalDyeColor, SignBlockEntity as InternalSignBlockEntity, Text as InternalText,
|
||||
};
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::PluginHostState,
|
||||
state::{BlockEntityResource, PluginHostState},
|
||||
wit::v0_1::pumpkin::{
|
||||
self,
|
||||
plugin::{
|
||||
block_entity::{BlockEntity, CommandBlockEntity},
|
||||
common::BlockPosition,
|
||||
block_entity::{
|
||||
BlockEntity, ChestBlockEntity, CommandBlockEntity, DyeColor, HostBlockEntity,
|
||||
HostChestBlockEntity, HostCommandBlockEntity, HostJukeboxBlockEntity,
|
||||
HostMobSpawnerBlockEntity, HostSignBlockEntity, JukeboxBlockEntity,
|
||||
MobSpawnerBlockEntity, SignBlockEntity, SignText,
|
||||
},
|
||||
common::BlockPos as WitBlockPos,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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")
|
||||
}
|
||||
fn block_entity_from_resource(
|
||||
state: &PluginHostState,
|
||||
entity: &Resource<BlockEntity>,
|
||||
) -> wasmtime::Result<Arc<dyn InternalBlockEntity>> {
|
||||
state
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(entity.rep()))
|
||||
.map_err(|_| wasmtime::Error::msg("invalid block 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 from_wasm_dye_color(color: DyeColor) -> InternalDyeColor {
|
||||
match color {
|
||||
DyeColor::White => InternalDyeColor::White,
|
||||
DyeColor::Orange => InternalDyeColor::Orange,
|
||||
DyeColor::Magenta => InternalDyeColor::Magenta,
|
||||
DyeColor::LightBlue => InternalDyeColor::LightBlue,
|
||||
DyeColor::Yellow => InternalDyeColor::Yellow,
|
||||
DyeColor::Lime => InternalDyeColor::Lime,
|
||||
DyeColor::Pink => InternalDyeColor::Pink,
|
||||
DyeColor::Gray => InternalDyeColor::Gray,
|
||||
DyeColor::LightGray => InternalDyeColor::LightGray,
|
||||
DyeColor::Cyan => InternalDyeColor::Cyan,
|
||||
DyeColor::Purple => InternalDyeColor::Purple,
|
||||
DyeColor::Blue => InternalDyeColor::Blue,
|
||||
DyeColor::Brown => InternalDyeColor::Brown,
|
||||
DyeColor::Green => InternalDyeColor::Green,
|
||||
DyeColor::Red => InternalDyeColor::Red,
|
||||
DyeColor::Black => InternalDyeColor::Black,
|
||||
}
|
||||
}
|
||||
|
||||
impl pumpkin::plugin::block_entity::HostCommandBlockEntity for PluginHostState {
|
||||
const fn to_wasm_dye_color(color: InternalDyeColor) -> DyeColor {
|
||||
match color {
|
||||
InternalDyeColor::White => DyeColor::White,
|
||||
InternalDyeColor::Orange => DyeColor::Orange,
|
||||
InternalDyeColor::Magenta => DyeColor::Magenta,
|
||||
InternalDyeColor::LightBlue => DyeColor::LightBlue,
|
||||
InternalDyeColor::Yellow => DyeColor::Yellow,
|
||||
InternalDyeColor::Lime => DyeColor::Lime,
|
||||
InternalDyeColor::Pink => DyeColor::Pink,
|
||||
InternalDyeColor::Gray => DyeColor::Gray,
|
||||
InternalDyeColor::LightGray => DyeColor::LightGray,
|
||||
InternalDyeColor::Cyan => DyeColor::Cyan,
|
||||
InternalDyeColor::Purple => DyeColor::Purple,
|
||||
InternalDyeColor::Blue => DyeColor::Blue,
|
||||
InternalDyeColor::Brown => DyeColor::Brown,
|
||||
InternalDyeColor::Green => DyeColor::Green,
|
||||
InternalDyeColor::Red => DyeColor::Red,
|
||||
InternalDyeColor::Black => DyeColor::Black,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_wasm_sign_text(text: &InternalText) -> SignText {
|
||||
SignText {
|
||||
messages: text.messages.lock().unwrap().to_vec(),
|
||||
color: to_wasm_dye_color(text.get_color()),
|
||||
has_glowing_text: text
|
||||
.has_glowing_text
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_wasm_sign_text(text: SignText) -> InternalText {
|
||||
let mut messages = [String::new(), String::new(), String::new(), String::new()];
|
||||
for (i, msg) in text.messages.into_iter().take(4).enumerate() {
|
||||
messages[i] = msg;
|
||||
}
|
||||
InternalText::from(pumpkin_nbt::tag::NbtTag::Compound({
|
||||
let mut nbt = pumpkin_nbt::compound::NbtCompound::new();
|
||||
nbt.put_bool("has_glowing_text", text.has_glowing_text);
|
||||
nbt.put_string(
|
||||
"color",
|
||||
InternalDyeColor::from(from_wasm_dye_color(text.color) as i8).into(),
|
||||
);
|
||||
nbt.put_list(
|
||||
"messages",
|
||||
messages
|
||||
.iter()
|
||||
.map(|s| pumpkin_nbt::tag::NbtTag::String(s.clone().into()))
|
||||
.collect(),
|
||||
);
|
||||
nbt
|
||||
}))
|
||||
}
|
||||
|
||||
impl HostBlockEntity for PluginHostState {
|
||||
async fn resource_location(&mut self, res: Resource<BlockEntity>) -> wasmtime::Result<String> {
|
||||
let entity = block_entity_from_resource(self, &res)?;
|
||||
Ok(entity.resource_location().to_string())
|
||||
}
|
||||
|
||||
async fn get_position(&mut self, res: Resource<BlockEntity>) -> wasmtime::Result<WitBlockPos> {
|
||||
let entity = block_entity_from_resource(self, &res)?;
|
||||
let pos = entity.get_position();
|
||||
Ok(WitBlockPos {
|
||||
x: pos.0.x,
|
||||
y: pos.0.y,
|
||||
z: pos.0.z,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_id(&mut self, res: Resource<BlockEntity>) -> wasmtime::Result<u32> {
|
||||
let entity = block_entity_from_resource(self, &res)?;
|
||||
Ok(entity.get_id())
|
||||
}
|
||||
|
||||
async fn is_dirty(&mut self, res: Resource<BlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &res)?;
|
||||
Ok(entity.is_dirty())
|
||||
}
|
||||
|
||||
async fn clear_dirty(&mut self, res: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &res)?;
|
||||
entity.clear_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<BlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HostCommandBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
todo!("Implement upcasting/mapping to BlockEntity")
|
||||
let entity = self
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(res.rep()))
|
||||
.map_err(|_| wasmtime::Error::msg("invalid command block entity resource handle"))?;
|
||||
let provider = entity.provider.clone();
|
||||
self.add_block_entity(provider)
|
||||
}
|
||||
|
||||
async fn last_output(
|
||||
&mut self,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<String> {
|
||||
todo!("Implement last_output lookup")
|
||||
async fn last_output(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
if let Some(cmd) = entity.as_any().downcast_ref::<InternalCommandBlockEntity>() {
|
||||
Ok(cmd.last_output.lock().await.clone())
|
||||
} else {
|
||||
Err(wasmtime::Error::msg("Not a command block entity"))
|
||||
}
|
||||
}
|
||||
|
||||
async fn track_output(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
todo!("Implement track_output lookup")
|
||||
async fn track_output(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a command block entity")),
|
||||
|cmd| Ok(cmd.track_output.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn success_count(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
|
||||
todo!("Implement success_count lookup")
|
||||
async fn success_count(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<u32> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a command block entity")),
|
||||
|cmd| Ok(cmd.success_count.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn command(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
|
||||
todo!("Implement command lookup")
|
||||
async fn command(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<String> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
if let Some(cmd) = entity.as_any().downcast_ref::<InternalCommandBlockEntity>() {
|
||||
Ok(cmd.command.lock().await.clone())
|
||||
} else {
|
||||
Err(wasmtime::Error::msg("Not a command block entity"))
|
||||
}
|
||||
}
|
||||
|
||||
async fn auto(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
todo!("Implement auto lookup")
|
||||
async fn auto(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a command block entity")),
|
||||
|cmd| Ok(cmd.auto.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn condition_met(
|
||||
&mut self,
|
||||
_res: Resource<CommandBlockEntity>,
|
||||
) -> wasmtime::Result<bool> {
|
||||
todo!("Implement condition_met lookup")
|
||||
async fn condition_met(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a command block entity")),
|
||||
|cmd| Ok(cmd.condition_met.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn powered(&mut self, _res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
todo!("Implement powered lookup")
|
||||
async fn powered(&mut self, res: Resource<CommandBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a command block entity")),
|
||||
|cmd| Ok(cmd.powered.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn drop(&mut self, _rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
|
||||
todo!();
|
||||
async fn drop(&mut self, rep: Resource<CommandBlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HostSignBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
let entity = self
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(res.rep()))
|
||||
.map_err(|_| wasmtime::Error::msg("invalid sign block entity resource handle"))?;
|
||||
let provider = entity.provider.clone();
|
||||
self.add_block_entity(provider)
|
||||
}
|
||||
|
||||
async fn get_front_text(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
) -> wasmtime::Result<SignText> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| Ok(to_wasm_sign_text(&sign.front_text)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn set_front_text(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
text: SignText,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| {
|
||||
let new_text = from_wasm_sign_text(text);
|
||||
sign.front_text.has_glowing_text.store(
|
||||
new_text
|
||||
.has_glowing_text
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
sign.front_text.set_color(new_text.get_color());
|
||||
(*sign.front_text.messages.lock().unwrap())
|
||||
.clone_from(&new_text.messages.lock().unwrap());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_back_text(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
) -> wasmtime::Result<SignText> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| Ok(to_wasm_sign_text(&sign.back_text)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn set_back_text(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
text: SignText,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| {
|
||||
let new_text = from_wasm_sign_text(text);
|
||||
sign.back_text.has_glowing_text.store(
|
||||
new_text
|
||||
.has_glowing_text
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
sign.back_text.set_color(new_text.get_color());
|
||||
(*sign.back_text.messages.lock().unwrap())
|
||||
.clone_from(&new_text.messages.lock().unwrap());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn is_waxed(&mut self, res: Resource<SignBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| Ok(sign.is_waxed.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn set_waxed(
|
||||
&mut self,
|
||||
res: Resource<SignBlockEntity>,
|
||||
waxed: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a sign block entity")),
|
||||
|sign| {
|
||||
sign.is_waxed
|
||||
.store(waxed, std::sync::atomic::Ordering::Relaxed);
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<SignBlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HostJukeboxBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
res: Resource<JukeboxBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
let entity = self
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(res.rep()))
|
||||
.map_err(|_| wasmtime::Error::msg("invalid jukebox block entity resource handle"))?;
|
||||
let provider = entity.provider.clone();
|
||||
self.add_block_entity(provider)
|
||||
}
|
||||
|
||||
async fn is_playing(&mut self, res: Resource<JukeboxBlockEntity>) -> wasmtime::Result<bool> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalJukeboxBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a jukebox block entity")),
|
||||
|jukebox| Ok(jukebox.is_playing()),
|
||||
)
|
||||
}
|
||||
|
||||
async fn stop_playing(&mut self, res: Resource<JukeboxBlockEntity>) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalJukeboxBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a jukebox block entity")),
|
||||
|jukebox| {
|
||||
jukebox.stop_playing();
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn start_playing(
|
||||
&mut self,
|
||||
res: Resource<JukeboxBlockEntity>,
|
||||
length_in_ticks: u64,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalJukeboxBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a jukebox block entity")),
|
||||
|jukebox| {
|
||||
jukebox.start_playing(length_in_ticks);
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<JukeboxBlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HostChestBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
res: Resource<ChestBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
let entity = self
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(res.rep()))
|
||||
.map_err(|_| wasmtime::Error::msg("invalid chest block entity resource handle"))?;
|
||||
let provider = entity.provider.clone();
|
||||
self.add_block_entity(provider)
|
||||
}
|
||||
|
||||
async fn viewer_count(&mut self, res: Resource<ChestBlockEntity>) -> wasmtime::Result<u32> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalChestBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a chest block entity")),
|
||||
|chest| Ok(chest.get_viewer_count() as u32),
|
||||
)
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<ChestBlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HostMobSpawnerBlockEntity for PluginHostState {
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
res: Resource<MobSpawnerBlockEntity>,
|
||||
) -> wasmtime::Result<Resource<BlockEntity>> {
|
||||
let entity = self
|
||||
.resource_table
|
||||
.get::<BlockEntityResource>(&Resource::new_own(res.rep()))
|
||||
.map_err(|_| {
|
||||
wasmtime::Error::msg("invalid mob spawner block entity resource handle")
|
||||
})?;
|
||||
let provider = entity.provider.clone();
|
||||
self.add_block_entity(provider)
|
||||
}
|
||||
|
||||
async fn get_spawn_count(
|
||||
&mut self,
|
||||
res: Resource<MobSpawnerBlockEntity>,
|
||||
) -> wasmtime::Result<i32> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalMobSpawnerBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a mob spawner block entity")),
|
||||
|spawner| Ok(spawner.spawn_count),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_spawn_range(
|
||||
&mut self,
|
||||
res: Resource<MobSpawnerBlockEntity>,
|
||||
) -> wasmtime::Result<i32> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalMobSpawnerBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a mob spawner block entity")),
|
||||
|spawner| Ok(spawner.spawn_range),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_delay(&mut self, res: Resource<MobSpawnerBlockEntity>) -> wasmtime::Result<i32> {
|
||||
let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?;
|
||||
entity
|
||||
.as_any()
|
||||
.downcast_ref::<InternalMobSpawnerBlockEntity>()
|
||||
.map_or_else(
|
||||
|| Err(wasmtime::Error::msg("Not a mob spawner block entity")),
|
||||
|spawner| Ok(spawner.delay.load(std::sync::atomic::Ordering::Relaxed)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn drop(&mut self, rep: Resource<MobSpawnerBlockEntity>) -> wasmtime::Result<()> {
|
||||
let _ = self
|
||||
.resource_table
|
||||
.delete::<BlockEntityResource>(Resource::new_own(rep.rep()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ use crate::{
|
||||
Arg, ArgumentType, Command, CommandNode, CommandSender, CommandSenderType,
|
||||
ConsumedArgs, PermissionLevel, StringType,
|
||||
},
|
||||
common::{Locale, Position},
|
||||
common::{BlockPos as WitBlockPos, Locale, Position},
|
||||
player::Player,
|
||||
server::Server,
|
||||
text::TextComponent,
|
||||
@ -149,7 +149,11 @@ impl pumpkin::plugin::command::HostConsumedArgs for PluginHostState {
|
||||
}
|
||||
}))
|
||||
}
|
||||
OwnedArg::BlockPos(p) => Arg::BlockPos((p.0.x, p.0.y, p.0.z)),
|
||||
OwnedArg::BlockPos(p) => Arg::BlockPos(WitBlockPos {
|
||||
x: p.0.x,
|
||||
y: p.0.y,
|
||||
z: p.0.z,
|
||||
}),
|
||||
OwnedArg::Pos3D(v) => Arg::Pos3d((v.x, v.y, v.z)),
|
||||
OwnedArg::Pos2D(v) => Arg::Pos2d((v.x, v.y)),
|
||||
OwnedArg::Rotation(a, b, c, d) => Arg::Rotation((a, b, c, d)),
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::{EntityResource, PluginHostState},
|
||||
wit::v0_1::events::to_wasm_position,
|
||||
@ -11,7 +13,8 @@ use crate::plugin::loader::wasm::wasm_host::{
|
||||
text::TextComponent,
|
||||
uuid::Uuid,
|
||||
world::{
|
||||
BlockPos as WitBlockPos, Entity, HostEntity, RaycastResult as WitRaycastResult, World,
|
||||
BlockPos as WitBlockPos, BoundingBox as WitBoundingBox, Entity, HostEntity,
|
||||
RaycastResult as WitRaycastResult, World,
|
||||
},
|
||||
},
|
||||
wit::v0_1::uuid::UuidExt,
|
||||
@ -283,8 +286,9 @@ impl HostEntity for PluginHostState {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.has_visual_fire
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
.fire_ticks
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
> 0)
|
||||
}
|
||||
|
||||
async fn set_on_fire(
|
||||
@ -410,6 +414,370 @@ impl HostEntity for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_health(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_living_entity()
|
||||
.map_or(0.0, |living| living.health.load()))
|
||||
}
|
||||
|
||||
async fn set_health(&mut self, entity: Resource<Entity>, health: f32) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(living) = entity.get_living_entity() {
|
||||
living.health.store(health);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_max_health(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_living_entity()
|
||||
.map_or(0.0, crate::entity::living::LivingEntity::get_max_health))
|
||||
}
|
||||
|
||||
async fn damage(&mut self, entity: Resource<Entity>, amount: f32) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity
|
||||
.damage(&*entity, amount, pumpkin_data::damage::DamageType::GENERIC)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_dead(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_living_entity().map_or_else(
|
||||
|| entity.get_entity().removal_reason.load().is_some(),
|
||||
|living| living.dead.load(std::sync::atomic::Ordering::Relaxed),
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_absorption(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_living_entity()
|
||||
.map_or(0.0, |living| living.absorption.load()))
|
||||
}
|
||||
|
||||
async fn set_absorption(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
amount: f32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(living) = entity.get_living_entity() {
|
||||
living.absorption.store(amount);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_age(&mut self, entity: Resource<Entity>) -> wasmtime::Result<i32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.age
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn set_age(&mut self, entity: Resource<Entity>, age: i32) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity
|
||||
.get_entity()
|
||||
.age
|
||||
.store(age, std::sync::atomic::Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_fall_distance(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_living_entity()
|
||||
.map_or(0.0, |living| living.fall_distance.load()))
|
||||
}
|
||||
|
||||
async fn set_fall_distance(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
distance: f32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(living) = entity.get_living_entity() {
|
||||
living.fall_distance.store(distance);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_eye_height(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_entity().entity_dimension.load().eye_height)
|
||||
}
|
||||
|
||||
async fn get_eye_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(to_wasm_position(entity.get_eye_pos()))
|
||||
}
|
||||
|
||||
async fn get_nearby_entities(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
) -> wasmtime::Result<Vec<Resource<Entity>>> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let pos = entity.get_entity().pos.load();
|
||||
let box_range = pumpkin_util::math::boundingbox::BoundingBox::new(
|
||||
Vector3::new(pos.x - x, pos.y - y, pos.z - z),
|
||||
Vector3::new(pos.x + x, pos.y + y, pos.z + z),
|
||||
);
|
||||
let world = entity.get_entity().world.load_full();
|
||||
let entities = world.get_entities_at_box(&box_range);
|
||||
|
||||
let mut result = Vec::new();
|
||||
for e in entities {
|
||||
// Don't include the entity itself
|
||||
if e.get_entity().entity_id != entity.get_entity().entity_id {
|
||||
result.push(
|
||||
self.add_entity(e)
|
||||
.map_err(|_| wasmtime::Error::msg("failed to add entity resource"))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn get_vehicle(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
) -> wasmtime::Result<Option<Resource<Entity>>> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let vehicle = entity.get_entity().vehicle.lock().await;
|
||||
if let Some(v) = vehicle.as_ref() {
|
||||
Ok(Some(self.add_entity(Arc::clone(v)).map_err(|_| {
|
||||
wasmtime::Error::msg("failed to add entity resource")
|
||||
})?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_passengers(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
) -> wasmtime::Result<Vec<Resource<Entity>>> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let passengers = entity.get_entity().passengers.lock().await;
|
||||
let mut result = Vec::new();
|
||||
for p in passengers.iter() {
|
||||
result.push(
|
||||
self.add_entity(Arc::clone(p))
|
||||
.map_err(|_| wasmtime::Error::msg("failed to add entity resource"))?,
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn add_passenger(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
passenger: Resource<Entity>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let passenger = entity_from_resource(self, &passenger)?;
|
||||
entity
|
||||
.get_entity()
|
||||
.add_passenger(Arc::clone(&entity), passenger)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_passenger(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
passenger: Resource<Entity>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let passenger = entity_from_resource(self, &passenger)?;
|
||||
entity
|
||||
.get_entity()
|
||||
.remove_passenger(passenger.get_entity().entity_id)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn eject_passengers(&mut self, entity: Resource<Entity>) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let ids: Vec<i32> = entity
|
||||
.get_entity()
|
||||
.passengers
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.map(|p| p.get_entity().entity_id)
|
||||
.collect();
|
||||
for id in ids {
|
||||
entity.get_entity().remove_passenger(id).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_bounding_box(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
) -> wasmtime::Result<WitBoundingBox> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let bb = entity.get_entity().bounding_box.load();
|
||||
Ok(WitBoundingBox {
|
||||
min: to_wasm_position(bb.min),
|
||||
max: to_wasm_position(bb.max),
|
||||
})
|
||||
}
|
||||
|
||||
async fn is_in_water(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.touching_water
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn is_in_lava(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.touching_lava
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn get_ticks_lived(&mut self, entity: Resource<Entity>) -> wasmtime::Result<i32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.age
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn set_ticks_lived(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
ticks: i32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity
|
||||
.get_entity()
|
||||
.age
|
||||
.store(ticks, std::sync::atomic::Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_width(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_entity().entity_dimension.load().width)
|
||||
}
|
||||
|
||||
async fn get_height(&mut self, entity: Resource<Entity>) -> wasmtime::Result<f32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_entity().entity_dimension.load().height)
|
||||
}
|
||||
|
||||
async fn set_rotation(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity.get_entity().set_rotation(yaw, pitch);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn has_visual_fire(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.has_visual_fire
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn set_visual_fire(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
visual_fire: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity.get_entity().set_on_fire(visual_fire).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_portal_cooldown(&mut self, entity: Resource<Entity>) -> wasmtime::Result<u32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_entity()
|
||||
.portal_cooldown
|
||||
.load(std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
async fn set_portal_cooldown(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
cooldown: u32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity
|
||||
.get_entity()
|
||||
.portal_cooldown
|
||||
.store(cooldown, std::sync::atomic::Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_remaining_air(&mut self, entity: Resource<Entity>) -> wasmtime::Result<i32> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_player().map_or(0, |player| {
|
||||
player
|
||||
.breath_manager
|
||||
.air_supply
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}))
|
||||
}
|
||||
|
||||
async fn set_remaining_air(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
air: i32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(player) = entity.get_player() {
|
||||
player
|
||||
.breath_manager
|
||||
.air_supply
|
||||
.store(air, std::sync::atomic::Ordering::Relaxed);
|
||||
player.breath_manager.send_air_supply(player);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_max_air(&mut self, _entity: Resource<Entity>) -> wasmtime::Result<i32> {
|
||||
Ok(crate::entity::breath::MAX_AIR)
|
||||
}
|
||||
|
||||
async fn send_system_message(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
message: Resource<TextComponent>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(player) = entity.get_player() {
|
||||
let text_res = self
|
||||
.resource_table
|
||||
.get::<crate::plugin::loader::wasm::wasm_host::state::TextComponentResource>(
|
||||
&Resource::new_own(message.rep()),
|
||||
)
|
||||
.map_err(|_| wasmtime::Error::msg("invalid text component resource handle"))?;
|
||||
player.send_system_message(&text_res.provider).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove(&mut self, entity: Resource<Entity>) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity.get_entity().remove().await;
|
||||
|
||||
@ -28,7 +28,7 @@ impl ToFromWasmEvent for BlockRedstoneEvent {
|
||||
Event::BlockRedstoneEvent(BlockRedstoneEventData {
|
||||
target_world,
|
||||
state_id: self.block_state_id,
|
||||
block_position: to_wasm_block_position(self.block_pos),
|
||||
block_pos: to_wasm_block_position(self.block_pos),
|
||||
old_current: self.old_current,
|
||||
new_current: self.new_current,
|
||||
cancelled: self.cancelled,
|
||||
@ -40,7 +40,7 @@ impl ToFromWasmEvent for BlockRedstoneEvent {
|
||||
Event::BlockRedstoneEvent(data) => Self {
|
||||
world: consume_world(state, &data.target_world),
|
||||
block_state_id: data.state_id,
|
||||
block_pos: from_wasm_block_position(data.block_position),
|
||||
block_pos: from_wasm_block_position(data.block_pos),
|
||||
old_current: data.old_current,
|
||||
new_current: data.new_current,
|
||||
cancelled: data.cancelled,
|
||||
@ -61,7 +61,7 @@ impl ToFromWasmEvent for BlockBreakEvent {
|
||||
Event::BlockBreakEvent(BlockBreakEventData {
|
||||
player,
|
||||
block: to_wasm_block_name(self.block),
|
||||
block_position: to_wasm_block_position(self.block_position),
|
||||
block_pos: to_wasm_block_position(self.block_position),
|
||||
exp: self.exp,
|
||||
should_drop: self.drop,
|
||||
cancelled: self.cancelled,
|
||||
@ -73,7 +73,7 @@ impl ToFromWasmEvent for BlockBreakEvent {
|
||||
Event::BlockBreakEvent(data) => Self {
|
||||
player: data.player.map(|player| consume_player(state, &player)),
|
||||
block: from_wasm_block_name(&data.block),
|
||||
block_position: from_wasm_block_position(data.block_position),
|
||||
block_position: from_wasm_block_position(data.block_pos),
|
||||
exp: data.exp,
|
||||
drop: data.should_drop,
|
||||
cancelled: data.cancelled,
|
||||
@ -145,7 +145,7 @@ impl ToFromWasmEvent for BlockGrowEvent {
|
||||
old_state_id: self.old_state_id,
|
||||
new_block: to_wasm_block_name(self.new_block),
|
||||
new_state_id: self.new_state_id,
|
||||
block_position: to_wasm_block_position(self.block_pos),
|
||||
block_pos: to_wasm_block_position(self.block_pos),
|
||||
cancelled: self.cancelled,
|
||||
})
|
||||
}
|
||||
@ -158,7 +158,7 @@ impl ToFromWasmEvent for BlockGrowEvent {
|
||||
old_state_id: data.old_state_id,
|
||||
new_block: from_wasm_block_name(&data.new_block),
|
||||
new_state_id: data.new_state_id,
|
||||
block_pos: from_wasm_block_position(data.block_position),
|
||||
block_pos: from_wasm_block_position(data.block_pos),
|
||||
cancelled: data.cancelled,
|
||||
},
|
||||
_ => panic!("unexpected event type"),
|
||||
@ -176,7 +176,7 @@ impl ToFromWasmEvent for BlockPlaceEvent {
|
||||
player,
|
||||
block_placed: to_wasm_block_name(self.block_placed),
|
||||
block_placed_against: to_wasm_block_name(self.block_placed_against),
|
||||
block_position: to_wasm_block_position(self.block_position),
|
||||
block_pos: to_wasm_block_position(self.block_position),
|
||||
can_build: self.can_build,
|
||||
cancelled: self.cancelled,
|
||||
})
|
||||
@ -188,7 +188,7 @@ impl ToFromWasmEvent for BlockPlaceEvent {
|
||||
player: consume_player(state, &data.player),
|
||||
block_placed: from_wasm_block_name(&data.block_placed),
|
||||
block_placed_against: from_wasm_block_name(&data.block_placed_against),
|
||||
block_position: from_wasm_block_position(data.block_position),
|
||||
block_position: from_wasm_block_position(data.block_pos),
|
||||
can_build: data.can_build,
|
||||
cancelled: data.cancelled,
|
||||
},
|
||||
|
||||
@ -59,14 +59,18 @@ pub(super) const fn from_wasm_position(
|
||||
|
||||
pub(super) const fn to_wasm_block_position(
|
||||
position: BlockPos,
|
||||
) -> pumpkin::plugin::common::BlockPosition {
|
||||
(position.0.x, position.0.y, position.0.z)
|
||||
) -> pumpkin::plugin::common::BlockPos {
|
||||
pumpkin::plugin::common::BlockPos {
|
||||
x: position.0.x,
|
||||
y: position.0.y,
|
||||
z: position.0.z,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) const fn from_wasm_block_position(
|
||||
position: pumpkin::plugin::common::BlockPosition,
|
||||
position: pumpkin::plugin::common::BlockPos,
|
||||
) -> BlockPos {
|
||||
BlockPos::new(position.0, position.1, position.2)
|
||||
BlockPos::new(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
pub(super) fn to_wasm_block_name(block: &'static Block) -> String {
|
||||
|
||||
@ -5,10 +5,11 @@ use crate::plugin::{
|
||||
wit::v0_1::{
|
||||
events::{
|
||||
ToFromWasmEvent, consume_player, consume_text_component, consume_world,
|
||||
from_wasm_click_type, from_wasm_entity_interaction_action, from_wasm_entity_type,
|
||||
from_wasm_game_mode, from_wasm_hand, from_wasm_position, to_wasm_block_position,
|
||||
to_wasm_click_type, to_wasm_entity_interaction_action, to_wasm_entity_type,
|
||||
to_wasm_game_mode, to_wasm_hand, to_wasm_position,
|
||||
from_wasm_block_name, from_wasm_block_position, from_wasm_click_type,
|
||||
from_wasm_entity_interaction_action, from_wasm_entity_type, from_wasm_game_mode,
|
||||
from_wasm_hand, from_wasm_position, to_wasm_block_position, to_wasm_click_type,
|
||||
to_wasm_entity_interaction_action, to_wasm_entity_type, to_wasm_game_mode,
|
||||
to_wasm_hand, to_wasm_position,
|
||||
},
|
||||
pumpkin::plugin::event::{
|
||||
BedrockFormResponseEventData, CustomClickActionEventData, Event,
|
||||
@ -647,6 +648,15 @@ const fn to_wasm_interact_action(action: &InteractAction) -> WasmInteractAction
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_wasm_interact_action(action: WasmInteractAction) -> InteractAction {
|
||||
match action {
|
||||
WasmInteractAction::LeftClickBlock => InteractAction::LeftClickBlock,
|
||||
WasmInteractAction::LeftClickAir => InteractAction::LeftClickAir,
|
||||
WasmInteractAction::RightClickAir => InteractAction::RightClickAir,
|
||||
WasmInteractAction::RightClickBlock => InteractAction::RightClickBlock,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToFromWasmEvent for PlayerInteractEvent {
|
||||
fn to_wasm_event(&self, state: &mut PluginHostState) -> Event {
|
||||
let player = state
|
||||
@ -662,11 +672,15 @@ impl ToFromWasmEvent for PlayerInteractEvent {
|
||||
})
|
||||
}
|
||||
|
||||
fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self {
|
||||
fn from_wasm_event(event: Event, state: &mut PluginHostState) -> Self {
|
||||
match event {
|
||||
Event::PlayerInteractEvent(_data) => {
|
||||
panic!("from_wasm_event for PlayerInteractEvent is not supported")
|
||||
}
|
||||
Event::PlayerInteractEvent(data) => Self {
|
||||
player: consume_player(state, &data.player),
|
||||
action: from_wasm_interact_action(data.action),
|
||||
clicked_pos: data.clicked_pos.map(from_wasm_block_position),
|
||||
block: from_wasm_block_name(&data.block),
|
||||
cancelled: data.cancelled,
|
||||
},
|
||||
_ => panic!("unexpected event type"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,14 @@ use pumpkin_world::world::BlockFlags;
|
||||
use std::sync::Arc;
|
||||
use wasmtime::component::Resource;
|
||||
|
||||
use crate::block::entities::chest::ChestBlockEntity as InternalChestBlockEntity;
|
||||
use crate::block::entities::command_block::CommandBlockEntity as InternalCommandBlockEntity;
|
||||
use crate::block::entities::jukebox::JukeboxBlockEntity as InternalJukeboxBlockEntity;
|
||||
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity as InternalMobSpawnerBlockEntity;
|
||||
use crate::block::entities::sign::SignBlockEntity as InternalSignBlockEntity;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
|
||||
BlockDirection as WitBlockDirection, BlockFlags as WitBlockFlags, BlockPos as WitBlockPos,
|
||||
BlockState as WitBlockState, PistonBehavior as WitPistonBehavior,
|
||||
BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags,
|
||||
BlockPos as WitBlockPos, BlockState as WitBlockState, PistonBehavior as WitPistonBehavior,
|
||||
};
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::{PluginHostState, TextComponentResource, WorldResource},
|
||||
@ -486,6 +491,69 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
||||
Ok(entities)
|
||||
}
|
||||
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitBlockPos,
|
||||
) -> wasmtime::Result<Option<BlockEntityType>> {
|
||||
let world_provider = self.get_world_res(&world)?.provider.clone();
|
||||
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||
let block_entity = world_provider.get_block_entity(&internal_pos);
|
||||
|
||||
if let Some(be) = block_entity {
|
||||
if be
|
||||
.as_any()
|
||||
.downcast_ref::<InternalCommandBlockEntity>()
|
||||
.is_some()
|
||||
{
|
||||
let res: Resource<BlockEntity> = self.add_block_entity(be)?;
|
||||
Ok(Some(BlockEntityType::CommandBlockEntity(
|
||||
Resource::new_own(res.rep()),
|
||||
)))
|
||||
} else if be
|
||||
.as_any()
|
||||
.downcast_ref::<InternalSignBlockEntity>()
|
||||
.is_some()
|
||||
{
|
||||
let res: Resource<BlockEntity> = self.add_block_entity(be)?;
|
||||
Ok(Some(BlockEntityType::SignBlockEntity(Resource::new_own(
|
||||
res.rep(),
|
||||
))))
|
||||
} else if be
|
||||
.as_any()
|
||||
.downcast_ref::<InternalJukeboxBlockEntity>()
|
||||
.is_some()
|
||||
{
|
||||
let res: Resource<BlockEntity> = self.add_block_entity(be)?;
|
||||
Ok(Some(BlockEntityType::JukeboxBlockEntity(
|
||||
Resource::new_own(res.rep()),
|
||||
)))
|
||||
} else if be
|
||||
.as_any()
|
||||
.downcast_ref::<InternalChestBlockEntity>()
|
||||
.is_some()
|
||||
{
|
||||
let res: Resource<BlockEntity> = self.add_block_entity(be)?;
|
||||
Ok(Some(BlockEntityType::ChestBlockEntity(Resource::new_own(
|
||||
res.rep(),
|
||||
))))
|
||||
} else if be
|
||||
.as_any()
|
||||
.downcast_ref::<InternalMobSpawnerBlockEntity>()
|
||||
.is_some()
|
||||
{
|
||||
let res: Resource<BlockEntity> = self.add_block_entity(be)?;
|
||||
Ok(Some(BlockEntityType::MobSpawnerBlockEntity(
|
||||
Resource::new_own(res.rep()),
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
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