781 lines
27 KiB
Rust
781 lines
27 KiB
Rust
use std::str::FromStr;
|
|
|
|
use pumpkin::entity::EntityBase;
|
|
|
|
use crate::{
|
|
java::native_callbacks::{CALLBACK_CONTEXT, utils::with_player},
|
|
proto::patchbukkit::{
|
|
common::Uuid,
|
|
entity::{
|
|
DamageEntityRequest, EntityHealthResponse, GetCooldownRequest, GetCooldownResponse,
|
|
GetExperienceResponse, GetFoodLevelResponse, GetPlayerPoseStateResponse,
|
|
KickPlayerRequest, PlayerConnectionInfoResponse, SendActionBarRequest,
|
|
SendBlockChangeRequest, SendGameEventRequest, SendResourcePackRequest,
|
|
SendTitleRequest, SetCompassTargetRequest, SetCooldownRequest, SetDisplayNameRequest,
|
|
SetEntityHealthRequest, SetEntityVelocityRequest, SetExhaustionRequest,
|
|
SetExperienceRequest, SetFoodLevelRequest, SetOpRequest,
|
|
SetPlayerListHeaderFooterRequest, SetPlayerListNameRequest, SetPlayerTimeRequest,
|
|
SetPlayerWeatherRequest, SetRespawnPointRequest, SetSaturationRequest,
|
|
SetSneakingRequest, SetSprintingRequest, StopSoundRequest, TeleportEntityRequest,
|
|
},
|
|
},
|
|
};
|
|
|
|
pub fn ffi_native_bridge_get_entity_health_impl(request: Uuid) -> Option<EntityHealthResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let health = f64::from(player.living_entity.health.load());
|
|
|
|
EntityHealthResponse {
|
|
health,
|
|
max_health: 20.0,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_entity_health_impl(request: SetEntityHealthRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let health = request.health as f32;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player.set_health(health).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_damage_entity_impl(request: DamageEntityRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let amount = request.amount as f32;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let current = player.living_entity.health.load();
|
|
player.set_health((current - amount).max(0.0)).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_entity_velocity_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::entity::EntityVelocityResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let vel = player.living_entity.entity.velocity.load();
|
|
crate::proto::patchbukkit::entity::EntityVelocityResponse {
|
|
x: vel.x,
|
|
y: vel.y,
|
|
z: vel.z,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_entity_velocity_impl(request: SetEntityVelocityRequest) -> Option<()> {
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let velocity = pumpkin_util::math::vector3::Vector3::new(request.x, request.y, request.z);
|
|
player.living_entity.entity.set_velocity(velocity);
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_entity_pose_impl(
|
|
_request: crate::proto::patchbukkit::entity::SetEntityPoseRequest,
|
|
) -> Option<()> {
|
|
Some(())
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_gamemode_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::entity::GetGamemodeResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let gamemode = player.gamemode.load();
|
|
crate::proto::patchbukkit::entity::GetGamemodeResponse {
|
|
gamemode: gamemode as i32,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_gamemode_impl(
|
|
request: crate::proto::patchbukkit::entity::SetGamemodeRequest,
|
|
) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let gamemode_val = request.gamemode as i8;
|
|
if let Ok(gamemode) = pumpkin_util::gamemode::GameMode::try_from(gamemode_val) {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player.set_gamemode(gamemode).await;
|
|
});
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_teleport_entity_impl(request: TeleportEntityRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
let loc = request.location?;
|
|
let pos = loc.position?;
|
|
let yaw = loc.yaw;
|
|
let pitch = loc.pitch;
|
|
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let position = pumpkin_util::math::vector3::Vector3::new(pos.x, pos.y, pos.z);
|
|
let world = player.living_entity.entity.world.load_full();
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player
|
|
.teleport(position, Some(yaw), Some(pitch), world)
|
|
.await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_is_on_ground_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::entity::IsOnGroundResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let on_ground = player
|
|
.living_entity
|
|
.entity
|
|
.on_ground
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
crate::proto::patchbukkit::entity::IsOnGroundResponse { on_ground }
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_player_locale_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::entity::GetPlayerLocaleResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let config = player.config.load();
|
|
crate::proto::patchbukkit::entity::GetPlayerLocaleResponse {
|
|
locale: config.locale.clone(),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_is_op_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::entity::IsOpResponse> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
let player_uuid = uuid::Uuid::parse_str(&request.value).ok()?;
|
|
|
|
let is_op = with_player(Some(&request), |player| {
|
|
let perm_lvl = player.permission_lvl.load();
|
|
let op_lvl = ctx.plugin_context.server.basic_config.op_permission_level;
|
|
let mut is_op = perm_lvl >= op_lvl || perm_lvl > pumpkin_util::PermissionLvl::Zero;
|
|
|
|
if !is_op
|
|
&& let Ok(op_config) = ctx.plugin_context.server.data.operator_config.try_read()
|
|
&& op_config.get_entry(&player_uuid).is_some()
|
|
{
|
|
is_op = true;
|
|
}
|
|
|
|
is_op
|
|
})
|
|
.unwrap_or_else(|| {
|
|
ctx.plugin_context
|
|
.server
|
|
.data
|
|
.operator_config
|
|
.try_read()
|
|
.is_ok_and(|ops| ops.get_entry(&player_uuid).is_some())
|
|
});
|
|
|
|
Some(crate::proto::patchbukkit::entity::IsOpResponse { is_op })
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_op_impl(request: SetOpRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
let is_op = request.is_op;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let op_lvl = ctx.plugin_context.server.basic_config.op_permission_level;
|
|
let lvl = if is_op {
|
|
op_lvl
|
|
} else {
|
|
pumpkin_util::PermissionLvl::Zero
|
|
};
|
|
player.permission_lvl.store(lvl);
|
|
});
|
|
Some(())
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_food_level_impl(request: Uuid) -> Option<GetFoodLevelResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let food_level = i32::from(player.hunger_manager.level.load());
|
|
let saturation = player.hunger_manager.saturation.load();
|
|
let exhaustion = player.hunger_manager.get_exhaustion();
|
|
GetFoodLevelResponse {
|
|
food_level,
|
|
saturation,
|
|
exhaustion,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_food_level_impl(request: SetFoodLevelRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let food_level = request.food_level as u8;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player.hunger_manager.set_level(food_level);
|
|
player.send_health().await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_saturation_impl(request: SetSaturationRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let saturation = request.saturation;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player.hunger_manager.set_saturation(saturation);
|
|
player.send_health().await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_exhaustion_impl(request: SetExhaustionRequest) -> Option<()> {
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
player.hunger_manager.set_exhaustion(request.exhaustion);
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_experience_impl(request: Uuid) -> Option<GetExperienceResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let level = player.get_experience_level();
|
|
let progress = player.get_experience_progress();
|
|
let total_experience = player.get_total_experience();
|
|
GetExperienceResponse {
|
|
level,
|
|
progress,
|
|
total_experience,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_experience_impl(request: SetExperienceRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let level = request.level;
|
|
let progress = request.progress;
|
|
let total_experience = request.total_experience;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player
|
|
.set_experience(level, progress, total_experience)
|
|
.await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_kick_player_impl(request: KickPlayerRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let msg = request.message;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let text = pumpkin_util::text::TextComponent::from_legacy_string(&msg);
|
|
player
|
|
.kick(pumpkin::net::DisconnectReason::Kicked, text)
|
|
.await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_send_title_impl(request: SendTitleRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let title = request.title;
|
|
let subtitle = request.subtitle;
|
|
let fade_in = request.fade_in;
|
|
let stay = request.stay;
|
|
let fade_out = request.fade_out;
|
|
ctx.runtime.spawn(async move {
|
|
if fade_in >= 0 && stay >= 0 && fade_out >= 0 {
|
|
player.send_title_animation(fade_in, stay, fade_out).await;
|
|
}
|
|
if !title.is_empty() {
|
|
let text = pumpkin_util::text::TextComponent::from_legacy_string(&title);
|
|
player
|
|
.show_title(&text, &pumpkin::entity::player::TitleMode::Title)
|
|
.await;
|
|
}
|
|
if !subtitle.is_empty() {
|
|
let text = pumpkin_util::text::TextComponent::from_legacy_string(&subtitle);
|
|
player
|
|
.show_title(&text, &pumpkin::entity::player::TitleMode::SubTitle)
|
|
.await;
|
|
}
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_send_action_bar_impl(request: SendActionBarRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let msg = request.message;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let text = pumpkin_util::text::TextComponent::from_legacy_string(&msg);
|
|
player.send_system_message_raw(&text, true).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_reset_title_impl(request: Uuid) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(Some(&request), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let text = pumpkin_util::text::TextComponent::text("");
|
|
player
|
|
.show_title(&text, &pumpkin::entity::player::TitleMode::Title)
|
|
.await;
|
|
player
|
|
.show_title(&text, &pumpkin::entity::player::TitleMode::SubTitle)
|
|
.await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_display_name_impl(request: SetDisplayNameRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let name = request.display_name;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let comp = if name.is_empty() {
|
|
None
|
|
} else {
|
|
Some(pumpkin_util::text::TextComponent::from_legacy_string(&name))
|
|
};
|
|
player.set_display_name(comp).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_player_list_name_impl(
|
|
request: SetPlayerListNameRequest,
|
|
) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let name = request.list_name;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let comp = if name.is_empty() {
|
|
None
|
|
} else {
|
|
Some(pumpkin_util::text::TextComponent::from_legacy_string(&name))
|
|
};
|
|
player.set_tab_list_name(comp).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_player_list_header_footer_impl(
|
|
request: SetPlayerListHeaderFooterRequest,
|
|
) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let header = request.header;
|
|
let footer = request.footer;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let h = pumpkin_util::text::TextComponent::from_legacy_string(&header);
|
|
let f = pumpkin_util::text::TextComponent::from_legacy_string(&footer);
|
|
player.set_tab_list_header_footer(h, f).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_player_pose_state_impl(
|
|
request: Uuid,
|
|
) -> Option<GetPlayerPoseStateResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let is_sneaking = player
|
|
.living_entity
|
|
.entity
|
|
.sneaking
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
let is_sprinting = player
|
|
.living_entity
|
|
.entity
|
|
.sprinting
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
let is_gliding = player
|
|
.living_entity
|
|
.entity
|
|
.fall_flying
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
let is_swimming = player
|
|
.living_entity
|
|
.entity
|
|
.swimming
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
let is_sleeping = player.sleeping_since.load().is_some();
|
|
GetPlayerPoseStateResponse {
|
|
is_sneaking,
|
|
is_sprinting,
|
|
is_gliding,
|
|
is_swimming,
|
|
is_sleeping,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_sneaking_impl(request: SetSneakingRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let sneaking = request.sneaking;
|
|
ctx.runtime.spawn(async move {
|
|
player.living_entity.entity.set_sneaking(sneaking).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_sprinting_impl(request: SetSprintingRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let sprinting = request.sprinting;
|
|
ctx.runtime.spawn(async move {
|
|
player.living_entity.entity.set_sprinting(sprinting).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_player_time_impl(request: SetPlayerTimeRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let time = request.time;
|
|
let relative = request.relative;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let packet = pumpkin_protocol::java::client::play::CUpdateTime::new(0, time, relative);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_reset_player_time_impl(request: Uuid) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(Some(&request), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let world_time = player.world().level_time.lock().await.time_of_day;
|
|
let packet = pumpkin_protocol::java::client::play::CUpdateTime::new(
|
|
world_time, world_time, true,
|
|
);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_player_weather_impl(request: SetPlayerWeatherRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let weather = request.weather;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let event = if weather == 1 {
|
|
pumpkin_protocol::java::client::play::GameEvent::BeginRaining
|
|
} else {
|
|
pumpkin_protocol::java::client::play::GameEvent::EndRaining
|
|
};
|
|
let packet = pumpkin_protocol::java::client::play::CGameEvent::new(event, 0.0);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_reset_player_weather_impl(request: Uuid) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(Some(&request), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let is_raining = player.world().is_raining().await;
|
|
let event = if is_raining {
|
|
pumpkin_protocol::java::client::play::GameEvent::BeginRaining
|
|
} else {
|
|
pumpkin_protocol::java::client::play::GameEvent::EndRaining
|
|
};
|
|
let packet = pumpkin_protocol::java::client::play::CGameEvent::new(event, 0.0);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_compass_target_impl(request: SetCompassTargetRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
let pos = request.position?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let block_pos = pumpkin_util::math::position::BlockPos::new(
|
|
pos.x as i32,
|
|
pos.y as i32,
|
|
pos.z as i32,
|
|
);
|
|
let dimension_name = player.world().dimension.minecraft_name.to_string();
|
|
let packet = pumpkin_protocol::java::client::play::CPlayerSpawnPosition::new(
|
|
block_pos,
|
|
0.0,
|
|
0.0,
|
|
dimension_name,
|
|
);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_compass_target_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::common::Vec3> {
|
|
with_player(Some(&request), |player| {
|
|
let info = player.world().level_info.load();
|
|
crate::proto::patchbukkit::common::Vec3 {
|
|
x: info.spawn_x as f64,
|
|
y: info.spawn_y as f64,
|
|
z: info.spawn_z as f64,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_respawn_point_impl(request: SetRespawnPointRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
let pos = request.position?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let block_pos =
|
|
pumpkin_util::math::position::BlockPos::new(pos.x as i32, pos.y as i32, pos.z as i32);
|
|
let yaw = request.yaw;
|
|
let force = request.force;
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
let dim = player.world().dimension.clone();
|
|
player
|
|
.set_respawn_point(dim, block_pos, yaw, 0.0, force)
|
|
.await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_respawn_point_impl(
|
|
request: Uuid,
|
|
) -> Option<crate::proto::patchbukkit::common::Location> {
|
|
with_player(Some(&request), |player| {
|
|
let (pos, yaw, world_uuid) = if let Ok(guard) = player.respawn_point.try_lock() {
|
|
match *guard {
|
|
Some(ref pt) => (
|
|
crate::proto::patchbukkit::common::Vec3 {
|
|
x: pt.position.0.x as f64,
|
|
y: pt.position.0.y as f64,
|
|
z: pt.position.0.z as f64,
|
|
},
|
|
pt.yaw,
|
|
player.world().uuid.to_string(),
|
|
),
|
|
None => {
|
|
let info = player.world().level_info.load();
|
|
(
|
|
crate::proto::patchbukkit::common::Vec3 {
|
|
x: info.spawn_x as f64,
|
|
y: info.spawn_y as f64,
|
|
z: info.spawn_z as f64,
|
|
},
|
|
info.spawn_yaw,
|
|
player.world().uuid.to_string(),
|
|
)
|
|
}
|
|
}
|
|
} else {
|
|
let info = player.world().level_info.load();
|
|
(
|
|
crate::proto::patchbukkit::common::Vec3 {
|
|
x: info.spawn_x as f64,
|
|
y: info.spawn_y as f64,
|
|
z: info.spawn_z as f64,
|
|
},
|
|
info.spawn_yaw,
|
|
player.world().uuid.to_string(),
|
|
)
|
|
};
|
|
crate::proto::patchbukkit::common::Location {
|
|
position: Some(pos),
|
|
yaw,
|
|
pitch: 0.0,
|
|
world: Some(crate::proto::patchbukkit::common::World {
|
|
uuid: Some(crate::proto::patchbukkit::common::Uuid { value: world_uuid }),
|
|
}),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_stop_sound_impl(request: StopSoundRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let sound = if request.sound.is_empty() {
|
|
None
|
|
} else {
|
|
pumpkin_util::resource_location::ResourceLocation::from_str(&request.sound).ok()
|
|
};
|
|
let category = if request.category.is_empty() {
|
|
None
|
|
} else {
|
|
pumpkin_data::sound::SoundCategory::from_name(&request.category.to_lowercase())
|
|
};
|
|
ctx.runtime.spawn(async move {
|
|
player.stop_sound(sound, category).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_send_block_change_impl(request: SendBlockChangeRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let pos = pumpkin_util::math::position::BlockPos::new(request.x, request.y, request.z);
|
|
let state_str = request.block_state;
|
|
let clean_key = state_str
|
|
.split('[')
|
|
.next()
|
|
.unwrap_or(&state_str)
|
|
.trim_start_matches("minecraft:");
|
|
let state_id = if let Some(b) = pumpkin_data::Block::from_registry_key(clean_key) {
|
|
b.default_state.id
|
|
} else {
|
|
pumpkin_data::BlockStateId::new_or_air(0)
|
|
};
|
|
ctx.runtime.spawn(async move {
|
|
let packet = pumpkin_protocol::java::client::play::CBlockUpdate::new(
|
|
pos,
|
|
pumpkin_protocol::codec::var_int::VarInt(state_id.as_u16() as i32),
|
|
);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_send_resource_pack_impl(request: SendResourcePackRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let url = request.url;
|
|
let prompt = request.prompt;
|
|
let required = request.required;
|
|
ctx.runtime.spawn(async move {
|
|
let prompt_comp = if prompt.is_empty() {
|
|
None
|
|
} else {
|
|
Some(pumpkin_util::text::TextComponent::from_legacy_string(
|
|
&prompt,
|
|
))
|
|
};
|
|
let id = uuid::Uuid::new_v4();
|
|
let packet = pumpkin_protocol::java::client::play::CAddResourcePack::new(
|
|
&id,
|
|
&url,
|
|
"",
|
|
required,
|
|
prompt_comp,
|
|
);
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_player_connection_info_impl(
|
|
request: Uuid,
|
|
) -> Option<PlayerConnectionInfoResponse> {
|
|
with_player(Some(&request), |player| {
|
|
let ping = player.ping.load(std::sync::atomic::Ordering::Relaxed) as i32;
|
|
let addr = player.client.address();
|
|
PlayerConnectionInfoResponse {
|
|
ping,
|
|
address: addr.ip().to_string(),
|
|
port: addr.port() as i32,
|
|
client_brand: "vanilla".to_string(),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_send_game_event_impl(request: SendGameEventRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let event_type = request.event_type as u8;
|
|
let value = request.value;
|
|
ctx.runtime.spawn(async move {
|
|
let packet = pumpkin_protocol::java::client::play::CGameEvent {
|
|
event: event_type,
|
|
value,
|
|
};
|
|
player.send_client_packet(&packet).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_set_cooldown_impl(request: SetCooldownRequest) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let player = player.clone();
|
|
let group = request.item_group;
|
|
let duration = request.duration_ticks;
|
|
ctx.runtime.spawn(async move {
|
|
player.start_cooldown(group, duration).await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_get_cooldown_impl(
|
|
request: GetCooldownRequest,
|
|
) -> Option<GetCooldownResponse> {
|
|
with_player(request.uuid.as_ref(), |player| {
|
|
let (cooldown, is_on_cooldown) = if let Ok(guard) = player.item_cooldowns.try_lock() {
|
|
if let Some(cooldown) = guard.get(&request.item_group) {
|
|
let current_tick = player
|
|
.tick_counter
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
let elapsed = current_tick - cooldown.start_tick;
|
|
if elapsed < cooldown.duration {
|
|
(1.0 - (elapsed as f32 / cooldown.duration as f32), true)
|
|
} else {
|
|
(0.0, false)
|
|
}
|
|
} else {
|
|
(0.0, false)
|
|
}
|
|
} else {
|
|
(0.0, false)
|
|
};
|
|
GetCooldownResponse {
|
|
cooldown,
|
|
is_on_cooldown,
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_open_ender_chest_impl(request: Uuid) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(Some(&request), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player.open_ender_chest().await;
|
|
});
|
|
})
|
|
}
|
|
|
|
pub fn ffi_native_bridge_update_inventory_impl(request: Uuid) -> Option<()> {
|
|
let ctx = CALLBACK_CONTEXT.get()?;
|
|
with_player(Some(&request), |player| {
|
|
let player = player.clone();
|
|
ctx.runtime.spawn(async move {
|
|
player
|
|
.on_screen_handler_opened(player.player_screen_handler.clone())
|
|
.await;
|
|
});
|
|
})
|
|
}
|