From a8b14af252002454f66f40b0da63e08f45c2a338 Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Sat, 31 Jan 2026 06:07:57 -0700 Subject: [PATCH] Add get location --- .../patchbukkit/bridge/NativePatchBukkit.java | 58 ++++++++++++++++++- .../patchbukkit/entity/PatchBukkitEntity.java | 14 +++-- rust/src/java/native_callbacks/abilities.rs | 8 +-- rust/src/java/native_callbacks/location.rs | 32 ++++++++++ rust/src/java/native_callbacks/message.rs | 9 ++- rust/src/java/native_callbacks/mod.rs | 4 ++ rust/src/java/native_callbacks/utils.rs | 5 ++ 7 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 rust/src/java/native_callbacks/location.rs create mode 100644 rust/src/java/native_callbacks/utils.rs diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java index e4ffb61..a5bcc7d 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java @@ -15,6 +15,7 @@ public class NativePatchBukkit { private static MethodHandle registerEventNative; private static MethodHandle getAbilitiesNative; private static MethodHandle setAbilitiesNative; + private static MethodHandle getLocationNative; // Struct layout matching Rust's #[repr(C)] AbilitiesFFI private static final StructLayout ABILITIES_LAYOUT = MemoryLayout.structLayout( @@ -44,6 +45,25 @@ public class NativePatchBukkit { private static final VarHandle WALK_SPEED = ABILITIES_LAYOUT.varHandle( MemoryLayout.PathElement.groupElement("walk_speed")); + + // Struct layout for Vec3 + private static final StructLayout VEC3_LAYOUT = MemoryLayout.structLayout( + ValueLayout.JAVA_DOUBLE.withName("x"), + ValueLayout.JAVA_DOUBLE.withName("y"), + ValueLayout.JAVA_DOUBLE.withName("z") + ); + + // VarHandles for Vec3 fields + private static final VarHandle VEC3_X = VEC3_LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("x")); + private static final VarHandle VEC3_Y = VEC3_LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("y")); + private static final VarHandle VEC3_Z = VEC3_LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("z")); + + public record Vec3(double x, double y, double z) {} + + /** * Java record to hold player abilities. */ @@ -89,7 +109,7 @@ public class NativePatchBukkit { /** * Called from Rust during initialization to register native function pointers. */ - public static void initCallbacks(long sendMessageAddr, long registerEventAddr, long getAbilitiesAddr, long setAbilitiesAddr) { + public static void initCallbacks(long sendMessageAddr, long registerEventAddr, long getAbilitiesAddr, long setAbilitiesAddr, long getLocationAddr) { // void rust_send_message(const char* uuid, const char* message) sendMessageNative = LINKER.downcallHandle( MemorySegment.ofAddress(sendMessageAddr), @@ -121,6 +141,16 @@ public class NativePatchBukkit { ValueLayout.ADDRESS // pointer to AbilitiesFFI ) ); + + // bool rust_get_location(const char* uuid, Vec3FFI* out) + getLocationNative = LINKER.downcallHandle( + MemorySegment.ofAddress(getLocationAddr), + FunctionDescriptor.of( + ValueLayout.JAVA_BOOLEAN, // return type + ValueLayout.ADDRESS, // uuid string + ValueLayout.ADDRESS // out pointer to Vec3FFI + ) + ); } /** @@ -205,4 +235,30 @@ public class NativePatchBukkit { } } + /** + * Get a player's location by UUID. + * + * @param uuid The player's UUID + * @return The player's position, or null if player not found + */ + public static Vec3 getLocation(UUID uuid) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment uuidStr = arena.allocateFrom(uuid.toString()); + MemorySegment outStruct = arena.allocate(VEC3_LAYOUT); + + boolean success = (boolean) getLocationNative.invokeExact(uuidStr, outStruct); + + if (!success) { + return null; + } + + return new Vec3( + (double) VEC3_X.get(outStruct, 0L), + (double) VEC3_Y.get(outStruct, 0L), + (double) VEC3_Z.get(outStruct, 0L) + ); + } catch (Throwable t) { + throw new RuntimeException("Failed to call native getLocation", t); + } + } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java index 64b1e52..f798612 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -37,6 +37,7 @@ import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.patchbukkit.bridge.NativePatchBukkit; import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.datacomponent.DataComponentType.Valued; @@ -259,14 +260,19 @@ public class PatchBukkitEntity implements Entity { @Override public @NotNull Location getLocation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLocation'"); + var loc = NativePatchBukkit.getLocation(this.uuid); + return new Location(this.getWorld(), loc.x(), loc.y(), loc.z()); } @Override public @Nullable Location getLocation(@Nullable Location loc) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLocation'"); + if (loc == null) { + return this.getLocation(); + } + + var newLoc = this.getLocation(); + loc.set(newLoc.x(), newLoc.y(), newLoc.z()); + return newLoc; } @Override diff --git a/rust/src/java/native_callbacks/abilities.rs b/rust/src/java/native_callbacks/abilities.rs index c63d184..bab6d5e 100644 --- a/rust/src/java/native_callbacks/abilities.rs +++ b/rust/src/java/native_callbacks/abilities.rs @@ -1,9 +1,9 @@ -use std::ffi::{CStr, c_char}; +use std::ffi::c_char; use pumpkin::entity::player::Abilities; use tokio::sync::MutexGuard; -use crate::java::native_callbacks::CALLBACK_CONTEXT; +use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; #[repr(C)] pub struct AbilitiesFFI { @@ -34,7 +34,7 @@ pub extern "C" fn rust_set_abilities( uuid_ptr: *const c_char, abilities: *mut AbilitiesFFI, ) -> bool { - let uuid_str = unsafe { CStr::from_ptr(uuid_ptr).to_string_lossy().into_owned() }; + let uuid_str = get_string(uuid_ptr); if let Some(ctx) = CALLBACK_CONTEXT.get() { let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); let player = ctx.plugin_context.server.get_player_by_uuid(uuid); @@ -62,7 +62,7 @@ pub extern "C" fn rust_set_abilities( } pub extern "C" fn rust_get_abilities(uuid_ptr: *const c_char, out: *mut AbilitiesFFI) -> bool { - let uuid_str = unsafe { CStr::from_ptr(uuid_ptr).to_string_lossy().into_owned() }; + let uuid_str = get_string(uuid_ptr); if let Some(ctx) = CALLBACK_CONTEXT.get() { let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); let player = ctx.plugin_context.server.get_player_by_uuid(uuid); diff --git a/rust/src/java/native_callbacks/location.rs b/rust/src/java/native_callbacks/location.rs new file mode 100644 index 0000000..32e1437 --- /dev/null +++ b/rust/src/java/native_callbacks/location.rs @@ -0,0 +1,32 @@ +use std::ffi::c_char; + +use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; + +#[repr(C)] +pub struct Vec3FFI { + pub x: f64, + pub y: f64, + pub z: f64, +} + +pub extern "C" fn rust_get_location(uuid_ptr: *const c_char, out: *mut Vec3FFI) -> bool { + let uuid_str = get_string(uuid_ptr); + if let Some(ctx) = CALLBACK_CONTEXT.get() { + let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); + let player = ctx.plugin_context.server.get_player_by_uuid(uuid); + if let Some(player) = player { + let position = + tokio::task::block_in_place(|| ctx.runtime.block_on(async { player.position() })); + + unsafe { + (*out).x = position.x; + (*out).y = position.y; + (*out).z = position.z; + } + + return true; + } + } + + false +} diff --git a/rust/src/java/native_callbacks/message.rs b/rust/src/java/native_callbacks/message.rs index 45643aa..8931367 100644 --- a/rust/src/java/native_callbacks/message.rs +++ b/rust/src/java/native_callbacks/message.rs @@ -1,14 +1,13 @@ -use std::ffi::{CStr, c_char}; +use std::ffi::c_char; use pumpkin_util::text::TextComponent; -use crate::java::native_callbacks::CALLBACK_CONTEXT; +use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; #[unsafe(no_mangle)] pub extern "C" fn rust_send_message(uuid_ptr: *const c_char, message_ptr: *const c_char) { - // This into_owned might not seem to make any sense, but it's necessary to ensure that the string is owned by the Rust code and can move into the runtime. - let uuid_str = unsafe { CStr::from_ptr(uuid_ptr).to_string_lossy().into_owned() }; - let message = unsafe { CStr::from_ptr(message_ptr).to_string_lossy().into_owned() }; + let uuid_str = get_string(uuid_ptr); + let message = get_string(message_ptr); if let Some(ctx) = CALLBACK_CONTEXT.get() { let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index 4d9d30c..1131b07 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -6,7 +6,9 @@ use pumpkin::plugin::Context; pub mod abilities; pub mod events; +pub mod location; pub mod message; +pub mod utils; static CALLBACK_CONTEXT: OnceLock = OnceLock::new(); @@ -35,6 +37,7 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { let register_event_addr = events::rust_register_event as *const () as i64; let get_abilities_addr = abilities::rust_get_abilities as *const () as i64; let set_abilities_addr = abilities::rust_set_abilities as *const () as i64; + let get_location_addr = location::rust_get_location as *const () as i64; jvm.invoke_static( "org.patchbukkit.bridge.NativePatchBukkit", @@ -44,6 +47,7 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { InvocationArg::try_from(register_event_addr)?.into_primitive()?, InvocationArg::try_from(get_abilities_addr)?.into_primitive()?, InvocationArg::try_from(set_abilities_addr)?.into_primitive()?, + InvocationArg::try_from(get_location_addr)?.into_primitive()?, ], )?; diff --git a/rust/src/java/native_callbacks/utils.rs b/rust/src/java/native_callbacks/utils.rs new file mode 100644 index 0000000..12aaeb7 --- /dev/null +++ b/rust/src/java/native_callbacks/utils.rs @@ -0,0 +1,5 @@ +use std::ffi::{CStr, c_char}; + +pub fn get_string(str_ptr: *const c_char) -> String { + unsafe { CStr::from_ptr(str_ptr).to_string_lossy().into_owned() } +}