feat: some more plugin api functions
This commit is contained in:
parent
460cb9b01c
commit
7c3177b404
@ -421,6 +421,12 @@ fn convert_value(
|
||||
} else {
|
||||
format!("vec_{}", tmp)
|
||||
}
|
||||
} else if is_ref {
|
||||
prep.push_str(&format!(
|
||||
"{}let vec_{}: Vec<_> = {}.iter().map(|v| *v as _).collect();\n",
|
||||
prep_prefix, tmp, src
|
||||
));
|
||||
format!("&vec_{}", tmp)
|
||||
} else {
|
||||
format!("{}.iter().map(|v| *v as _).collect()", src)
|
||||
}
|
||||
|
||||
@ -294,6 +294,11 @@ pub mod fuels;
|
||||
#[path = "generated/effect.rs"]
|
||||
pub mod effect;
|
||||
|
||||
#[cfg(feature = "effect")]
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/status_effect.rs"]
|
||||
pub mod status_effect;
|
||||
|
||||
#[cfg(feature = "structures")]
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/structures.rs"]
|
||||
|
||||
39
pumpkin-plugin-api/src/ai.rs
Normal file
39
pumpkin-plugin-api/src/ai.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::wit::pumpkin::plugin::context::Server;
|
||||
use crate::wit::pumpkin::plugin::world::Entity;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait AiGoal: Send + Sync {
|
||||
fn can_start(&mut self, server: Server, entity: Entity) -> bool {
|
||||
false
|
||||
}
|
||||
fn should_continue(&mut self, server: Server, entity: Entity) -> bool {
|
||||
false
|
||||
}
|
||||
fn start(&mut self, server: Server, entity: Entity) {}
|
||||
fn tick(&mut self, server: Server, entity: Entity) {}
|
||||
fn stop(&mut self, server: Server, entity: Entity) {}
|
||||
}
|
||||
|
||||
pub(crate) static AI_GOAL_HANDLERS: Mutex<LazyAiGoalHandlers> = Mutex::new(LazyAiGoalHandlers {
|
||||
handlers: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
});
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct LazyAiGoalHandlers {
|
||||
pub handlers: BTreeMap<u32, Box<dyn AiGoal>>,
|
||||
pub next_id: u32,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl LazyAiGoalHandlers {
|
||||
#[must_use]
|
||||
pub fn register(&mut self, goal: Box<dyn AiGoal>) -> u32 {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handlers.insert(id, goal);
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -165,6 +165,49 @@ impl wit::Guest for Component {
|
||||
let mut handlers = TASK_HANDLERS.lock().unwrap();
|
||||
handlers.handle(handler_id, server);
|
||||
}
|
||||
|
||||
fn handle_ai_goal_can_start(goal_id: u32, server: Server, entity: entity::Entity) -> bool {
|
||||
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
|
||||
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
|
||||
goal.can_start(server, entity)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ai_goal_should_continue(
|
||||
goal_id: u32,
|
||||
server: Server,
|
||||
entity: entity::Entity,
|
||||
) -> bool {
|
||||
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
|
||||
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
|
||||
goal.should_continue(server, entity)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ai_goal_start(goal_id: u32, server: Server, entity: entity::Entity) {
|
||||
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
|
||||
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
|
||||
goal.start(server, entity);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ai_goal_tick(goal_id: u32, server: Server, entity: entity::Entity) {
|
||||
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
|
||||
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
|
||||
goal.tick(server, entity);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ai_goal_stop(goal_id: u32, server: Server, entity: entity::Entity) {
|
||||
let mut handlers = crate::ai::AI_GOAL_HANDLERS.lock().unwrap();
|
||||
if let Some(goal) = handlers.handlers.get_mut(&goal_id) {
|
||||
goal.stop(server, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience alias for `core::result::Result<T, String>` used throughout the plugin API.
|
||||
@ -237,3 +280,4 @@ macro_rules! register_plugin {
|
||||
}
|
||||
};
|
||||
}
|
||||
pub mod ai;
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit ff6acb80b159b567c087a835f53f205f6755e403
|
||||
Subproject commit ab193d1160b90c446fb35d48e6c636d8f867c473
|
||||
@ -51,6 +51,17 @@ impl GoalSelector {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) -> Vec<PrioritizedGoal> {
|
||||
let mut running = Vec::new();
|
||||
for goal in self.goals.drain(..) {
|
||||
if goal.running {
|
||||
running.push(goal);
|
||||
}
|
||||
}
|
||||
self.goals_by_control = [usize::MAX; 4];
|
||||
running
|
||||
}
|
||||
|
||||
fn uses_any(prioritized_goal: &PrioritizedGoal, controls: Controls) -> bool {
|
||||
let goal_controls = prioritized_goal.controls();
|
||||
for control in Controls::ITER {
|
||||
|
||||
@ -71,6 +71,20 @@ impl BreathManager {
|
||||
let new_air = (prev - AIR_DEPLETION_RATE).max(0);
|
||||
if new_air != prev {
|
||||
self.air_supply.store(new_air, Ordering::Relaxed);
|
||||
if let Some(server) = player.world().server.upgrade() {
|
||||
let mut event = crate::plugin::api::events::entity::entity_air_change::EntityAirChangeEvent::new(
|
||||
player.entity_id(),
|
||||
new_air,
|
||||
);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current()
|
||||
.block_on(server.plugin_manager.fire(&server, &mut event));
|
||||
});
|
||||
if event.cancelled {
|
||||
self.air_supply.store(prev, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.send_air_supply(player);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player};
|
||||
use super::{NBTStorage, NBTStorageInit, player::Player};
|
||||
use crate::entity::NbtFuture;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
|
||||
@ -216,6 +216,22 @@ impl EndermanEntity {
|
||||
world.play_sound(Sound::EntityEndermanTeleport, SoundCategory::Hostile, pos);
|
||||
}
|
||||
|
||||
if let Some(server) = world.server.upgrade() {
|
||||
let mut event =
|
||||
crate::plugin::api::events::entity::entity_teleport::EntityTeleportEvent::new(
|
||||
entity.entity_id,
|
||||
origin,
|
||||
new_pos,
|
||||
);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current()
|
||||
.block_on(server.plugin_manager.fire(&server, &mut event));
|
||||
});
|
||||
if event.cancelled {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
entity.set_pos(new_pos);
|
||||
let chunk_pos = entity.chunk_pos.load();
|
||||
world.broadcast_to_chunk(
|
||||
|
||||
@ -167,6 +167,45 @@ impl MobEntity {
|
||||
(self.mob_flags.load(Relaxed) & Self::AI_DISABLED_FLAG) != 0
|
||||
}
|
||||
|
||||
pub async fn clear_ai_goals(&self, mob: &dyn Mob) {
|
||||
let running_goals = self.goals_selector.lock().unwrap().clear();
|
||||
for mut goal in running_goals {
|
||||
goal.goal.stop(mob).await;
|
||||
}
|
||||
|
||||
let running_target_goals = self.target_selector.lock().unwrap().clear();
|
||||
for mut goal in running_target_goals {
|
||||
goal.goal.stop(mob).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_goal<G: crate::entity::ai::goal::Goal + 'static>(&self, priority: u8, goal: G) {
|
||||
self.goals_selector
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_goal(priority, Box::new(goal));
|
||||
}
|
||||
|
||||
pub fn add_target_goal<G: crate::entity::ai::goal::Goal + 'static>(
|
||||
&self,
|
||||
priority: u8,
|
||||
goal: G,
|
||||
) {
|
||||
self.target_selector
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_goal(priority, Box::new(goal));
|
||||
}
|
||||
|
||||
pub async fn set_target(&self, target: Option<Arc<dyn EntityBase>>) {
|
||||
let mut t = self.target.lock().await;
|
||||
*t = target;
|
||||
}
|
||||
|
||||
pub async fn get_target(&self) -> Option<Arc<dyn EntityBase>> {
|
||||
self.target.lock().await.clone()
|
||||
}
|
||||
|
||||
fn set_mob_flag(&self, flag: u8, value: bool) {
|
||||
let old_b = self.mob_flags.load(Ordering::Relaxed);
|
||||
|
||||
@ -560,6 +599,10 @@ pub trait Mob: EntityBase + Send + Sync {
|
||||
fn mob_set_variant_name(&self, _name: &str) {}
|
||||
}
|
||||
impl<T: Mob + Send + 'static> EntityBase for T {
|
||||
fn get_mob(&self) -> Option<&dyn Mob> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
self.mob_init_data_tracker().await;
|
||||
|
||||
@ -226,6 +226,10 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
||||
0.0
|
||||
}
|
||||
|
||||
fn get_mob(&self) -> Option<&dyn mob::Mob> {
|
||||
None
|
||||
}
|
||||
|
||||
fn tick_in_void<'a>(&'a self, _dyn_self: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async move { self.get_entity().remove().await })
|
||||
}
|
||||
@ -805,6 +809,8 @@ pub struct Entity {
|
||||
pub custom_name: ArcSwap<Option<TextComponent>>,
|
||||
/// Indicates whether the entity's custom name is visible
|
||||
pub custom_name_visible: AtomicBool,
|
||||
pub silent: AtomicBool,
|
||||
pub has_no_gravity: AtomicBool,
|
||||
/// Scoreboard tags attached to this entity, managed with `/tag`.
|
||||
/// Vanilla allows at most [`MAX_SCOREBOARD_TAGS`] tags per entity.
|
||||
pub scoreboard_tags: Mutex<HashSet<String>>,
|
||||
@ -943,6 +949,8 @@ impl Entity {
|
||||
portal_manager: Mutex::new(None),
|
||||
custom_name: ArcSwap::new(Arc::new(None)),
|
||||
custom_name_visible: AtomicBool::new(false),
|
||||
silent: AtomicBool::new(false),
|
||||
has_no_gravity: AtomicBool::new(false),
|
||||
scoreboard_tags: Mutex::new(HashSet::new()),
|
||||
no_clip: AtomicBool::new(false),
|
||||
movement_multiplier: AtomicCell::new(Vector3::default()),
|
||||
@ -1102,6 +1110,38 @@ impl Entity {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn is_silent(&self) -> bool {
|
||||
self.silent.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_silent(&self, silent: bool) {
|
||||
self.silent.store(silent, Ordering::Relaxed);
|
||||
self.send_meta_data(
|
||||
&[Metadata::new(
|
||||
TrackedData::SILENT,
|
||||
MetaDataType::BOOLEAN,
|
||||
silent,
|
||||
)],
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn has_no_gravity(&self) -> bool {
|
||||
self.has_no_gravity.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_has_no_gravity(&self, no_gravity: bool) {
|
||||
self.has_no_gravity.store(no_gravity, Ordering::Relaxed);
|
||||
self.send_meta_data(
|
||||
&[Metadata::new(
|
||||
TrackedData::NO_GRAVITY,
|
||||
MetaDataType::BOOLEAN,
|
||||
no_gravity,
|
||||
)],
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn send_velocity(&self) {
|
||||
let velocity = self.velocity.load();
|
||||
let chunk_pos = self.chunk_pos.load();
|
||||
|
||||
@ -418,6 +418,12 @@ pub struct ItemCooldown {
|
||||
pub duration: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum PlayerWeather {
|
||||
Clear,
|
||||
Downfall,
|
||||
}
|
||||
|
||||
pub struct Player {
|
||||
/// The underlying living entity object that represents the player.
|
||||
pub living_entity: LivingEntity,
|
||||
@ -520,6 +526,11 @@ pub struct Player {
|
||||
pub tab_list_order: AtomicI32,
|
||||
pub tab_list_latency: AtomicI32,
|
||||
pub tab_list_listed: AtomicBool,
|
||||
pub per_player_time: AtomicCell<Option<(u64, bool)>>,
|
||||
pub per_player_weather: AtomicCell<Option<PlayerWeather>>,
|
||||
pub compass_target: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
||||
pub respawn_location: AtomicCell<Option<pumpkin_util::math::position::BlockPos>>,
|
||||
pub hidden_players: Mutex<std::collections::HashSet<uuid::Uuid>>,
|
||||
pub advancements: Arc<Mutex<PlayerAdvancement>>,
|
||||
pub enchantment_seed: AtomicI32,
|
||||
pub fishing_bobber: AtomicI32,
|
||||
@ -753,11 +764,26 @@ impl Player {
|
||||
tab_list_order: AtomicI32::new(0),
|
||||
tab_list_latency: AtomicI32::new(0),
|
||||
tab_list_listed: AtomicBool::new(true),
|
||||
per_player_time: AtomicCell::new(None),
|
||||
per_player_weather: AtomicCell::new(None),
|
||||
compass_target: AtomicCell::new(None),
|
||||
respawn_location: AtomicCell::new(None),
|
||||
hidden_players: Mutex::new(std::collections::HashSet::new()),
|
||||
fishing_bobber: AtomicI32::new(-1),
|
||||
bedrock_skin: ArcSwap::new(Arc::new(bedrock_skin)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tab list header and footer for Java Edition clients.
|
||||
///
|
||||
/// Note: Tab list header and footer formatting is a Java Edition-specific protocol feature
|
||||
/// and is safely ignored on Bedrock Edition clients.
|
||||
pub async fn set_tab_list(&self, tab_list: impl Into<crate::plugin::api::tab_list::TabList>) {
|
||||
let list = tab_list.into();
|
||||
self.set_tab_list_header_footer(list.header, list.footer)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn set_tab_list_header_footer(&self, header: TextComponent, footer: TextComponent) {
|
||||
*self.tab_list_header.lock().await = header.clone();
|
||||
*self.tab_list_footer.lock().await = footer.clone();
|
||||
@ -1279,6 +1305,15 @@ impl Player {
|
||||
// Send the break status before clearing the slot so the client can
|
||||
// use the item texture for break particles.
|
||||
if result == pumpkin_data::item_stack::DamageResult::Broken {
|
||||
if let Some(server) = self.world().server.upgrade()
|
||||
&& let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||
{
|
||||
let mut event = crate::plugin::api::events::player::player_item_break::PlayerItemBreakEvent::new(
|
||||
player_arc,
|
||||
updated_stack.item.registry_key.to_string(),
|
||||
);
|
||||
server.plugin_manager.fire(&server, &mut event).await;
|
||||
}
|
||||
self.increment_stat(
|
||||
statistics::StatisticCategory::Broken,
|
||||
updated_stack.item.id as i32,
|
||||
@ -2513,6 +2548,23 @@ impl Player {
|
||||
|
||||
/// Sends the world time to only this player.
|
||||
pub async fn send_time(&self, world: &World) {
|
||||
if let Some((custom_time, relative)) = self.per_player_time.load() {
|
||||
let time_of_day = if relative {
|
||||
let l_world = world.level_time.lock().await;
|
||||
(l_world.time_of_day as u64 + custom_time) as i64
|
||||
} else {
|
||||
custom_time as i64
|
||||
};
|
||||
let l_world = world.level_time.lock().await;
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CUpdateTime::new(l_world.world_age, time_of_day, true),
|
||||
&CSetTime::new(time_of_day as _),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let l_world = world.level_time.lock().await;
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
@ -2522,6 +2574,94 @@ impl Player {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn set_player_time(&self, world: &World, time: u64, relative: bool) {
|
||||
self.per_player_time.store(Some((time, relative)));
|
||||
self.send_time(world).await;
|
||||
}
|
||||
|
||||
pub async fn reset_player_time(&self, world: &World) {
|
||||
self.per_player_time.store(None);
|
||||
self.send_time(world).await;
|
||||
}
|
||||
|
||||
pub fn get_player_time(&self) -> Option<u64> {
|
||||
self.per_player_time.load().map(|(t, _)| t)
|
||||
}
|
||||
|
||||
pub fn is_player_time_relative(&self) -> bool {
|
||||
self.per_player_time.load().is_none_or(|(_, r)| r)
|
||||
}
|
||||
|
||||
pub fn set_player_weather(&self, weather: PlayerWeather) {
|
||||
self.per_player_weather.store(Some(weather));
|
||||
}
|
||||
|
||||
pub fn reset_player_weather(&self) {
|
||||
self.per_player_weather.store(None);
|
||||
}
|
||||
|
||||
pub fn get_player_weather(&self) -> Option<PlayerWeather> {
|
||||
self.per_player_weather.load()
|
||||
}
|
||||
|
||||
pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) {
|
||||
use pumpkin_protocol::java::client::play::CPlayerSpawnPosition;
|
||||
self.compass_target.store(Some(pos));
|
||||
self.client
|
||||
.enqueue_packet(&CPlayerSpawnPosition::new(pos, 0.0, 0.0, String::new()))
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn get_compass_target(&self) -> Option<pumpkin_util::math::position::BlockPos> {
|
||||
self.compass_target.load()
|
||||
}
|
||||
|
||||
pub fn set_respawn_location(&self, pos: pumpkin_util::math::position::BlockPos) {
|
||||
self.respawn_location.store(Some(pos));
|
||||
}
|
||||
|
||||
pub fn get_respawn_location(&self) -> Option<pumpkin_util::math::position::BlockPos> {
|
||||
self.respawn_location.load()
|
||||
}
|
||||
|
||||
pub async fn hide_player(&self, other_id: uuid::Uuid) {
|
||||
self.hidden_players.lock().await.insert(other_id);
|
||||
}
|
||||
|
||||
pub async fn show_player(&self, other_id: uuid::Uuid) {
|
||||
self.hidden_players.lock().await.remove(&other_id);
|
||||
}
|
||||
|
||||
pub async fn can_see(&self, other_id: &uuid::Uuid) -> bool {
|
||||
!self.hidden_players.lock().await.contains(other_id)
|
||||
}
|
||||
|
||||
pub async fn get_target_block(
|
||||
&self,
|
||||
world: &Arc<World>,
|
||||
max_distance: f64,
|
||||
) -> Option<pumpkin_util::math::position::BlockPos> {
|
||||
let (yaw, pitch) = (
|
||||
self.living_entity.entity.yaw.load(),
|
||||
self.living_entity.entity.pitch.load(),
|
||||
);
|
||||
let eye_pos = self.living_entity.entity.get_eye_pos();
|
||||
let yaw_rad = f64::from(yaw + 90.0).to_radians();
|
||||
let pitch_rad = f64::from(-pitch).to_radians();
|
||||
let dir = pumpkin_util::math::vector3::Vector3::new(
|
||||
pitch_rad.cos() * yaw_rad.cos(),
|
||||
pitch_rad.sin(),
|
||||
pitch_rad.cos() * yaw_rad.sin(),
|
||||
);
|
||||
let end_pos = eye_pos + dir * max_distance;
|
||||
let res = world
|
||||
.raycast(eye_pos, end_pos, async |pos, w| {
|
||||
!w.get_block_state(pos).is_air()
|
||||
})
|
||||
.await;
|
||||
res.map(|(pos, _)| pos)
|
||||
}
|
||||
|
||||
pub async fn unload_watched_chunks(&self, world: &World) {
|
||||
let radial_chunks = self.watched_section.load().all_chunks_within();
|
||||
let level = &world.level;
|
||||
@ -2723,6 +2863,18 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn kick(&self, reason: DisconnectReason, message: TextComponent) {
|
||||
if let Some(server) = self.world().server.upgrade()
|
||||
&& let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||
{
|
||||
let mut event = crate::plugin::api::events::player::player_kick::PlayerKickEvent::new(
|
||||
player_arc,
|
||||
message.clone().to_pretty_console(),
|
||||
);
|
||||
server.plugin_manager.fire(&server, &mut event).await;
|
||||
if event.cancelled {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.client.kick(reason, message).await;
|
||||
}
|
||||
|
||||
@ -2749,6 +2901,30 @@ impl Player {
|
||||
self.send_health().await;
|
||||
}
|
||||
|
||||
pub async fn damage(
|
||||
&self,
|
||||
caller: &dyn crate::entity::EntityBase,
|
||||
amount: f32,
|
||||
damage_type: pumpkin_data::damage::DamageType,
|
||||
) -> bool {
|
||||
self.living_entity.damage(caller, amount, damage_type).await
|
||||
}
|
||||
|
||||
pub async fn damage_generic(&self, amount: f32) -> bool {
|
||||
use pumpkin_data::damage::DamageType;
|
||||
self.living_entity
|
||||
.damage(self, amount, DamageType::GENERIC)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn kill(&self) {
|
||||
use pumpkin_data::damage::DamageType;
|
||||
let health = self.living_entity.health.load();
|
||||
self.living_entity
|
||||
.damage(self, health + 10.0, DamageType::OUT_OF_WORLD)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn send_health(&self) {
|
||||
if !self.has_client_loaded() {
|
||||
return;
|
||||
@ -2800,16 +2976,49 @@ impl Player {
|
||||
self.send_health().await;
|
||||
}
|
||||
|
||||
pub fn get_food_level(&self) -> u8 {
|
||||
self.hunger_manager.level.load()
|
||||
}
|
||||
|
||||
pub async fn set_food_level(&self, food_level: u8) {
|
||||
self.hunger_manager.set_level(food_level);
|
||||
self.send_health().await;
|
||||
}
|
||||
|
||||
pub fn get_saturation(&self) -> f32 {
|
||||
self.hunger_manager.saturation.load()
|
||||
}
|
||||
|
||||
pub async fn set_saturation(&self, saturation: f32) {
|
||||
self.hunger_manager.set_saturation(saturation);
|
||||
self.send_health().await;
|
||||
}
|
||||
|
||||
pub async fn set_allow_flight(&self, allow: bool) {
|
||||
self.abilities.lock().await.allow_flying = allow;
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
|
||||
pub async fn set_flying(&self, flying: bool) {
|
||||
self.abilities.lock().await.flying = flying;
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
|
||||
pub async fn set_fly_speed(&self, speed: f32) {
|
||||
self.abilities.lock().await.fly_speed = speed;
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
|
||||
pub async fn set_walk_speed(&self, speed: f32) {
|
||||
self.abilities.lock().await.walk_speed = speed;
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
|
||||
pub async fn set_invulnerable(&self, invulnerable: bool) {
|
||||
self.abilities.lock().await.invulnerable = invulnerable;
|
||||
self.send_abilities_update().await;
|
||||
}
|
||||
|
||||
pub fn get_exhaustion(&self) -> f32 {
|
||||
self.hunger_manager.get_exhaustion()
|
||||
}
|
||||
@ -3258,6 +3467,15 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn swap_item(&self) {
|
||||
if let Some(server) = self.world().server.upgrade()
|
||||
&& let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||
{
|
||||
let mut event = crate::plugin::api::events::player::player_swap_hands::PlayerSwapHandItemsEvent::new(player_arc);
|
||||
server.plugin_manager.fire(&server, &mut event).await;
|
||||
if event.cancelled {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let (main_hand_item, off_hand_item) = self.inventory.swap_item().await;
|
||||
let equipment = &[
|
||||
(EquipmentSlot::MAIN_HAND, main_hand_item),
|
||||
@ -3386,6 +3604,18 @@ impl Player {
|
||||
|
||||
/// Sets the player's experience level and notifies the client.
|
||||
pub async fn set_experience(&self, level: i32, progress: f32, points: i32) {
|
||||
let old_level = self.experience_level.load(Ordering::Relaxed);
|
||||
if old_level != level
|
||||
&& let Some(server) = self.world().server.upgrade()
|
||||
&& let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
|
||||
{
|
||||
let mut event = crate::plugin::api::events::player::player_level_change::PlayerLevelChangeEvent::new(
|
||||
player_arc,
|
||||
old_level,
|
||||
level,
|
||||
);
|
||||
server.plugin_manager.fire(&server, &mut event).await;
|
||||
}
|
||||
// TODO: These should be atomic together, not isolated; make a struct containing these. can cause ABA issues
|
||||
self.experience_level.store(level, Ordering::Relaxed);
|
||||
self.experience_progress.store(progress.clamp(0.0, 1.0));
|
||||
@ -3429,6 +3659,19 @@ impl Player {
|
||||
self.living_entity.add_effect(effect).await;
|
||||
}
|
||||
|
||||
pub async fn has_effect(&self, effect_type: &'static StatusEffect) -> bool {
|
||||
self.living_entity.has_effect(effect_type).await
|
||||
}
|
||||
|
||||
pub async fn get_effect(&self, effect_type: &'static StatusEffect) -> Option<Effect> {
|
||||
self.living_entity.get_effect(effect_type).await
|
||||
}
|
||||
|
||||
pub async fn get_active_effects(&self) -> Vec<Effect> {
|
||||
let effects = self.living_entity.active_effects.lock().await;
|
||||
effects.values().cloned().collect()
|
||||
}
|
||||
|
||||
pub async fn send_active_effects(&self) {
|
||||
let effects = self.living_entity.active_effects.lock().await;
|
||||
for effect in effects.values() {
|
||||
|
||||
@ -1356,6 +1356,18 @@ impl JavaClient {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut anim_event = crate::plugin::api::events::player::player_animation::PlayerAnimationEvent::new(
|
||||
player.clone(),
|
||||
match hand {
|
||||
Hand::Left => crate::plugin::api::events::player::player_animation::PlayerAnimationType::ArmSwingOff,
|
||||
Hand::Right => crate::plugin::api::events::player::player_animation::PlayerAnimationType::ArmSwingMain,
|
||||
},
|
||||
);
|
||||
server.plugin_manager.fire(server, &mut anim_event).await;
|
||||
if anim_event.cancelled {
|
||||
return;
|
||||
}
|
||||
|
||||
let (yaw, pitch) = player.rotation();
|
||||
let hit_result = player
|
||||
.world()
|
||||
|
||||
23
pumpkin/src/plugin/api/events/entity/entity_air_change.rs
Normal file
23
pumpkin/src/plugin/api/events/entity/entity_air_change.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
|
||||
/// An event that occurs when an entity's remaining air supply changes.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityAirChangeEvent {
|
||||
/// The ID of the entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The new air amount.
|
||||
pub amount: i32,
|
||||
}
|
||||
|
||||
impl EntityAirChangeEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, amount: i32) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
amount,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
27
pumpkin/src/plugin/api/events/entity/entity_breed.rs
Normal file
27
pumpkin/src/plugin/api/events/entity/entity_breed.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
|
||||
/// An event that occurs when two entities breed to create a child.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityBreedEvent {
|
||||
/// The ID of the parent 1 entity.
|
||||
pub father_id: i32,
|
||||
|
||||
/// The ID of the parent 2 entity.
|
||||
pub mother_id: i32,
|
||||
|
||||
/// The ID of the child entity.
|
||||
pub child_id: i32,
|
||||
}
|
||||
|
||||
impl EntityBreedEvent {
|
||||
#[must_use]
|
||||
pub const fn new(father_id: i32, mother_id: i32, child_id: i32) -> Self {
|
||||
Self {
|
||||
father_id,
|
||||
mother_id,
|
||||
child_id,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
50
pumpkin/src/plugin/api/events/entity/entity_dye.rs
Normal file
50
pumpkin/src/plugin/api/events/entity/entity_dye.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The 16 colors available for dyeing entities.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DyeColor {
|
||||
White,
|
||||
Orange,
|
||||
Magenta,
|
||||
LightBlue,
|
||||
Yellow,
|
||||
Lime,
|
||||
Pink,
|
||||
Gray,
|
||||
LightGray,
|
||||
Cyan,
|
||||
Purple,
|
||||
Blue,
|
||||
Brown,
|
||||
Green,
|
||||
Red,
|
||||
Black,
|
||||
}
|
||||
|
||||
/// An event that occurs when an entity is dyed.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityDyeEvent {
|
||||
/// The ID of the dyed entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The color applied.
|
||||
pub color: DyeColor,
|
||||
|
||||
/// The player dyeing the entity, if any.
|
||||
pub player: Option<Arc<Player>>,
|
||||
}
|
||||
|
||||
impl EntityDyeEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, color: DyeColor, player: Option<Arc<Player>>) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
color,
|
||||
player,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
28
pumpkin/src/plugin/api/events/entity/entity_explode.rs
Normal file
28
pumpkin/src/plugin/api/events/entity/entity_explode.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
/// An event that occurs when an entity explodes.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityExplodeEvent {
|
||||
/// The ID of the exploding entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The position where the explosion occurred.
|
||||
pub position: Vector3<f64>,
|
||||
|
||||
/// The yield rate of blocks destroyed.
|
||||
pub yield_rate: f32,
|
||||
}
|
||||
|
||||
impl EntityExplodeEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, position: Vector3<f64>, yield_rate: f32) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
position,
|
||||
yield_rate,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
pumpkin/src/plugin/api/events/entity/entity_mount.rs
Normal file
23
pumpkin/src/plugin/api/events/entity/entity_mount.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
|
||||
/// An event that occurs when an entity mounts another entity.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityMountEvent {
|
||||
/// The ID of the mounting entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The ID of the vehicle entity being mounted.
|
||||
pub mount_id: i32,
|
||||
}
|
||||
|
||||
impl EntityMountEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, mount_id: i32) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
mount_id,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
25
pumpkin/src/plugin/api/events/entity/entity_tame.rs
Normal file
25
pumpkin/src/plugin/api/events/entity/entity_tame.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when an entity is tamed by a player.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityTameEvent {
|
||||
/// The ID of the tamed entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The player taming the entity.
|
||||
pub owner: Arc<Player>,
|
||||
}
|
||||
|
||||
impl EntityTameEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, owner: Arc<Player>) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
owner,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
pumpkin/src/plugin/api/events/entity/entity_target.rs
Normal file
23
pumpkin/src/plugin/api/events/entity/entity_target.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
|
||||
/// An event that occurs when an entity targets another entity.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityTargetEvent {
|
||||
/// The ID of the targeting entity.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The ID of the target entity, if any.
|
||||
pub target_id: Option<i32>,
|
||||
}
|
||||
|
||||
impl EntityTargetEvent {
|
||||
#[must_use]
|
||||
pub const fn new(entity_id: i32, target_id: Option<i32>) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
target_id,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
32
pumpkin/src/plugin/api/events/entity/entity_teleport.rs
Normal file
32
pumpkin/src/plugin/api/events/entity/entity_teleport.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
/// An event that occurs when an entity teleports.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct EntityTeleportEvent {
|
||||
/// The entity ID.
|
||||
pub entity_id: i32,
|
||||
|
||||
/// The origin position.
|
||||
pub from_position: Vector3<f64>,
|
||||
|
||||
/// The destination position.
|
||||
pub to_position: Vector3<f64>,
|
||||
}
|
||||
|
||||
impl EntityTeleportEvent {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
entity_id: i32,
|
||||
from_position: Vector3<f64>,
|
||||
to_position: Vector3<f64>,
|
||||
) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
from_position,
|
||||
to_position,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,27 @@
|
||||
pub mod entity_air_change;
|
||||
pub mod entity_breed;
|
||||
pub mod entity_combust;
|
||||
pub mod entity_damage;
|
||||
pub mod entity_death;
|
||||
pub mod entity_dye;
|
||||
pub mod entity_explode;
|
||||
pub mod entity_mount;
|
||||
pub mod entity_regain_health;
|
||||
pub mod entity_spawn;
|
||||
pub mod entity_tame;
|
||||
pub mod entity_target;
|
||||
pub mod entity_teleport;
|
||||
|
||||
pub use entity_air_change::*;
|
||||
pub use entity_breed::*;
|
||||
pub use entity_combust::*;
|
||||
pub use entity_damage::*;
|
||||
pub use entity_death::*;
|
||||
pub use entity_dye::*;
|
||||
pub use entity_explode::*;
|
||||
pub use entity_mount::*;
|
||||
pub use entity_regain_health::*;
|
||||
pub use entity_spawn::*;
|
||||
pub use entity_tame::*;
|
||||
pub use entity_target::*;
|
||||
pub use entity_teleport::*;
|
||||
|
||||
24
pumpkin/src/plugin/api/events/inventory/brew.rs
Normal file
24
pumpkin/src/plugin/api/events/inventory/brew.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
/// An event that occurs when a brewing stand finishes brewing potion(s).
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct BrewEvent {
|
||||
/// The block position of the brewing stand.
|
||||
pub block_pos: BlockPos,
|
||||
|
||||
/// The remaining fuel power.
|
||||
pub fuel_level: u8,
|
||||
}
|
||||
|
||||
impl BrewEvent {
|
||||
#[must_use]
|
||||
pub const fn new(block_pos: BlockPos, fuel_level: u8) -> Self {
|
||||
Self {
|
||||
block_pos,
|
||||
fuel_level,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
28
pumpkin/src/plugin/api/events/inventory/furnace_burn.rs
Normal file
28
pumpkin/src/plugin/api/events/inventory/furnace_burn.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
/// An event that occurs when an item is consumed as fuel in a furnace.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct FurnaceBurnEvent {
|
||||
/// The position of the furnace block.
|
||||
pub block_pos: BlockPos,
|
||||
|
||||
/// The registry key of the fuel item.
|
||||
pub fuel_item: String,
|
||||
|
||||
/// The burn time in ticks.
|
||||
pub burn_time: u32,
|
||||
}
|
||||
|
||||
impl FurnaceBurnEvent {
|
||||
#[must_use]
|
||||
pub const fn new(block_pos: BlockPos, fuel_item: String, burn_time: u32) -> Self {
|
||||
Self {
|
||||
block_pos,
|
||||
fuel_item,
|
||||
burn_time,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,17 @@
|
||||
pub mod brew;
|
||||
pub mod craft_item;
|
||||
pub mod furnace_burn;
|
||||
pub mod furnace_smelt;
|
||||
pub mod inventory_drag;
|
||||
pub mod inventory_open;
|
||||
pub mod prepare_anvil;
|
||||
pub mod trade_select;
|
||||
|
||||
pub use brew::*;
|
||||
pub use craft_item::*;
|
||||
pub use furnace_burn::*;
|
||||
pub use furnace_smelt::*;
|
||||
pub use inventory_drag::*;
|
||||
pub use inventory_open::*;
|
||||
pub use prepare_anvil::*;
|
||||
pub use trade_select::*;
|
||||
|
||||
27
pumpkin/src/plugin/api/events/inventory/prepare_anvil.rs
Normal file
27
pumpkin/src/plugin/api/events/inventory/prepare_anvil.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::Event;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when an item is prepared in an anvil.
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PrepareAnvilEvent {
|
||||
/// The player using the anvil.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The name prepared in the anvil input text field.
|
||||
pub rename_text: String,
|
||||
|
||||
/// The repair cost in experience levels.
|
||||
pub repair_cost: u32,
|
||||
}
|
||||
|
||||
impl PrepareAnvilEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, rename_text: String, repair_cost: u32) -> Self {
|
||||
Self {
|
||||
player,
|
||||
rename_text,
|
||||
repair_cost,
|
||||
}
|
||||
}
|
||||
}
|
||||
25
pumpkin/src/plugin/api/events/inventory/trade_select.rs
Normal file
25
pumpkin/src/plugin/api/events/inventory/trade_select.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a player selects a trade recipe with a merchant.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct TradeSelectEvent {
|
||||
/// The player selecting the trade.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The index of the selected trade slot.
|
||||
pub slot_index: u8,
|
||||
}
|
||||
|
||||
impl TradeSelectEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, slot_index: u8) -> Self {
|
||||
Self {
|
||||
player,
|
||||
slot_index,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ pub mod fish;
|
||||
pub mod inventory_close;
|
||||
pub mod inventory_interact;
|
||||
pub mod item_held;
|
||||
pub mod player_animation;
|
||||
pub mod player_bed;
|
||||
pub mod player_bucket;
|
||||
pub mod player_change_world;
|
||||
@ -18,14 +19,18 @@ pub mod player_gamemode_change;
|
||||
pub mod player_interact_entity_event;
|
||||
pub mod player_interact_event;
|
||||
pub mod player_interact_unknown_entity_event;
|
||||
pub mod player_item_break;
|
||||
pub mod player_item_consume;
|
||||
pub mod player_item_damage;
|
||||
pub mod player_join;
|
||||
pub mod player_kick;
|
||||
pub mod player_leave;
|
||||
pub mod player_level_change;
|
||||
pub mod player_login;
|
||||
pub mod player_move;
|
||||
pub mod player_permission_check;
|
||||
pub mod player_respawn;
|
||||
pub mod player_swap_hands;
|
||||
pub mod player_teleport;
|
||||
pub mod player_toggle_flight_event;
|
||||
pub mod player_toggle_sneak_event;
|
||||
@ -40,6 +45,7 @@ pub use fish::*;
|
||||
pub use inventory_close::*;
|
||||
pub use inventory_interact::*;
|
||||
pub use item_held::*;
|
||||
pub use player_animation::*;
|
||||
pub use player_bed::*;
|
||||
pub use player_bucket::*;
|
||||
pub use player_change_world::*;
|
||||
@ -51,14 +57,18 @@ pub use player_gamemode_change::*;
|
||||
pub use player_interact_entity_event::*;
|
||||
pub use player_interact_event::*;
|
||||
pub use player_interact_unknown_entity_event::*;
|
||||
pub use player_item_break::*;
|
||||
pub use player_item_consume::*;
|
||||
pub use player_item_damage::*;
|
||||
pub use player_join::*;
|
||||
pub use player_kick::*;
|
||||
pub use player_leave::*;
|
||||
pub use player_level_change::*;
|
||||
pub use player_login::*;
|
||||
pub use player_move::*;
|
||||
pub use player_permission_check::*;
|
||||
pub use player_respawn::*;
|
||||
pub use player_swap_hands::*;
|
||||
pub use player_teleport::*;
|
||||
pub use player_toggle_flight_event::*;
|
||||
pub use player_toggle_sneak_event::*;
|
||||
|
||||
33
pumpkin/src/plugin/api/events/player/player_animation.rs
Normal file
33
pumpkin/src/plugin/api/events/player/player_animation.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The type of animation performed by a player.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PlayerAnimationType {
|
||||
ArmSwingMain,
|
||||
ArmSwingOff,
|
||||
LeaveBed,
|
||||
}
|
||||
|
||||
/// An event that occurs when a player performs an animation.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerAnimationEvent {
|
||||
/// The player performing the animation.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The type of animation.
|
||||
pub animation_type: PlayerAnimationType,
|
||||
}
|
||||
|
||||
impl PlayerAnimationEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, animation_type: PlayerAnimationType) -> Self {
|
||||
Self {
|
||||
player,
|
||||
animation_type,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
20
pumpkin/src/plugin/api/events/player/player_item_break.rs
Normal file
20
pumpkin/src/plugin/api/events/player/player_item_break.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::Event;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a player breaks an item.
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerItemBreakEvent {
|
||||
/// The player whose item broke.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The registry key of the broken item.
|
||||
pub item_name: String,
|
||||
}
|
||||
|
||||
impl PlayerItemBreakEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, item_name: String) -> Self {
|
||||
Self { player, item_name }
|
||||
}
|
||||
}
|
||||
25
pumpkin/src/plugin/api/events/player/player_kick.rs
Normal file
25
pumpkin/src/plugin/api/events/player/player_kick.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a player is kicked from the server.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerKickEvent {
|
||||
/// The player being kicked.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The reason for the kick.
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
impl PlayerKickEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, reason: String) -> Self {
|
||||
Self {
|
||||
player,
|
||||
reason,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
27
pumpkin/src/plugin/api/events/player/player_level_change.rs
Normal file
27
pumpkin/src/plugin/api/events/player/player_level_change.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::Event;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a player's experience level changes.
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerLevelChangeEvent {
|
||||
/// The player whose level changed.
|
||||
pub player: Arc<Player>,
|
||||
|
||||
/// The old level.
|
||||
pub old_level: i32,
|
||||
|
||||
/// The new level.
|
||||
pub new_level: i32,
|
||||
}
|
||||
|
||||
impl PlayerLevelChangeEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>, old_level: i32, new_level: i32) -> Self {
|
||||
Self {
|
||||
player,
|
||||
old_level,
|
||||
new_level,
|
||||
}
|
||||
}
|
||||
}
|
||||
21
pumpkin/src/plugin/api/events/player/player_swap_hands.rs
Normal file
21
pumpkin/src/plugin/api/events/player/player_swap_hands.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a player swaps items between main hand and offhand.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PlayerSwapHandItemsEvent {
|
||||
/// The player swapping hand items.
|
||||
pub player: Arc<Player>,
|
||||
}
|
||||
|
||||
impl PlayerSwapHandItemsEvent {
|
||||
#[must_use]
|
||||
pub const fn new(player: Arc<Player>) -> Self {
|
||||
Self {
|
||||
player,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
24
pumpkin/src/plugin/api/events/world/generic_game.rs
Normal file
24
pumpkin/src/plugin/api/events/world/generic_game.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
/// An event that occurs when a generic game event is triggered in a world.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct GenericGameEvent {
|
||||
/// The key or type of the game event.
|
||||
pub event_key: String,
|
||||
|
||||
/// The position where the game event occurred.
|
||||
pub position: Vector3<f64>,
|
||||
}
|
||||
|
||||
impl GenericGameEvent {
|
||||
#[must_use]
|
||||
pub const fn new(event_key: String, position: Vector3<f64>) -> Self {
|
||||
Self {
|
||||
event_key,
|
||||
position,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
24
pumpkin/src/plugin/api/events/world/lightning_strike.rs
Normal file
24
pumpkin/src/plugin/api/events/world/lightning_strike.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
/// An event that occurs when a lightning bolt strikes in a world.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct LightningStrikeEvent {
|
||||
/// The location of the strike.
|
||||
pub position: Vector3<f64>,
|
||||
|
||||
/// Whether this strike was spawned by an effect.
|
||||
pub is_effect: bool,
|
||||
}
|
||||
|
||||
impl LightningStrikeEvent {
|
||||
#[must_use]
|
||||
pub const fn new(position: Vector3<f64>, is_effect: bool) -> Self {
|
||||
Self {
|
||||
position,
|
||||
is_effect,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,23 @@
|
||||
pub mod chunk_load;
|
||||
pub mod chunk_save;
|
||||
pub mod chunk_send;
|
||||
pub mod generic_game;
|
||||
pub mod lightning_strike;
|
||||
pub mod portal_create;
|
||||
pub mod spawn_change;
|
||||
pub mod structure_grow;
|
||||
pub mod weather_change;
|
||||
pub mod world_init;
|
||||
pub mod world_load;
|
||||
|
||||
pub use chunk_load::*;
|
||||
pub use chunk_save::*;
|
||||
pub use chunk_send::*;
|
||||
pub use generic_game::*;
|
||||
pub use lightning_strike::*;
|
||||
pub use portal_create::*;
|
||||
pub use spawn_change::*;
|
||||
pub use structure_grow::*;
|
||||
pub use weather_change::*;
|
||||
pub use world_init::*;
|
||||
pub use world_load::*;
|
||||
|
||||
32
pumpkin/src/plugin/api/events/world/portal_create.rs
Normal file
32
pumpkin/src/plugin/api/events/world/portal_create.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
/// The type of portal created.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PortalType {
|
||||
Nether,
|
||||
End,
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// An event that occurs when a nether or end portal frame/block is created.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct PortalCreateEvent {
|
||||
/// The block position of the portal creation.
|
||||
pub block_pos: BlockPos,
|
||||
|
||||
/// The type of portal created.
|
||||
pub portal_type: PortalType,
|
||||
}
|
||||
|
||||
impl PortalCreateEvent {
|
||||
#[must_use]
|
||||
pub const fn new(block_pos: BlockPos, portal_type: PortalType) -> Self {
|
||||
Self {
|
||||
block_pos,
|
||||
portal_type,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
45
pumpkin/src/plugin/api/events/world/structure_grow.rs
Normal file
45
pumpkin/src/plugin/api/events/world/structure_grow.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use pumpkin_macros::{Event, cancellable};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
/// The type of structure growing.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TreeType {
|
||||
Oak,
|
||||
Spruce,
|
||||
Birch,
|
||||
Jungle,
|
||||
Acacia,
|
||||
DarkOak,
|
||||
Mangrove,
|
||||
Cherry,
|
||||
Azalea,
|
||||
BrownMushroom,
|
||||
RedMushroom,
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// An event that occurs when a structure (like a tree or mushroom) grows.
|
||||
#[cancellable]
|
||||
#[derive(Event, Clone)]
|
||||
pub struct StructureGrowEvent {
|
||||
/// The origin block position where growth started.
|
||||
pub block_pos: BlockPos,
|
||||
|
||||
/// The type of structure growing.
|
||||
pub species: TreeType,
|
||||
|
||||
/// Whether bone meal was used.
|
||||
pub bone_meal: bool,
|
||||
}
|
||||
|
||||
impl StructureGrowEvent {
|
||||
#[must_use]
|
||||
pub const fn new(block_pos: BlockPos, species: TreeType, bone_meal: bool) -> Self {
|
||||
Self {
|
||||
block_pos,
|
||||
species,
|
||||
bone_meal,
|
||||
cancelled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
17
pumpkin/src/plugin/api/events/world/world_init.rs
Normal file
17
pumpkin/src/plugin/api/events/world/world_init.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::world::World;
|
||||
use pumpkin_macros::Event;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An event that occurs when a world is initialized.
|
||||
#[derive(Event, Clone)]
|
||||
pub struct WorldInitEvent {
|
||||
/// The world being initialized.
|
||||
pub world: Arc<World>,
|
||||
}
|
||||
|
||||
impl WorldInitEvent {
|
||||
#[must_use]
|
||||
pub const fn new(world: Arc<World>) -> Self {
|
||||
Self { world }
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,15 @@
|
||||
pub mod context;
|
||||
pub mod events;
|
||||
pub mod gui;
|
||||
pub mod tab_list;
|
||||
pub mod title;
|
||||
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
pub use context::*;
|
||||
pub use events::*;
|
||||
pub use tab_list::*;
|
||||
pub use title::*;
|
||||
|
||||
/// Struct representing metadata for a plugin.
|
||||
///
|
||||
|
||||
85
pumpkin/src/plugin/api/tab_list.rs
Normal file
85
pumpkin/src/plugin/api/tab_list.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
/// Represents a Java Edition tab list configuration containing header and footer components.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabList {
|
||||
/// The tab list header component.
|
||||
pub header: TextComponent,
|
||||
/// The tab list footer component.
|
||||
pub footer: TextComponent,
|
||||
}
|
||||
|
||||
impl Default for TabList {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
header: TextComponent::text(""),
|
||||
footer: TextComponent::text(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TabList {
|
||||
/// Creates a new `TabListBuilder`.
|
||||
#[must_use]
|
||||
pub fn builder() -> TabListBuilder {
|
||||
TabListBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A fluent builder for `TabList`.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TabListBuilder {
|
||||
header: Option<TextComponent>,
|
||||
footer: Option<TextComponent>,
|
||||
}
|
||||
|
||||
impl TabListBuilder {
|
||||
/// Creates a new `TabListBuilder`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets the tab list header component.
|
||||
#[must_use]
|
||||
pub fn header(mut self, header: impl Into<TextComponent>) -> Self {
|
||||
self.header = Some(header.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tab list header from plain text.
|
||||
#[must_use]
|
||||
pub fn header_text(mut self, text: impl Into<String>) -> Self {
|
||||
self.header = Some(TextComponent::text(text.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tab list footer component.
|
||||
#[must_use]
|
||||
pub fn footer(mut self, footer: impl Into<TextComponent>) -> Self {
|
||||
self.footer = Some(footer.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tab list footer from plain text.
|
||||
#[must_use]
|
||||
pub fn footer_text(mut self, text: impl Into<String>) -> Self {
|
||||
self.footer = Some(TextComponent::text(text.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the `TabList`.
|
||||
#[must_use]
|
||||
pub fn build(self) -> TabList {
|
||||
TabList {
|
||||
header: self.header.unwrap_or_else(|| TextComponent::text("")),
|
||||
footer: self.footer.unwrap_or_else(|| TextComponent::text("")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TabListBuilder> for TabList {
|
||||
fn from(builder: TabListBuilder) -> Self {
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
93
pumpkin/src/plugin/api/title.rs
Normal file
93
pumpkin/src/plugin/api/title.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use crate::entity::player::{Player, TitleMode};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
/// A fluent Builder for constructing and displaying titles, subtitles, and action bar text with animation timings.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TitleBuilder {
|
||||
title: Option<TextComponent>,
|
||||
subtitle: Option<TextComponent>,
|
||||
actionbar: Option<TextComponent>,
|
||||
fade_in: Option<i32>,
|
||||
stay: Option<i32>,
|
||||
fade_out: Option<i32>,
|
||||
}
|
||||
|
||||
impl TitleBuilder {
|
||||
/// Creates a new `TitleBuilder`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets the main title text component.
|
||||
#[must_use]
|
||||
pub fn title(mut self, title: impl Into<TextComponent>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the main title from plain text.
|
||||
#[must_use]
|
||||
pub fn title_text(mut self, text: impl Into<String>) -> Self {
|
||||
self.title = Some(TextComponent::text(text.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the subtitle text component.
|
||||
#[must_use]
|
||||
pub fn subtitle(mut self, subtitle: impl Into<TextComponent>) -> Self {
|
||||
self.subtitle = Some(subtitle.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the subtitle from plain text.
|
||||
#[must_use]
|
||||
pub fn subtitle_text(mut self, text: impl Into<String>) -> Self {
|
||||
self.subtitle = Some(TextComponent::text(text.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the action bar text component.
|
||||
#[must_use]
|
||||
pub fn actionbar(mut self, actionbar: impl Into<TextComponent>) -> Self {
|
||||
self.actionbar = Some(actionbar.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the action bar text from plain text.
|
||||
#[must_use]
|
||||
pub fn actionbar_text(mut self, text: impl Into<String>) -> Self {
|
||||
self.actionbar = Some(TextComponent::text(text.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the fade-in, stay, and fade-out timings in ticks.
|
||||
#[must_use]
|
||||
pub const fn times(mut self, fade_in: i32, stay: i32, fade_out: i32) -> Self {
|
||||
self.fade_in = Some(fade_in);
|
||||
self.stay = Some(stay);
|
||||
self.fade_out = Some(fade_out);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sends the title configuration to the specified player.
|
||||
pub async fn send_to(&self, player: &Player) {
|
||||
if let (Some(fade_in), Some(stay), Some(fade_out)) =
|
||||
(self.fade_in, self.stay, self.fade_out)
|
||||
{
|
||||
player.send_title_animation(fade_in, stay, fade_out).await;
|
||||
}
|
||||
|
||||
if let Some(ref title) = self.title {
|
||||
player.show_title(title, &TitleMode::Title).await;
|
||||
}
|
||||
|
||||
if let Some(ref subtitle) = self.subtitle {
|
||||
player.show_title(subtitle, &TitleMode::SubTitle).await;
|
||||
}
|
||||
|
||||
if let Some(ref actionbar) = self.actionbar {
|
||||
player.show_title(actionbar, &TitleMode::ActionBar).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,9 @@ use wasmtime::component::Resource;
|
||||
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::entity::ai::goal::{Goal, GoalFuture};
|
||||
use crate::entity::mob::Mob;
|
||||
use crate::plugin::loader::wasm::wasm_host::{PluginInstance, WasmPlugin};
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::{EntityResource, PluginHostState},
|
||||
wit::v0_1::events::to_wasm_position,
|
||||
@ -501,6 +504,32 @@ impl HostEntity for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_silent(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity.get_entity().is_silent())
|
||||
}
|
||||
|
||||
async fn set_silent(&mut self, entity: Resource<Entity>, silent: bool) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity.get_entity().set_silent(silent);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn has_gravity(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(!entity.get_entity().has_no_gravity())
|
||||
}
|
||||
|
||||
async fn set_has_gravity(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
gravity: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
entity.get_entity().set_has_no_gravity(!gravity);
|
||||
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)
|
||||
@ -804,6 +833,118 @@ impl HostEntity for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_ai_goal(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
priority: u8,
|
||||
goal: crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(mob) = entity.get_mob() {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
match goal {
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::Swim => {
|
||||
mob_entity.add_goal(priority, crate::entity::ai::goal::swim::SwimGoal::default());
|
||||
}
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::WanderAround(speed) => {
|
||||
mob_entity.add_goal(priority, crate::entity::ai::goal::wander_around::WanderAroundGoal::new(f64::from(speed)));
|
||||
}
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::MeleeAttack(speed) => {
|
||||
mob_entity.add_goal(priority, crate::entity::ai::goal::melee_attack::MeleeAttackGoal::new(f64::from(speed), false));
|
||||
}
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::LookAtPlayer(range) => {
|
||||
mob_entity.add_goal(priority, crate::entity::ai::goal::look_at_entity::LookAtEntityGoal::new(
|
||||
std::sync::Weak::<crate::entity::mob::zombie::zombie::ZombieEntity>::new() as std::sync::Weak<dyn crate::entity::mob::Mob>,
|
||||
&pumpkin_data::entity::EntityType::PLAYER,
|
||||
range,
|
||||
0.02,
|
||||
false,
|
||||
));
|
||||
}
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::LookAround => {
|
||||
mob_entity.add_goal(priority, crate::entity::ai::goal::look_around::RandomLookAroundGoal::default());
|
||||
}
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::BuiltinAiGoal::EscapeDanger(speed) => {
|
||||
mob_entity.add_goal(priority, *crate::entity::ai::goal::escape_danger::EscapeDangerGoal::new(f64::from(speed)));
|
||||
}
|
||||
_ => {} // Remaining goals
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_custom_ai_goal(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
priority: u8,
|
||||
goal_id: u32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let plugin = self.plugin.as_ref().unwrap().upgrade().unwrap();
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(mob) = entity.get_mob() {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
mob_entity.add_goal(priority, CustomWasmGoal { plugin, goal_id });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn clear_ai_goals(&mut self, entity: Resource<Entity>) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(mob) = entity.get_mob() {
|
||||
mob.get_mob_entity().clear_ai_goals(mob).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_ai_disabled(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
disabled: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(mob) = entity.get_mob() {
|
||||
mob.get_mob_entity().set_no_ai(disabled);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_ai_disabled(&mut self, entity: Resource<Entity>) -> wasmtime::Result<bool> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
Ok(entity
|
||||
.get_mob()
|
||||
.is_none_or(|mob| mob.get_mob_entity().is_no_ai()))
|
||||
}
|
||||
|
||||
async fn set_target(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
target: Option<Resource<Entity>>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
let target_entity = if let Some(t) = target {
|
||||
Some(entity_from_resource(self, &t)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mob) = entity.get_mob() {
|
||||
mob.get_mob_entity().set_target(target_entity).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_target(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
) -> wasmtime::Result<Option<Resource<Entity>>> {
|
||||
let entity = entity_from_resource(self, &entity)?;
|
||||
if let Some(mob) = entity.get_mob()
|
||||
&& let Some(target) = mob.get_mob_entity().get_target().await
|
||||
{
|
||||
return Ok(Some(self.add_entity(target)?));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn raycast(
|
||||
&mut self,
|
||||
entity: Resource<Entity>,
|
||||
@ -848,3 +989,156 @@ impl HostEntity for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CustomWasmGoal {
|
||||
pub plugin: Arc<WasmPlugin>,
|
||||
pub goal_id: u32,
|
||||
}
|
||||
|
||||
impl Goal for CustomWasmGoal {
|
||||
fn can_start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> {
|
||||
Box::pin(async {
|
||||
let mut store = self.plugin.store.lock().await;
|
||||
if let Some(entity_arc) = mob
|
||||
.get_entity()
|
||||
.world
|
||||
.load()
|
||||
.get_entity_by_id(mob.get_entity().entity_id)
|
||||
{
|
||||
match self.plugin.plugin_instance {
|
||||
PluginInstance::V0_1(ref plugin) => {
|
||||
let server = store.data_mut().server.clone().unwrap();
|
||||
let server_res = store.data_mut().add_server(server).unwrap();
|
||||
let entity_res = store.data_mut().add_entity(entity_arc).unwrap();
|
||||
plugin
|
||||
.call_handle_ai_goal_can_start(
|
||||
&mut *store,
|
||||
self.goal_id,
|
||||
server_res,
|
||||
entity_res,
|
||||
)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> {
|
||||
Box::pin(async {
|
||||
let mut store = self.plugin.store.lock().await;
|
||||
if let Some(entity_arc) = mob
|
||||
.get_entity()
|
||||
.world
|
||||
.load()
|
||||
.get_entity_by_id(mob.get_entity().entity_id)
|
||||
{
|
||||
match self.plugin.plugin_instance {
|
||||
PluginInstance::V0_1(ref plugin) => {
|
||||
let server = store.data_mut().server.clone().unwrap();
|
||||
let server_res = store.data_mut().add_server(server).unwrap();
|
||||
let entity_res = store.data_mut().add_entity(entity_arc).unwrap();
|
||||
plugin
|
||||
.call_handle_ai_goal_should_continue(
|
||||
&mut *store,
|
||||
self.goal_id,
|
||||
server_res,
|
||||
entity_res,
|
||||
)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn start<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let mut store = self.plugin.store.lock().await;
|
||||
if let Some(entity_arc) = mob
|
||||
.get_entity()
|
||||
.world
|
||||
.load()
|
||||
.get_entity_by_id(mob.get_entity().entity_id)
|
||||
{
|
||||
match self.plugin.plugin_instance {
|
||||
PluginInstance::V0_1(ref plugin) => {
|
||||
let server = store.data_mut().server.clone().unwrap();
|
||||
let server_res = store.data_mut().add_server(server).unwrap();
|
||||
let entity_res = store.data_mut().add_entity(entity_arc).unwrap();
|
||||
let _ = plugin
|
||||
.call_handle_ai_goal_start(
|
||||
&mut *store,
|
||||
self.goal_id,
|
||||
server_res,
|
||||
entity_res,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn tick<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let mut store = self.plugin.store.lock().await;
|
||||
if let Some(entity_arc) = mob
|
||||
.get_entity()
|
||||
.world
|
||||
.load()
|
||||
.get_entity_by_id(mob.get_entity().entity_id)
|
||||
{
|
||||
match self.plugin.plugin_instance {
|
||||
PluginInstance::V0_1(ref plugin) => {
|
||||
let server = store.data_mut().server.clone().unwrap();
|
||||
let server_res = store.data_mut().add_server(server).unwrap();
|
||||
let entity_res = store.data_mut().add_entity(entity_arc).unwrap();
|
||||
let _ = plugin
|
||||
.call_handle_ai_goal_tick(
|
||||
&mut *store,
|
||||
self.goal_id,
|
||||
server_res,
|
||||
entity_res,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let mut store = self.plugin.store.lock().await;
|
||||
if let Some(entity_arc) = mob
|
||||
.get_entity()
|
||||
.world
|
||||
.load()
|
||||
.get_entity_by_id(mob.get_entity().entity_id)
|
||||
{
|
||||
match self.plugin.plugin_instance {
|
||||
PluginInstance::V0_1(ref plugin) => {
|
||||
let server = store.data_mut().server.clone().unwrap();
|
||||
let server_res = store.data_mut().add_server(server).unwrap();
|
||||
let entity_res = store.data_mut().add_entity(entity_arc).unwrap();
|
||||
let _ = plugin
|
||||
.call_handle_ai_goal_stop(
|
||||
&mut *store,
|
||||
self.goal_id,
|
||||
server_res,
|
||||
entity_res,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,9 +206,9 @@ pub fn serialize_java_packet(
|
||||
Some(buf.into())
|
||||
}
|
||||
ClientboundPacket::CDebugSample(data) => {
|
||||
let sample_vec: Vec<i64> = data.sample.iter().map(|v| *v as i64).collect();
|
||||
let vec_sample: Vec<_> = data.sample.iter().map(|v| *v as _).collect();
|
||||
let p = pumpkin_protocol::java::client::play::CDebugSample {
|
||||
sample: &sample_vec,
|
||||
sample: &vec_sample,
|
||||
sample_type: VarInt(data.sample_type),
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
|
||||
@ -28,6 +28,7 @@ pub mod recipe;
|
||||
pub mod scheduler;
|
||||
pub mod scoreboard;
|
||||
pub mod server;
|
||||
pub mod status_effect;
|
||||
pub mod text;
|
||||
pub mod uuid;
|
||||
pub mod world;
|
||||
|
||||
@ -868,6 +868,395 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_effect(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
effect: pumpkin::plugin::status_effect::StatusEffectInstance,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let effect_type = super::status_effect::from_wasm_status_effect_type(effect.effect_type);
|
||||
if let Some(status_effect) =
|
||||
pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name())
|
||||
{
|
||||
let effect_obj = pumpkin_data::potion::Effect {
|
||||
effect_type: status_effect,
|
||||
duration: effect.duration as i32,
|
||||
amplifier: effect.amplifier,
|
||||
ambient: effect.ambient,
|
||||
show_particles: effect.show_particles,
|
||||
show_icon: effect.show_icon,
|
||||
blend: false,
|
||||
};
|
||||
player.add_effect(effect_obj).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_effect(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
effect: pumpkin::plugin::status_effect::StatusEffectType,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let effect_type = super::status_effect::from_wasm_status_effect_type(effect);
|
||||
if let Some(status_effect) =
|
||||
pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name())
|
||||
{
|
||||
player.remove_effect(status_effect).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn clear_effects(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.remove_all_effects().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn has_effect(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
effect: pumpkin::plugin::status_effect::StatusEffectType,
|
||||
) -> wasmtime::Result<bool> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let effect_type = super::status_effect::from_wasm_status_effect_type(effect);
|
||||
if let Some(status_effect) =
|
||||
pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name())
|
||||
{
|
||||
Ok(player.has_effect(status_effect).await)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_effect(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
effect: pumpkin::plugin::status_effect::StatusEffectType,
|
||||
) -> wasmtime::Result<Option<pumpkin::plugin::status_effect::StatusEffectInstance>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let effect_type = super::status_effect::from_wasm_status_effect_type(effect);
|
||||
if let Some(status_effect) =
|
||||
pumpkin_data::effect::StatusEffect::from_name(effect_type.to_name())
|
||||
&& let Some(eff) = player.get_effect(status_effect).await
|
||||
{
|
||||
return Ok(super::status_effect::to_wasm_status_effect_instance(&eff));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn get_active_effects(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
) -> wasmtime::Result<Vec<pumpkin::plugin::status_effect::StatusEffectInstance>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let effects = player.get_active_effects().await;
|
||||
let mut list = Vec::with_capacity(effects.len());
|
||||
for eff in &effects {
|
||||
if let Some(instance) = super::status_effect::to_wasm_status_effect_instance(eff) {
|
||||
list.push(instance);
|
||||
}
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
async fn heal(&mut self, player: Resource<Player>, amount: f32) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.heal(amount).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn damage(&mut self, player: Resource<Player>, amount: f32) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.damage_generic(amount).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn kill(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.kill().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_cooldown(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
group: String,
|
||||
duration_ticks: i32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.start_cooldown(group, duration_ticks).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_cooldown(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
group: String,
|
||||
) -> wasmtime::Result<f32> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.get_cooldown(&group).await)
|
||||
}
|
||||
|
||||
async fn is_on_cooldown(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
group: String,
|
||||
) -> wasmtime::Result<bool> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.is_on_cooldown(&group).await)
|
||||
}
|
||||
|
||||
async fn set_allow_flight(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
allowed: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.set_allow_flight(allowed).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_fly_speed(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
speed: f32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.set_fly_speed(speed).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_walk_speed(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
speed: f32,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.set_walk_speed(speed).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_invulnerable(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
invulnerable: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.set_invulnerable(invulnerable).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_player_time(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
time: u64,
|
||||
relative: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player
|
||||
.set_player_time(
|
||||
&player.living_entity.entity.world.load_full(),
|
||||
time,
|
||||
relative,
|
||||
)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reset_player_time(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player
|
||||
.reset_player_time(&player.living_entity.entity.world.load_full())
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_player_time(&mut self, player: Resource<Player>) -> wasmtime::Result<Option<u64>> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.get_player_time())
|
||||
}
|
||||
|
||||
async fn is_player_time_relative(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
) -> wasmtime::Result<bool> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.is_player_time_relative())
|
||||
}
|
||||
|
||||
async fn set_player_weather(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
weather: crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let w = match weather {
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather::Clear => crate::entity::player::PlayerWeather::Clear,
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather::Downfall => crate::entity::player::PlayerWeather::Downfall,
|
||||
};
|
||||
player.set_player_weather(w);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reset_player_weather(&mut self, player: Resource<Player>) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
player.reset_player_weather();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_player_weather(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
) -> wasmtime::Result<Option<crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather>>{
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.get_player_weather().map(|w| match w {
|
||||
crate::entity::player::PlayerWeather::Clear => crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather::Clear,
|
||||
crate::entity::player::PlayerWeather::Downfall => crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::PlayerWeather::Downfall,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn set_compass_target(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
pos: crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let pos_vec = from_wasm_position(pos);
|
||||
let block_pos = pumpkin_util::math::position::BlockPos::new(
|
||||
pos_vec.x as i32,
|
||||
pos_vec.y as i32,
|
||||
pos_vec.z as i32,
|
||||
);
|
||||
player.set_compass_target(block_pos).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_compass_target(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
) -> wasmtime::Result<
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position,
|
||||
> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let target = player
|
||||
.get_compass_target()
|
||||
.unwrap_or(pumpkin_util::math::position::BlockPos::new(0, 0, 0));
|
||||
let vec3 = pumpkin_util::math::vector3::Vector3::new(
|
||||
f64::from(target.0.x),
|
||||
f64::from(target.0.y),
|
||||
f64::from(target.0.z),
|
||||
);
|
||||
Ok(to_wasm_position(vec3))
|
||||
}
|
||||
|
||||
async fn set_respawn_location(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
pos: crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let pos_vec = from_wasm_position(pos);
|
||||
let block_pos = pumpkin_util::math::position::BlockPos::new(
|
||||
pos_vec.x as i32,
|
||||
pos_vec.y as i32,
|
||||
pos_vec.z as i32,
|
||||
);
|
||||
player.set_respawn_location(block_pos);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_respawn_location(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
) -> wasmtime::Result<
|
||||
Option<
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position,
|
||||
>,
|
||||
> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
Ok(player.get_respawn_location().map(|p| {
|
||||
let vec3 = pumpkin_util::math::vector3::Vector3::new(
|
||||
f64::from(p.0.x),
|
||||
f64::from(p.0.y),
|
||||
f64::from(p.0.z),
|
||||
);
|
||||
to_wasm_position(vec3)
|
||||
}))
|
||||
}
|
||||
|
||||
async fn hide_player(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
other: Resource<Player>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let other = player_from_resource(self, &other)?;
|
||||
player.hide_player(other.gameprofile.id).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn show_player(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
other: Resource<Player>,
|
||||
) -> wasmtime::Result<()> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let other = player_from_resource(self, &other)?;
|
||||
player.show_player(other.gameprofile.id).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn can_see(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
other: Resource<Player>,
|
||||
) -> wasmtime::Result<bool> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let other = player_from_resource(self, &other)?;
|
||||
Ok(player.can_see(&other.gameprofile.id).await)
|
||||
}
|
||||
|
||||
async fn get_target_block(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
max_distance: u32,
|
||||
) -> wasmtime::Result<
|
||||
Option<
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position,
|
||||
>,
|
||||
> {
|
||||
let player = player_from_resource(self, &player)?;
|
||||
let res = player
|
||||
.get_target_block(
|
||||
&player.living_entity.entity.world.load_full(),
|
||||
f64::from(max_distance),
|
||||
)
|
||||
.await;
|
||||
Ok(res.map(|p| {
|
||||
let vec3 = pumpkin_util::math::vector3::Vector3::new(
|
||||
f64::from(p.0.x),
|
||||
f64::from(p.0.y),
|
||||
f64::from(p.0.z),
|
||||
);
|
||||
to_wasm_position(vec3)
|
||||
}))
|
||||
}
|
||||
|
||||
async fn launch_projectile(
|
||||
&mut self,
|
||||
_player: Resource<Player>,
|
||||
_type_: crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::player::ProjectileType,
|
||||
) -> wasmtime::Result<
|
||||
Option<
|
||||
Resource<
|
||||
crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::Entity,
|
||||
>,
|
||||
>,
|
||||
> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn set_tab_list_header_footer(
|
||||
&mut self,
|
||||
player: Resource<Player>,
|
||||
|
||||
@ -0,0 +1,242 @@
|
||||
use crate::plugin::loader::wasm::wasm_host::{
|
||||
state::PluginHostState, wit::v0_1::pumpkin::plugin::status_effect,
|
||||
};
|
||||
|
||||
impl status_effect::Host for PluginHostState {}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub const fn from_wasm_status_effect_type(
|
||||
t: status_effect::StatusEffectType,
|
||||
) -> pumpkin_data::status_effect::EffectType {
|
||||
match t {
|
||||
status_effect::StatusEffectType::Speed => pumpkin_data::status_effect::EffectType::Speed,
|
||||
status_effect::StatusEffectType::Slowness => {
|
||||
pumpkin_data::status_effect::EffectType::Slowness
|
||||
}
|
||||
status_effect::StatusEffectType::Haste => pumpkin_data::status_effect::EffectType::Haste,
|
||||
status_effect::StatusEffectType::MiningFatigue => {
|
||||
pumpkin_data::status_effect::EffectType::MiningFatigue
|
||||
}
|
||||
status_effect::StatusEffectType::Strength => {
|
||||
pumpkin_data::status_effect::EffectType::Strength
|
||||
}
|
||||
status_effect::StatusEffectType::InstantHealth => {
|
||||
pumpkin_data::status_effect::EffectType::InstantHealth
|
||||
}
|
||||
status_effect::StatusEffectType::InstantDamage => {
|
||||
pumpkin_data::status_effect::EffectType::InstantDamage
|
||||
}
|
||||
status_effect::StatusEffectType::JumpBoost => {
|
||||
pumpkin_data::status_effect::EffectType::JumpBoost
|
||||
}
|
||||
status_effect::StatusEffectType::Nausea => pumpkin_data::status_effect::EffectType::Nausea,
|
||||
status_effect::StatusEffectType::Regeneration => {
|
||||
pumpkin_data::status_effect::EffectType::Regeneration
|
||||
}
|
||||
status_effect::StatusEffectType::Resistance => {
|
||||
pumpkin_data::status_effect::EffectType::Resistance
|
||||
}
|
||||
status_effect::StatusEffectType::FireResistance => {
|
||||
pumpkin_data::status_effect::EffectType::FireResistance
|
||||
}
|
||||
status_effect::StatusEffectType::WaterBreathing => {
|
||||
pumpkin_data::status_effect::EffectType::WaterBreathing
|
||||
}
|
||||
status_effect::StatusEffectType::Invisibility => {
|
||||
pumpkin_data::status_effect::EffectType::Invisibility
|
||||
}
|
||||
status_effect::StatusEffectType::Blindness => {
|
||||
pumpkin_data::status_effect::EffectType::Blindness
|
||||
}
|
||||
status_effect::StatusEffectType::NightVision => {
|
||||
pumpkin_data::status_effect::EffectType::NightVision
|
||||
}
|
||||
status_effect::StatusEffectType::Hunger => pumpkin_data::status_effect::EffectType::Hunger,
|
||||
status_effect::StatusEffectType::Weakness => {
|
||||
pumpkin_data::status_effect::EffectType::Weakness
|
||||
}
|
||||
status_effect::StatusEffectType::Poison => pumpkin_data::status_effect::EffectType::Poison,
|
||||
status_effect::StatusEffectType::Wither => pumpkin_data::status_effect::EffectType::Wither,
|
||||
status_effect::StatusEffectType::HealthBoost => {
|
||||
pumpkin_data::status_effect::EffectType::HealthBoost
|
||||
}
|
||||
status_effect::StatusEffectType::Absorption => {
|
||||
pumpkin_data::status_effect::EffectType::Absorption
|
||||
}
|
||||
status_effect::StatusEffectType::Saturation => {
|
||||
pumpkin_data::status_effect::EffectType::Saturation
|
||||
}
|
||||
status_effect::StatusEffectType::Glowing => {
|
||||
pumpkin_data::status_effect::EffectType::Glowing
|
||||
}
|
||||
status_effect::StatusEffectType::Levitation => {
|
||||
pumpkin_data::status_effect::EffectType::Levitation
|
||||
}
|
||||
status_effect::StatusEffectType::Luck => pumpkin_data::status_effect::EffectType::Luck,
|
||||
status_effect::StatusEffectType::Unluck => pumpkin_data::status_effect::EffectType::Unluck,
|
||||
status_effect::StatusEffectType::SlowFalling => {
|
||||
pumpkin_data::status_effect::EffectType::SlowFalling
|
||||
}
|
||||
status_effect::StatusEffectType::ConduitPower => {
|
||||
pumpkin_data::status_effect::EffectType::ConduitPower
|
||||
}
|
||||
status_effect::StatusEffectType::DolphinsGrace => {
|
||||
pumpkin_data::status_effect::EffectType::DolphinsGrace
|
||||
}
|
||||
status_effect::StatusEffectType::BadOmen => {
|
||||
pumpkin_data::status_effect::EffectType::BadOmen
|
||||
}
|
||||
status_effect::StatusEffectType::HeroOfTheVillage => {
|
||||
pumpkin_data::status_effect::EffectType::HeroOfTheVillage
|
||||
}
|
||||
status_effect::StatusEffectType::Darkness => {
|
||||
pumpkin_data::status_effect::EffectType::Darkness
|
||||
}
|
||||
status_effect::StatusEffectType::TrialOmen => {
|
||||
pumpkin_data::status_effect::EffectType::TrialOmen
|
||||
}
|
||||
status_effect::StatusEffectType::RaidOmen => {
|
||||
pumpkin_data::status_effect::EffectType::RaidOmen
|
||||
}
|
||||
status_effect::StatusEffectType::WindCharged => {
|
||||
pumpkin_data::status_effect::EffectType::WindCharged
|
||||
}
|
||||
status_effect::StatusEffectType::Weaving => {
|
||||
pumpkin_data::status_effect::EffectType::Weaving
|
||||
}
|
||||
status_effect::StatusEffectType::Oozing => pumpkin_data::status_effect::EffectType::Oozing,
|
||||
status_effect::StatusEffectType::Infested => {
|
||||
pumpkin_data::status_effect::EffectType::Infested
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub const fn to_wasm_status_effect_type(
|
||||
t: pumpkin_data::status_effect::EffectType,
|
||||
) -> status_effect::StatusEffectType {
|
||||
match t {
|
||||
pumpkin_data::status_effect::EffectType::Speed => status_effect::StatusEffectType::Speed,
|
||||
pumpkin_data::status_effect::EffectType::Slowness => {
|
||||
status_effect::StatusEffectType::Slowness
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Haste => status_effect::StatusEffectType::Haste,
|
||||
pumpkin_data::status_effect::EffectType::MiningFatigue => {
|
||||
status_effect::StatusEffectType::MiningFatigue
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Strength => {
|
||||
status_effect::StatusEffectType::Strength
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::InstantHealth => {
|
||||
status_effect::StatusEffectType::InstantHealth
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::InstantDamage => {
|
||||
status_effect::StatusEffectType::InstantDamage
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::JumpBoost => {
|
||||
status_effect::StatusEffectType::JumpBoost
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Nausea => status_effect::StatusEffectType::Nausea,
|
||||
pumpkin_data::status_effect::EffectType::Regeneration => {
|
||||
status_effect::StatusEffectType::Regeneration
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Resistance => {
|
||||
status_effect::StatusEffectType::Resistance
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::FireResistance => {
|
||||
status_effect::StatusEffectType::FireResistance
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::WaterBreathing => {
|
||||
status_effect::StatusEffectType::WaterBreathing
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Invisibility => {
|
||||
status_effect::StatusEffectType::Invisibility
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Blindness => {
|
||||
status_effect::StatusEffectType::Blindness
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::NightVision => {
|
||||
status_effect::StatusEffectType::NightVision
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Hunger => status_effect::StatusEffectType::Hunger,
|
||||
pumpkin_data::status_effect::EffectType::Weakness => {
|
||||
status_effect::StatusEffectType::Weakness
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Poison => status_effect::StatusEffectType::Poison,
|
||||
pumpkin_data::status_effect::EffectType::Wither => status_effect::StatusEffectType::Wither,
|
||||
pumpkin_data::status_effect::EffectType::HealthBoost => {
|
||||
status_effect::StatusEffectType::HealthBoost
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Absorption => {
|
||||
status_effect::StatusEffectType::Absorption
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Saturation => {
|
||||
status_effect::StatusEffectType::Saturation
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Glowing => {
|
||||
status_effect::StatusEffectType::Glowing
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Levitation => {
|
||||
status_effect::StatusEffectType::Levitation
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Luck => status_effect::StatusEffectType::Luck,
|
||||
pumpkin_data::status_effect::EffectType::Unluck => status_effect::StatusEffectType::Unluck,
|
||||
pumpkin_data::status_effect::EffectType::SlowFalling => {
|
||||
status_effect::StatusEffectType::SlowFalling
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::ConduitPower => {
|
||||
status_effect::StatusEffectType::ConduitPower
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::DolphinsGrace => {
|
||||
status_effect::StatusEffectType::DolphinsGrace
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::BadOmen => {
|
||||
status_effect::StatusEffectType::BadOmen
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::HeroOfTheVillage => {
|
||||
status_effect::StatusEffectType::HeroOfTheVillage
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Darkness => {
|
||||
status_effect::StatusEffectType::Darkness
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::TrialOmen => {
|
||||
status_effect::StatusEffectType::TrialOmen
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::RaidOmen => {
|
||||
status_effect::StatusEffectType::RaidOmen
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::WindCharged => {
|
||||
status_effect::StatusEffectType::WindCharged
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Weaving => {
|
||||
status_effect::StatusEffectType::Weaving
|
||||
}
|
||||
pumpkin_data::status_effect::EffectType::Oozing => status_effect::StatusEffectType::Oozing,
|
||||
pumpkin_data::status_effect::EffectType::Infested => {
|
||||
status_effect::StatusEffectType::Infested
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_wasm_status_effect_instance(
|
||||
effect: &pumpkin_data::potion::Effect,
|
||||
) -> Option<status_effect::StatusEffectInstance> {
|
||||
let name = effect
|
||||
.effect_type
|
||||
.minecraft_name
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(effect.effect_type.minecraft_name);
|
||||
let effect_type_enum = pumpkin_data::status_effect::EffectType::from_name(name)?;
|
||||
let wasm_type = to_wasm_status_effect_type(effect_type_enum);
|
||||
Some(status_effect::StatusEffectInstance {
|
||||
effect_type: wasm_type,
|
||||
duration: u32::try_from(effect.duration).unwrap_or(0),
|
||||
amplifier: effect.amplifier,
|
||||
ambient: effect.ambient,
|
||||
show_particles: effect.show_particles,
|
||||
show_icon: effect.show_icon,
|
||||
})
|
||||
}
|
||||
@ -13,6 +13,7 @@ use crate::block::entities::command_block::CommandBlockEntity as InternalCommand
|
||||
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::common::Position as WitPosition;
|
||||
use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{
|
||||
BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags,
|
||||
BlockPos as WitBlockPos, BlockState as WitBlockState, BoundingBox as WitBoundingBox,
|
||||
@ -677,6 +678,43 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
||||
Ok(entities)
|
||||
}
|
||||
|
||||
async fn strike_lightning(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
pos: WitPosition,
|
||||
effect_only: bool,
|
||||
) -> wasmtime::Result<()> {
|
||||
let world_provider = self.get_world_res(&world)?.provider.clone();
|
||||
let internal_pos = super::events::from_wasm_position(pos);
|
||||
world_provider
|
||||
.strike_lightning(internal_pos, effect_only)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ray_trace_blocks(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
start: WitPosition,
|
||||
end: WitPosition,
|
||||
) -> wasmtime::Result<Option<WitPosition>> {
|
||||
let world_provider = self.get_world_res(&world)?.provider.clone();
|
||||
let start_pos = super::events::from_wasm_position(start);
|
||||
let end_pos = super::events::from_wasm_position(end);
|
||||
let res = world_provider
|
||||
.raycast(start_pos, end_pos, async |pos, w| {
|
||||
!w.get_block_state(pos).is_air()
|
||||
})
|
||||
.await;
|
||||
Ok(res.map(|(p, _)| {
|
||||
super::events::to_wasm_position(pumpkin_util::math::vector3::Vector3::new(
|
||||
f64::from(p.0.x),
|
||||
f64::from(p.0.y),
|
||||
f64::from(p.0.z),
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
async fn get_block_entity(
|
||||
&mut self,
|
||||
world: Resource<World>,
|
||||
|
||||
@ -448,6 +448,12 @@ impl Server {
|
||||
new_worlds.push(world.clone());
|
||||
new_worlds
|
||||
});
|
||||
let mut event =
|
||||
crate::plugin::api::events::world::world_init::WorldInitEvent::new(world.clone());
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current()
|
||||
.block_on(server.plugin_manager.fire(&server, &mut event));
|
||||
});
|
||||
world
|
||||
})
|
||||
.await
|
||||
|
||||
@ -4729,6 +4729,18 @@ impl World {
|
||||
self.spawn_entity(item_entity).await;
|
||||
}
|
||||
|
||||
pub async fn strike_lightning(self: &Arc<Self>, pos: Vector3<f64>, _effect_only: bool) {
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use uuid::Uuid;
|
||||
let lightning = crate::entity::r#type::from_type(
|
||||
&EntityType::LIGHTNING_BOLT,
|
||||
pos,
|
||||
self,
|
||||
Uuid::new_v4(),
|
||||
);
|
||||
self.spawn_entity(lightning).await;
|
||||
}
|
||||
|
||||
/* ItemScatterer.java */
|
||||
pub async fn scatter_inventory(
|
||||
self: &Arc<Self>,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user