Add get location

This commit is contained in:
Bjorn Beishline 2026-01-31 06:07:57 -07:00
parent 609f894c56
commit a8b14af252
7 changed files with 116 additions and 14 deletions

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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
}

View File

@ -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();

View File

@ -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<CallbackContext> = 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()?,
],
)?;

View File

@ -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() }
}