fix: entity spawn height
This commit is contained in:
parent
7f21c3cfe2
commit
6ea869c4a0
@ -773,7 +773,7 @@ pub trait ScreenHandler: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cancels any client-side changes and resynchronizes the state.
|
/// Cancels any client-side changes and resynchronizes the state.
|
||||||
fn cancel<'a>(&'a mut self, _player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
fn cancel(&mut self) -> ScreenHandlerFuture<'_, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.sync_state().await;
|
self.sync_state().await;
|
||||||
})
|
})
|
||||||
|
|||||||
@ -25,6 +25,25 @@ interface player {
|
|||||||
walk-speed: f32,
|
walk-speed: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a player's skin textures and metadata.
|
||||||
|
record player-skin {
|
||||||
|
/// The base64-encoded texture data (usually JSON containing skin/cape URLs).
|
||||||
|
value: string,
|
||||||
|
/// The optional signature for the texture data.
|
||||||
|
signature: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flags representing visible skin parts.
|
||||||
|
flags skin-parts {
|
||||||
|
cape,
|
||||||
|
jacket,
|
||||||
|
left-sleeve,
|
||||||
|
right-sleeve,
|
||||||
|
left-pants-leg,
|
||||||
|
right-pants-leg,
|
||||||
|
hat,
|
||||||
|
}
|
||||||
|
|
||||||
/// A handle to a player on the server.
|
/// A handle to a player on the server.
|
||||||
resource player {
|
resource player {
|
||||||
/// Returns the player as an entity.
|
/// Returns the player as an entity.
|
||||||
@ -162,36 +181,6 @@ interface player {
|
|||||||
/// Adds experience points to the player.
|
/// Adds experience points to the player.
|
||||||
add-experience-points: func(points: s32);
|
add-experience-points: func(points: s32);
|
||||||
|
|
||||||
/// Returns whether the player is currently sneaking.
|
|
||||||
is-sneaking: func() -> bool;
|
|
||||||
/// Sets whether the player is currently sneaking.
|
|
||||||
set-sneaking: func(sneaking: bool);
|
|
||||||
/// Returns whether the player is currently sprinting.
|
|
||||||
is-sprinting: func() -> bool;
|
|
||||||
/// Sets whether the player is currently sprinting.
|
|
||||||
set-sprinting: func(sprinting: bool);
|
|
||||||
/// Returns whether the player is currently swimming.
|
|
||||||
is-swimming: func() -> bool;
|
|
||||||
/// Sets whether the player is currently swimming.
|
|
||||||
set-swimming: func(swimming: bool);
|
|
||||||
/// Returns whether the player is currently invisible.
|
|
||||||
is-invisible: func() -> bool;
|
|
||||||
/// Sets whether the player is currently invisible.
|
|
||||||
set-invisible: func(invisible: bool);
|
|
||||||
/// Returns whether the player is currently glowing.
|
|
||||||
is-glowing: func() -> bool;
|
|
||||||
/// Sets whether the player is currently glowing.
|
|
||||||
set-glowing: func(glowing: bool);
|
|
||||||
/// Returns whether the player is currently flying due to a fall.
|
|
||||||
is-fall-flying: func() -> bool;
|
|
||||||
/// Sets whether the player is currently flying due to a fall.
|
|
||||||
set-fall-flying: func(fall-flying: bool);
|
|
||||||
/// Returns whether the player is currently on fire. Note: this is only visually like fire, there is no actual logic.
|
|
||||||
is-on-fire: func() -> bool;
|
|
||||||
/// Sets whether the player is visually on fire. Note: this is only visually like fire, there is no actual logic.
|
|
||||||
set-on-fire: func(on-fire: bool);
|
|
||||||
/// Returns whether the player is currently on the ground.
|
|
||||||
is-on-ground: func() -> bool;
|
|
||||||
/// Returns whether the player is currently flying.
|
/// Returns whether the player is currently flying.
|
||||||
is-flying: func() -> bool;
|
is-flying: func() -> bool;
|
||||||
/// Sets whether the player should be flying.
|
/// Sets whether the player should be flying.
|
||||||
@ -204,5 +193,13 @@ interface player {
|
|||||||
|
|
||||||
/// Returns the player's IP address.
|
/// Returns the player's IP address.
|
||||||
get-ip: func() -> string;
|
get-ip: func() -> string;
|
||||||
|
|
||||||
|
/// Returns the player's current skin textures.
|
||||||
|
get-skin: func() -> option<player-skin>;
|
||||||
|
|
||||||
|
/// Returns the currently visible skin parts.
|
||||||
|
get-skin-parts: func() -> skin-parts;
|
||||||
|
/// Sets which skin parts are visible.
|
||||||
|
set-skin-parts: func(parts: skin-parts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,18 @@ interface server {
|
|||||||
hard,
|
hard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the three main Minecraft dimensions.
|
||||||
|
enum dimension {
|
||||||
|
overworld,
|
||||||
|
nether,
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
variant command-sender {
|
||||||
|
console,
|
||||||
|
player(player),
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the global server instance.
|
/// Represents the global server instance.
|
||||||
resource server {
|
resource server {
|
||||||
/// Returns the current difficulty level of the server.
|
/// Returns the current difficulty level of the server.
|
||||||
@ -47,6 +59,12 @@ interface server {
|
|||||||
/// Gets a world by its dimension name (e.g., "minecraft:overworld").
|
/// Gets a world by its dimension name (e.g., "minecraft:overworld").
|
||||||
get-world-by-name: func(name: string) -> option<%world>;
|
get-world-by-name: func(name: string) -> option<%world>;
|
||||||
|
|
||||||
|
execute-command: func(command: string, sender: command-sender);
|
||||||
|
|
||||||
|
/// Creates or loads a world by its name and dimension.
|
||||||
|
/// If a world with the given name already exists, it will be returned.
|
||||||
|
create-world: func(name: string, dimension: dimension) -> %world;
|
||||||
|
|
||||||
/// Broadcasts a system chat message to all players.
|
/// Broadcasts a system chat message to all players.
|
||||||
broadcast: func(message: string);
|
broadcast: func(message: string);
|
||||||
|
|
||||||
|
|||||||
@ -114,10 +114,27 @@ interface %world {
|
|||||||
get-head-yaw: func() -> f32;
|
get-head-yaw: func() -> f32;
|
||||||
|
|
||||||
is-on-ground: func() -> bool;
|
is-on-ground: func() -> bool;
|
||||||
|
|
||||||
is-sneaking: func() -> bool;
|
is-sneaking: func() -> bool;
|
||||||
|
set-sneaking: func(sneaking: bool);
|
||||||
|
|
||||||
is-sprinting: func() -> bool;
|
is-sprinting: func() -> bool;
|
||||||
|
set-sprinting: func(sprinting: bool);
|
||||||
|
|
||||||
|
is-swimming: func() -> bool;
|
||||||
|
set-swimming: func(swimming: bool);
|
||||||
|
|
||||||
is-invisible: func() -> bool;
|
is-invisible: func() -> bool;
|
||||||
|
set-invisible: func(invisible: bool);
|
||||||
|
|
||||||
is-glowing: func() -> 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);
|
teleport: func(pos: position, world-ref: %world);
|
||||||
set-velocity: func(velocity: position);
|
set-velocity: func(velocity: position);
|
||||||
@ -137,6 +154,27 @@ interface %world {
|
|||||||
set-fire-ticks: func(ticks: s32);
|
set-fire-ticks: func(ticks: s32);
|
||||||
|
|
||||||
remove: func();
|
remove: func();
|
||||||
|
|
||||||
|
/// Performs a raycast from the entity's eye position in its looking direction.
|
||||||
|
raycast: func(max-distance: f64, fluid-handling: bool) -> option<raycast-result>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of a raycast operation.
|
||||||
|
record raycast-result {
|
||||||
|
/// The block position that was hit.
|
||||||
|
pos: block-pos,
|
||||||
|
/// The face of the block that was hit.
|
||||||
|
face: block-direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a cardinal direction or block face.
|
||||||
|
enum block-direction {
|
||||||
|
down,
|
||||||
|
up,
|
||||||
|
north,
|
||||||
|
south,
|
||||||
|
west,
|
||||||
|
east,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a Minecraft world (dimension).
|
/// Represents a Minecraft world (dimension).
|
||||||
|
|||||||
@ -106,6 +106,21 @@ impl<T> Vector3<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Vector3<f64> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_yaw_pitch(yaw: f32, pitch: f32) -> Self {
|
||||||
|
let yaw_rad = f64::from(yaw).to_radians();
|
||||||
|
let pitch_rad = f64::from(pitch).to_radians();
|
||||||
|
|
||||||
|
let cos_pitch = pitch_rad.cos();
|
||||||
|
let sin_pitch = pitch_rad.sin();
|
||||||
|
let cos_yaw = yaw_rad.cos();
|
||||||
|
let sin_yaw = yaw_rad.sin();
|
||||||
|
|
||||||
|
Self::new(-cos_pitch * sin_yaw, -sin_pitch, cos_pitch * cos_yaw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||||
/// Calculates the squared length (magnitude) of the vector.
|
/// Calculates the squared length (magnitude) of the vector.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -136,6 +136,15 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_eye_pos(&self) -> Vector3<f64> {
|
||||||
|
self.get_entity().get_eye_pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_looking_vector(&self) -> Vector3<f64> {
|
||||||
|
let entity = self.get_entity();
|
||||||
|
Vector3::from_yaw_pitch(entity.yaw.load(), entity.pitch.load())
|
||||||
|
}
|
||||||
|
|
||||||
fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let entity = self.get_entity();
|
let entity = self.get_entity();
|
||||||
|
|||||||
@ -88,7 +88,6 @@ use crate::entity::{EntityBaseFuture, NbtFuture, TeleportFuture};
|
|||||||
use crate::net::{ClientPlatform, GameProfile};
|
use crate::net::{ClientPlatform, GameProfile};
|
||||||
use crate::net::{DisconnectReason, PlayerConfig};
|
use crate::net::{DisconnectReason, PlayerConfig};
|
||||||
use crate::plugin::player::exp_change::PlayerExpChangeEvent;
|
use crate::plugin::player::exp_change::PlayerExpChangeEvent;
|
||||||
use crate::plugin::player::inventory_close::InventoryCloseEvent;
|
|
||||||
use crate::plugin::player::inventory_interact::InventoryClickEvent;
|
use crate::plugin::player::inventory_interact::InventoryClickEvent;
|
||||||
use crate::plugin::player::player_change_world::PlayerChangeWorldEvent;
|
use crate::plugin::player::player_change_world::PlayerChangeWorldEvent;
|
||||||
use crate::plugin::player::player_gamemode_change::PlayerGamemodeChangeEvent;
|
use crate::plugin::player::player_gamemode_change::PlayerGamemodeChangeEvent;
|
||||||
@ -466,14 +465,9 @@ pub struct Player {
|
|||||||
pub tab_list_order: AtomicI32,
|
pub tab_list_order: AtomicI32,
|
||||||
pub tab_list_latency: AtomicI32,
|
pub tab_list_latency: AtomicI32,
|
||||||
pub tab_list_listed: AtomicBool,
|
pub tab_list_listed: AtomicBool,
|
||||||
pub this: Weak<Self>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn this(&self) -> Arc<Self> {
|
|
||||||
self.this.upgrade().expect("Player should have a reference")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
client: ClientPlatform,
|
client: ClientPlatform,
|
||||||
@ -603,7 +597,6 @@ impl Player {
|
|||||||
tab_list_order: AtomicI32::new(0),
|
tab_list_order: AtomicI32::new(0),
|
||||||
tab_list_latency: AtomicI32::new(0),
|
tab_list_latency: AtomicI32::new(0),
|
||||||
tab_list_listed: AtomicBool::new(false),
|
tab_list_listed: AtomicBool::new(false),
|
||||||
this: Weak::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2897,20 +2890,21 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_handled_screen_closed(&self) {
|
pub async fn on_handled_screen_closed(&self) {
|
||||||
let window_type = {
|
// let window_type = {
|
||||||
let handler_lock = self.current_screen_handler.lock().await;
|
// let handler_lock = self.current_screen_handler.lock().await;
|
||||||
let mut handler = handler_lock.lock().await;
|
// let mut handler = handler_lock.lock().await;
|
||||||
let wt = handler.window_type();
|
// let wt = handler.window_type();
|
||||||
handler.on_closed(self).await;
|
// handler.on_closed(self).await;
|
||||||
wt
|
// wt
|
||||||
};
|
// };
|
||||||
|
|
||||||
if let Some(server) = self.living_entity.entity.world.load().server.upgrade() {
|
// TODO
|
||||||
server
|
// if let Some(server) = self.living_entity.entity.world.load().server.upgrade() {
|
||||||
.plugin_manager
|
// server
|
||||||
.fire(InventoryCloseEvent::new(&self.this(), window_type))
|
// .plugin_manager
|
||||||
.await;
|
// .fire(InventoryCloseEvent::new(&self, window_type))
|
||||||
}
|
// .await;
|
||||||
|
// }
|
||||||
|
|
||||||
let player_screen_handler: Arc<Mutex<dyn ScreenHandler>> =
|
let player_screen_handler: Arc<Mutex<dyn ScreenHandler>> =
|
||||||
self.player_screen_handler.clone();
|
self.player_screen_handler.clone();
|
||||||
@ -3026,7 +3020,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn on_slot_click(&self, packet: SClickSlot, server: &Server) {
|
pub async fn on_slot_click(self: &Arc<Self>, packet: SClickSlot, server: &Server) {
|
||||||
self.update_last_action_time();
|
self.update_last_action_time();
|
||||||
let screen_handler_arc = self.current_screen_handler.lock().await.clone();
|
let screen_handler_arc = self.current_screen_handler.lock().await.clone();
|
||||||
let mut screen_handler = screen_handler_arc.lock().await;
|
let mut screen_handler = screen_handler_arc.lock().await;
|
||||||
@ -3050,7 +3044,7 @@ impl Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !screen_handler.can_use(self) {
|
if !screen_handler.can_use(self.as_ref()) {
|
||||||
warn!(
|
warn!(
|
||||||
"Player {} interacted with invalid menu {:?}",
|
"Player {} interacted with invalid menu {:?}",
|
||||||
self.gameprofile.name,
|
self.gameprofile.name,
|
||||||
@ -3133,7 +3127,7 @@ impl Player {
|
|||||||
send_cancellable! {{
|
send_cancellable! {{
|
||||||
server;
|
server;
|
||||||
InventoryClickEvent::new(
|
InventoryClickEvent::new(
|
||||||
&self.this(),
|
self,
|
||||||
screen_handler.window_type(),
|
screen_handler.window_type(),
|
||||||
click_type,
|
click_type,
|
||||||
slot,
|
slot,
|
||||||
@ -3144,7 +3138,7 @@ impl Player {
|
|||||||
);
|
);
|
||||||
'after: {}
|
'after: {}
|
||||||
'cancelled: {
|
'cancelled: {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -3158,48 +3152,48 @@ impl Player {
|
|||||||
if is_container_slot {
|
if is_container_slot {
|
||||||
if !cursor_stack.is_empty() && !allow_put_items {
|
if !cursor_stack.is_empty() && !allow_put_items {
|
||||||
drop(cursor_stack);
|
drop(cursor_stack);
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if cursor_stack.is_empty() && !allow_grab_items {
|
if cursor_stack.is_empty() && !allow_grab_items {
|
||||||
drop(cursor_stack);
|
drop(cursor_stack);
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlotActionType::QuickMove => {
|
SlotActionType::QuickMove => {
|
||||||
if is_container_slot && !allow_grab_items {
|
if is_container_slot && !allow_grab_items {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if !is_container_slot && !allow_put_items {
|
if !is_container_slot && !allow_put_items {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlotActionType::Swap => {
|
SlotActionType::Swap => {
|
||||||
if is_container_slot && (!allow_grab_items || !allow_put_items) {
|
if is_container_slot && (!allow_grab_items || !allow_put_items) {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlotActionType::Throw => {
|
SlotActionType::Throw => {
|
||||||
if is_container_slot && !allow_grab_items {
|
if is_container_slot && !allow_grab_items {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlotActionType::QuickCraft => {
|
SlotActionType::QuickCraft => {
|
||||||
if !allow_put_items {
|
if !allow_put_items {
|
||||||
// Dragging items into slots
|
// Dragging items into slots
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlotActionType::PickupAll => {
|
SlotActionType::PickupAll => {
|
||||||
if !allow_grab_items {
|
if !allow_grab_items {
|
||||||
screen_handler.cancel(self).await;
|
screen_handler.cancel().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3218,7 +3212,7 @@ impl Player {
|
|||||||
i32::from(slot),
|
i32::from(slot),
|
||||||
i32::from(packet.button),
|
i32::from(packet.button),
|
||||||
packet.mode.clone(),
|
packet.mode.clone(),
|
||||||
self,
|
self.as_ref(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
@ -7,8 +8,11 @@ use crate::plugin::loader::wasm::wasm_host::{
|
|||||||
common::{EntityPose, Position},
|
common::{EntityPose, Position},
|
||||||
entity::Host,
|
entity::Host,
|
||||||
text::TextComponent,
|
text::TextComponent,
|
||||||
world::{Entity, HostEntity, World},
|
world::{
|
||||||
|
BlockPos as WitBlockPos, Entity, HostEntity, RaycastResult as WitRaycastResult, World,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
wit::v0_1::world::to_wasm_block_direction,
|
||||||
};
|
};
|
||||||
use pumpkin_data::entity::EntityPose as InternalEntityPose;
|
use pumpkin_data::entity::EntityPose as InternalEntityPose;
|
||||||
|
|
||||||
@ -176,6 +180,100 @@ impl HostEntity for PluginHostState {
|
|||||||
Ok(to_wasm_position(entity.get_entity().velocity.load()))
|
Ok(to_wasm_position(entity.get_entity().velocity.load()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_sneaking(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
sneaking: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_sneaking(sneaking).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_sprinting(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
sprinting: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_sprinting(sprinting).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_swimming(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.swimming
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_swimming(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
swimming: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_swimming(swimming).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_invisible(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
invisible: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_invisible(invisible).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_glowing(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
glowing: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_glowing(glowing).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_fall_flying(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
Ok(entity
|
||||||
|
.get_entity()
|
||||||
|
.fall_flying
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_fall_flying(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
fall_flying: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_fall_flying(fall_flying).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_on_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_on_fire(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
on_fire: bool,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
entity.get_entity().set_on_fire(on_fire).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_pose(&mut self, entity: Resource<Entity>) -> wasmtime::Result<EntityPose> {
|
async fn get_pose(&mut self, entity: Resource<Entity>) -> wasmtime::Result<EntityPose> {
|
||||||
let entity = entity_from_resource(self, &entity)?;
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
Ok(map_entity_pose(entity.get_entity().pose.load()))
|
Ok(map_entity_pose(entity.get_entity().pose.load()))
|
||||||
@ -295,6 +393,43 @@ impl HostEntity for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn raycast(
|
||||||
|
&mut self,
|
||||||
|
entity: Resource<Entity>,
|
||||||
|
max_distance: f64,
|
||||||
|
_fluid_handling: bool,
|
||||||
|
) -> wasmtime::Result<Option<WitRaycastResult>> {
|
||||||
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
|
let start = entity.get_eye_pos();
|
||||||
|
let direction = entity.get_looking_vector();
|
||||||
|
let end = start + direction * max_distance;
|
||||||
|
let world = entity.get_entity().world.load_full();
|
||||||
|
|
||||||
|
let hit = world
|
||||||
|
.raycast(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
|pos: &pumpkin_util::math::position::BlockPos, w: &Arc<crate::world::World>| {
|
||||||
|
let pos = *pos;
|
||||||
|
let world = w.clone();
|
||||||
|
async move {
|
||||||
|
let block = world.get_block_state(&pos).await;
|
||||||
|
!block.is_air()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(hit.map(|(pos, face)| WitRaycastResult {
|
||||||
|
pos: WitBlockPos {
|
||||||
|
x: pos.0.x,
|
||||||
|
y: pos.0.y,
|
||||||
|
z: pos.0.z,
|
||||||
|
},
|
||||||
|
face: to_wasm_block_direction(face),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
async fn drop(&mut self, rep: Resource<Entity>) -> wasmtime::Result<()> {
|
async fn drop(&mut self, rep: Resource<Entity>) -> wasmtime::Result<()> {
|
||||||
let _ = self
|
let _ = self
|
||||||
.resource_table
|
.resource_table
|
||||||
|
|||||||
@ -16,14 +16,18 @@ use crate::{
|
|||||||
events::{
|
events::{
|
||||||
from_wasm_game_mode, from_wasm_position, to_wasm_game_mode, to_wasm_position,
|
from_wasm_game_mode, from_wasm_position, to_wasm_game_mode, to_wasm_position,
|
||||||
},
|
},
|
||||||
pumpkin::{self, plugin::player::Player, plugin::world::World},
|
pumpkin::{
|
||||||
|
self,
|
||||||
|
plugin::player::{Player, PlayerSkin, SkinParts},
|
||||||
|
plugin::world::World,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||||
use pumpkin_util::permission::PermissionLvl;
|
use pumpkin_util::permission::PermissionLvl;
|
||||||
|
|
||||||
fn player_from_resource(
|
pub fn player_from_resource(
|
||||||
state: &PluginHostState,
|
state: &PluginHostState,
|
||||||
player: &Resource<Player>,
|
player: &Resource<Player>,
|
||||||
) -> wasmtime::Result<std::sync::Arc<crate::entity::player::Player>> {
|
) -> wasmtime::Result<std::sync::Arc<crate::entity::player::Player>> {
|
||||||
@ -657,116 +661,6 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_sneaking(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().sneaking.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_sneaking(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
sneaking: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_sneaking(sneaking).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_sprinting(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().sprinting.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_sprinting(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
sprinting: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_sprinting(sprinting).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_swimming(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().swimming.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_swimming(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
swimming: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_swimming(swimming).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_invisible(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().invisible.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_invisible(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
invisible: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_invisible(invisible).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_glowing(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().glowing.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_glowing(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
glowing: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_glowing(glowing).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_fall_flying(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().fall_flying.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_fall_flying(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
fall_flying: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_fall_flying(fall_flying).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_on_fire(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().has_visual_fire.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_on_fire(
|
|
||||||
&mut self,
|
|
||||||
player: Resource<Player>,
|
|
||||||
on_fire: bool,
|
|
||||||
) -> wasmtime::Result<()> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
player.get_entity().set_on_fire(on_fire).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_on_ground(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
|
||||||
let player = player_from_resource(self, &player)?;
|
|
||||||
Ok(player.get_entity().on_ground.load(Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_flying(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
async fn is_flying(&mut self, player: Resource<Player>) -> wasmtime::Result<bool> {
|
||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
Ok(player.is_flying().await)
|
Ok(player.is_flying().await)
|
||||||
@ -824,6 +718,85 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
Ok(player.get_ip().await)
|
Ok(player.get_ip().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_skin(&mut self, player: Resource<Player>) -> wasmtime::Result<Option<PlayerSkin>> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
Ok(player
|
||||||
|
.gameprofile
|
||||||
|
.properties
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.name == "textures")
|
||||||
|
.map(|p| PlayerSkin {
|
||||||
|
value: p.value.clone(),
|
||||||
|
signature: p.signature.clone(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_skin_parts(&mut self, player: Resource<Player>) -> wasmtime::Result<SkinParts> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
let mask = player.config.load().skin_parts;
|
||||||
|
let mut parts = SkinParts::empty();
|
||||||
|
if mask & 0x01 != 0 {
|
||||||
|
parts |= SkinParts::CAPE;
|
||||||
|
}
|
||||||
|
if mask & 0x02 != 0 {
|
||||||
|
parts |= SkinParts::JACKET;
|
||||||
|
}
|
||||||
|
if mask & 0x04 != 0 {
|
||||||
|
parts |= SkinParts::LEFT_SLEEVE;
|
||||||
|
}
|
||||||
|
if mask & 0x08 != 0 {
|
||||||
|
parts |= SkinParts::RIGHT_SLEEVE;
|
||||||
|
}
|
||||||
|
if mask & 0x10 != 0 {
|
||||||
|
parts |= SkinParts::LEFT_PANTS_LEG;
|
||||||
|
}
|
||||||
|
if mask & 0x20 != 0 {
|
||||||
|
parts |= SkinParts::RIGHT_PANTS_LEG;
|
||||||
|
}
|
||||||
|
if mask & 0x40 != 0 {
|
||||||
|
parts |= SkinParts::HAT;
|
||||||
|
}
|
||||||
|
Ok(parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_skin_parts(
|
||||||
|
&mut self,
|
||||||
|
player: Resource<Player>,
|
||||||
|
parts: SkinParts,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let player = player_from_resource(self, &player)?;
|
||||||
|
let mut mask = 0u8;
|
||||||
|
if parts.contains(SkinParts::CAPE) {
|
||||||
|
mask |= 0x01;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::JACKET) {
|
||||||
|
mask |= 0x02;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::LEFT_SLEEVE) {
|
||||||
|
mask |= 0x04;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::RIGHT_SLEEVE) {
|
||||||
|
mask |= 0x08;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::LEFT_PANTS_LEG) {
|
||||||
|
mask |= 0x10;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::RIGHT_PANTS_LEG) {
|
||||||
|
mask |= 0x20;
|
||||||
|
}
|
||||||
|
if parts.contains(SkinParts::HAT) {
|
||||||
|
mask |= 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut config = (**player.config.load()).clone();
|
||||||
|
config.skin_parts = mask;
|
||||||
|
player.config.store(Arc::new(config));
|
||||||
|
};
|
||||||
|
player.send_client_information().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn drop(&mut self, rep: Resource<Player>) -> wasmtime::Result<()> {
|
async fn drop(&mut self, rep: Resource<Player>) -> wasmtime::Result<()> {
|
||||||
let _ = self
|
let _ = self
|
||||||
.resource_table
|
.resource_table
|
||||||
|
|||||||
@ -2,6 +2,9 @@ use pumpkin_util::text::TextComponent;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
|
use crate::command::CommandSender;
|
||||||
|
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
|
||||||
|
|
||||||
use super::player::text_component_from_resource;
|
use super::player::text_component_from_resource;
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::{PluginHostState, ServerResource},
|
state::{PluginHostState, ServerResource},
|
||||||
@ -9,7 +12,7 @@ use crate::plugin::loader::wasm::wasm_host::{
|
|||||||
self,
|
self,
|
||||||
plugin::{
|
plugin::{
|
||||||
player::Player,
|
player::Player,
|
||||||
server::{Difficulty, Server},
|
server::{Difficulty, Dimension, Server},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -156,6 +159,28 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_world(
|
||||||
|
&mut self,
|
||||||
|
_rep: Resource<Server>,
|
||||||
|
name: String,
|
||||||
|
dimension: Dimension,
|
||||||
|
) -> wasmtime::Result<Resource<pumpkin::plugin::world::World>> {
|
||||||
|
let server = self
|
||||||
|
.server
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
|
||||||
|
|
||||||
|
let internal_dim = match dimension {
|
||||||
|
Dimension::Overworld => pumpkin_data::dimension::Dimension::OVERWORLD,
|
||||||
|
Dimension::Nether => pumpkin_data::dimension::Dimension::THE_NETHER,
|
||||||
|
Dimension::End => pumpkin_data::dimension::Dimension::THE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
let world = server.create_world(name, internal_dim).await;
|
||||||
|
self.add_world(world)
|
||||||
|
.map_err(|_| wasmtime::Error::msg("failed to add world resource"))
|
||||||
|
}
|
||||||
|
|
||||||
async fn broadcast(&mut self, _rep: Resource<Server>, message: String) -> wasmtime::Result<()> {
|
async fn broadcast(&mut self, _rep: Resource<Server>, message: String) -> wasmtime::Result<()> {
|
||||||
let server = self
|
let server = self
|
||||||
.server
|
.server
|
||||||
@ -192,6 +217,39 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn execute_command(
|
||||||
|
&mut self,
|
||||||
|
_rep: Resource<Server>,
|
||||||
|
command: String,
|
||||||
|
sender: WasmCommandSender,
|
||||||
|
) -> wasmtime::Result<()> {
|
||||||
|
let server = self
|
||||||
|
.server
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| wasmtime::Error::msg("Server not available"))?;
|
||||||
|
|
||||||
|
let native_sender = match sender {
|
||||||
|
WasmCommandSender::Console => CommandSender::Console,
|
||||||
|
WasmCommandSender::Player(player_res) => {
|
||||||
|
// Extract the native Player reference from the WASM resource
|
||||||
|
let player_resource =
|
||||||
|
self.resource_table
|
||||||
|
.get::<crate::plugin::loader::wasm::wasm_host::state::PlayerResource>(
|
||||||
|
&Resource::new_own(player_res.rep()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
CommandSender::Player(player_resource.provider.clone())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let dispatcher = server.command_dispatcher.read().await;
|
||||||
|
dispatcher
|
||||||
|
.handle_command(&native_sender.into_source(server).await, &command)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_max_players(&mut self, _rep: Resource<Server>) -> wasmtime::Result<u32> {
|
async fn get_max_players(&mut self, _rep: Resource<Server>) -> wasmtime::Result<u32> {
|
||||||
let server = self
|
let server = self
|
||||||
.server
|
.server
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use pumpkin_data::BlockDirection as InternalBlockDirection;
|
||||||
use pumpkin_data::block_state::PistonBehavior;
|
use pumpkin_data::block_state::PistonBehavior;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_world::world::{BlockFlags, SimpleWorld};
|
use pumpkin_world::world::{BlockFlags, SimpleWorld};
|
||||||
@ -5,8 +6,8 @@ use std::sync::Arc;
|
|||||||
use wasmtime::component::Resource;
|
use wasmtime::component::Resource;
|
||||||
|
|
||||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
|
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
|
||||||
BlockFlags as WitBlockFlags, BlockPos as WitBlockPos, BlockState as WitBlockState,
|
BlockDirection as WitBlockDirection, BlockFlags as WitBlockFlags, BlockPos as WitBlockPos,
|
||||||
PistonBehavior as WitPistonBehavior,
|
BlockState as WitBlockState, PistonBehavior as WitPistonBehavior,
|
||||||
};
|
};
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::loader::wasm::wasm_host::{
|
||||||
state::{PluginHostState, TextComponentResource, WorldResource},
|
state::{PluginHostState, TextComponentResource, WorldResource},
|
||||||
@ -14,6 +15,17 @@ use crate::plugin::loader::wasm::wasm_host::{
|
|||||||
};
|
};
|
||||||
use crate::world::explosion::Explosion;
|
use crate::world::explosion::Explosion;
|
||||||
|
|
||||||
|
pub(crate) const fn to_wasm_block_direction(dir: InternalBlockDirection) -> WitBlockDirection {
|
||||||
|
match dir {
|
||||||
|
InternalBlockDirection::Down => WitBlockDirection::Down,
|
||||||
|
InternalBlockDirection::Up => WitBlockDirection::Up,
|
||||||
|
InternalBlockDirection::North => WitBlockDirection::North,
|
||||||
|
InternalBlockDirection::South => WitBlockDirection::South,
|
||||||
|
InternalBlockDirection::West => WitBlockDirection::West,
|
||||||
|
InternalBlockDirection::East => WitBlockDirection::East,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Trapping Helpers ---
|
// --- Trapping Helpers ---
|
||||||
impl PluginHostState {
|
impl PluginHostState {
|
||||||
fn get_world_res(&self, res: &Resource<World>) -> wasmtime::Result<&WorldResource> {
|
fn get_world_res(&self, res: &Resource<World>) -> wasmtime::Result<&WorldResource> {
|
||||||
|
|||||||
@ -341,6 +341,54 @@ impl Server {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_world(self: &Arc<Self>, name: String, dimension: Dimension) -> Arc<World> {
|
||||||
|
{
|
||||||
|
let worlds = self.worlds.load();
|
||||||
|
if let Some(world) = worlds
|
||||||
|
.iter()
|
||||||
|
.find(|w| w.get_world_name() == name && w.dimension == dimension)
|
||||||
|
{
|
||||||
|
return world.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = self.clone();
|
||||||
|
let name_clone = name.clone();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let world_path = server.basic_config.get_world_path().join(name_clone);
|
||||||
|
let registry = server.block_registry.clone();
|
||||||
|
let l_info = server.level_info.clone();
|
||||||
|
let weak = Arc::downgrade(&server);
|
||||||
|
let config = Arc::new(server.advanced_config.world.clone());
|
||||||
|
let seed = server.level_info.load().world_gen_settings.seed;
|
||||||
|
|
||||||
|
// TODO: gen_pool should be reused
|
||||||
|
let world = World::load(
|
||||||
|
pumpkin_world::dimension::into_level(
|
||||||
|
dimension,
|
||||||
|
&config,
|
||||||
|
world_path,
|
||||||
|
registry.clone(),
|
||||||
|
seed,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
l_info,
|
||||||
|
dimension,
|
||||||
|
registry,
|
||||||
|
weak,
|
||||||
|
);
|
||||||
|
let world_arc = Arc::new(world);
|
||||||
|
server.worlds.rcu(|worlds| {
|
||||||
|
let mut new_worlds = (**worlds).clone();
|
||||||
|
new_worlds.push(world_arc.clone());
|
||||||
|
new_worlds
|
||||||
|
});
|
||||||
|
world_arc
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.expect("World creation panicked")
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a new player to the server.
|
/// Adds a new player to the server.
|
||||||
///
|
///
|
||||||
/// This function takes an `Arc<Client>` representing the connected client and performs the following actions:
|
/// This function takes an `Arc<Client>` representing the connected client and performs the following actions:
|
||||||
@ -430,8 +478,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap in Arc after data is loaded
|
// Wrap in Arc after data is loaded
|
||||||
let mut player = Arc::new(player);
|
let player = Arc::new(player);
|
||||||
Arc::get_mut(&mut player).unwrap().this = Arc::downgrade(&player);
|
|
||||||
|
|
||||||
send_cancellable! {{
|
send_cancellable! {{
|
||||||
self;
|
self;
|
||||||
|
|||||||
@ -381,12 +381,11 @@ pub async fn spawn_category_for_position(
|
|||||||
chunk_pos: &Vector2<i32>,
|
chunk_pos: &Vector2<i32>,
|
||||||
spawn_state: &SpawnState,
|
spawn_state: &SpawnState,
|
||||||
) {
|
) {
|
||||||
// TODO StructureManager structureManager = level.structureManager();
|
|
||||||
// TODO blockState.isRedstoneConductor(chunk, pos) is true then return
|
|
||||||
let mut batch_buffer = vec![];
|
let mut batch_buffer = vec![];
|
||||||
let mut spawn_cluster_size = 0;
|
let mut spawn_cluster_size = 0;
|
||||||
let mut new_pos = pos;
|
let mut new_pos = pos;
|
||||||
let player_positions: Vec<_> = world.players.load().iter().map(|p| p.position()).collect();
|
let player_positions: Vec<_> = world.players.load().iter().map(|p| p.position()).collect();
|
||||||
|
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
let mut new_x = new_pos.0.x;
|
let mut new_x = new_pos.0.x;
|
||||||
let mut new_z = new_pos.0.z;
|
let mut new_z = new_pos.0.z;
|
||||||
@ -398,18 +397,27 @@ pub async fn spawn_category_for_position(
|
|||||||
new_x += rng().random_range(0..6) - rng().random_range(0..6);
|
new_x += rng().random_range(0..6) - rng().random_range(0..6);
|
||||||
new_z += rng().random_range(0..6) - rng().random_range(0..6);
|
new_z += rng().random_range(0..6) - rng().random_range(0..6);
|
||||||
new_pos = BlockPos::new(new_x, new_pos.0.y, new_z);
|
new_pos = BlockPos::new(new_x, new_pos.0.y, new_z);
|
||||||
let new_pos_center = new_pos.to_centered_f64();
|
|
||||||
let player_distance = get_nearest_player(&new_pos_center, &player_positions);
|
let spawn_pos_f64 = Vector3::new(
|
||||||
|
f64::from(new_pos.0.x) + 0.5,
|
||||||
|
f64::from(new_pos.0.y),
|
||||||
|
f64::from(new_pos.0.z) + 0.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
let player_distance = get_nearest_player(&spawn_pos_f64, &player_positions);
|
||||||
if !is_right_distance_to_player_and_spawn_point(&new_pos, player_distance, chunk_pos) {
|
if !is_right_distance_to_player_and_spawn_point(&new_pos, player_distance, chunk_pos) {
|
||||||
inc += 1;
|
inc += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(spawner) = get_random_spawn_mob_at(world, category, &new_pos).await else {
|
let Some(spawner) = get_random_spawn_mob_at(world, category, &new_pos).await else {
|
||||||
break 'outer;
|
break 'outer;
|
||||||
};
|
};
|
||||||
|
|
||||||
random_group_size = rng().random_range(spawner.min_count..=spawner.max_count);
|
random_group_size = rng().random_range(spawner.min_count..=spawner.max_count);
|
||||||
let entity_type =
|
let entity_type =
|
||||||
&EntityType::from_name(spawner.r#type.strip_prefix("minecraft:").unwrap()).unwrap();
|
&EntityType::from_name(spawner.r#type.strip_prefix("minecraft:").unwrap()).unwrap();
|
||||||
|
|
||||||
if !is_valid_spawn_position_for_type(
|
if !is_valid_spawn_position_for_type(
|
||||||
world,
|
world,
|
||||||
&new_pos,
|
&new_pos,
|
||||||
@ -426,21 +434,19 @@ pub async fn spawn_category_for_position(
|
|||||||
inc += 1;
|
inc += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let entity = from_type(entity_type, new_pos_center, world, Uuid::new_v4()).await;
|
|
||||||
|
let entity = from_type(entity_type, spawn_pos_f64, world, Uuid::new_v4()).await;
|
||||||
entity
|
entity
|
||||||
.get_entity()
|
.get_entity()
|
||||||
.set_rotation(rng().random::<f32>() * 360., 0.);
|
.set_rotation(rng().random::<f32>() * 360., 0.);
|
||||||
// TODO isValidPositionForMob(level, mob, f)
|
|
||||||
// TODO spawnGroupData = mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.NATURAL, spawnGroupData);
|
|
||||||
spawn_cluster_size += 1;
|
spawn_cluster_size += 1;
|
||||||
//group_size += 1;
|
|
||||||
batch_buffer.push(entity);
|
batch_buffer.push(entity);
|
||||||
spawn_state.after_spawn(entity_type, &new_pos, world).await;
|
spawn_state.after_spawn(entity_type, &new_pos, world).await;
|
||||||
if spawn_cluster_size >= entity_type.limit_per_chunk {
|
if spawn_cluster_size >= entity_type.limit_per_chunk {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO mob.isMaxGroupSizeReached(p)
|
|
||||||
inc += 1;
|
inc += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -601,14 +607,19 @@ pub async fn is_spawn_position_ok(
|
|||||||
SpawnLocation::InLava => world.get_fluid(block_pos).await.has_tag(&MINECRAFT_LAVA),
|
SpawnLocation::InLava => world.get_fluid(block_pos).await.has_tag(&MINECRAFT_LAVA),
|
||||||
SpawnLocation::InWater => {
|
SpawnLocation::InWater => {
|
||||||
// TODO !level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)
|
// TODO !level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)
|
||||||
|
let above_state = world.get_block_state(&block_pos.up()).await;
|
||||||
world.get_fluid(block_pos).await.has_tag(&MINECRAFT_WATER)
|
world.get_fluid(block_pos).await.has_tag(&MINECRAFT_WATER)
|
||||||
|
&& !above_state.is_full_cube()
|
||||||
}
|
}
|
||||||
SpawnLocation::OnGround => {
|
SpawnLocation::OnGround => {
|
||||||
let down = world.get_block_state(&block_pos.down()).await;
|
let down = world.get_block_state(&block_pos.down()).await;
|
||||||
let up = world.get_block_state(&block_pos.up()).await;
|
let up = world.get_block_state(&block_pos.up()).await;
|
||||||
let cur = world.get_block_state(block_pos).await;
|
let cur = world.get_block_state(block_pos).await;
|
||||||
// TODO: blockState.allowsSpawning
|
// TODO: blockState.allowsSpawning
|
||||||
if down.is_side_solid(BlockDirection::Up) {
|
let is_valid_spawn_below =
|
||||||
|
down.is_side_solid(BlockDirection::Up) && down.luminance < 14;
|
||||||
|
|
||||||
|
if is_valid_spawn_below {
|
||||||
is_valid_empty_spawn_block(cur) && is_valid_empty_spawn_block(up)
|
is_valid_empty_spawn_block(cur) && is_valid_empty_spawn_block(up)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -620,10 +631,20 @@ pub async fn is_spawn_position_ok(
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_valid_empty_spawn_block(state: &'static BlockState) -> bool {
|
pub fn is_valid_empty_spawn_block(state: &'static BlockState) -> bool {
|
||||||
// TODO: emitsRedstonePower
|
if state.is_full_cube() {
|
||||||
if state.is_full_cube() || state.is_liquid() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO !entityType.isBlockDangerous(blockState);
|
// if state.is_signal_source() {
|
||||||
!Block::from_state_id(state.id).has_tag(&MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE)
|
// return false;
|
||||||
|
// }
|
||||||
|
if state.is_liquid() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if Block::from_state_id(state.id).has_tag(&MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: !entityType.isBlockDangerous(blockState)
|
||||||
|
// (e.g., preventing spawns inside Sweet Berry Bushes, Wither Roses, or Fire)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user