From 03e762cd454f9b347be555f787718cf9d5e39879 Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:06:04 -0700 Subject: [PATCH] Add get world and free string --- .../patchbukkit/bridge/NativePatchBukkit.java | 51 +++++++++++++++++++ rust/src/java/native_callbacks/memory.rs | 9 ++++ rust/src/java/native_callbacks/mod.rs | 5 ++ rust/src/java/native_callbacks/world.rs | 48 ++++++++++------- 4 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 rust/src/java/native_callbacks/memory.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 1496cc4..d36780d 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java @@ -16,6 +16,8 @@ public class NativePatchBukkit { private static MethodHandle getAbilitiesNative; private static MethodHandle setAbilitiesNative; private static MethodHandle getLocationNative; + private static MethodHandle getWorldNative; + private static MethodHandle freeStringNative; // Struct layout matching Rust's #[repr(C)] AbilitiesFFI private static final StructLayout ABILITIES_LAYOUT = @@ -223,6 +225,19 @@ public class NativePatchBukkit { ValueLayout.ADDRESS // out pointer to Vec3FFI ) ); + + // const char* rust_get_world(const char* uuid) + getWorldNative = LINKER.downcallHandle( + MemorySegment.ofAddress(getWorldAddr), + FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS) + ); + + // void rust_free_string(const char* str) + freeStringNative = LINKER.downcallHandle( + MemorySegment.ofAddress(freeStringAddr), + FunctionDescriptor.ofVoid(ValueLayout.ADDRESS) + ); + } /** @@ -348,4 +363,40 @@ public class NativePatchBukkit { throw new RuntimeException("Failed to call native getLocation", t); } } + + /** + * Free a string allocated by the Rust side. + */ + private static void freeRustString(MemorySegment ptr) { + try { + freeStringNative.invokeExact(ptr); + } catch (Throwable t) { + throw new RuntimeException("Failed to free Rust string", t); + } + } + + /** + * Get the world UUID for an entity. + */ + public static String getWorld(UUID entityUuid) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment uuidStr = arena.allocateFrom(entityUuid.toString()); + MemorySegment resultPtr = (MemorySegment) getWorldNative.invokeExact(uuidStr); + + if (resultPtr.equals(MemorySegment.NULL)) { + return null; + } + + // Read the string, then free the Rust-allocated memory + try { + String result = resultPtr.reinterpret(Long.MAX_VALUE).getString(0); + return result; + } finally { + freeRustString(resultPtr); + } + } catch (Throwable t) { + throw new RuntimeException("Failed to call native getWorld", t); + } + } + } diff --git a/rust/src/java/native_callbacks/memory.rs b/rust/src/java/native_callbacks/memory.rs new file mode 100644 index 0000000..e095ef8 --- /dev/null +++ b/rust/src/java/native_callbacks/memory.rs @@ -0,0 +1,9 @@ +use std::ffi::{CString, c_char}; + +pub extern "C" fn rust_free_string(ptr: *mut c_char) { + if !ptr.is_null() { + unsafe { + drop(CString::from_raw(ptr)); + } + } +} diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index 84306d5..50c2d6c 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -7,6 +7,7 @@ use pumpkin::plugin::Context; pub mod abilities; pub mod events; pub mod location; +pub mod memory; pub mod message; pub mod utils; pub mod world; @@ -39,6 +40,8 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { 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; + let free_string_addr = memory::rust_free_string as *const () as i64; + let get_world_addr = world::rust_get_world as *const () as i64; jvm.invoke_static( "org.patchbukkit.bridge.NativePatchBukkit", @@ -49,6 +52,8 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { InvocationArg::try_from(get_abilities_addr)?.into_primitive()?, InvocationArg::try_from(set_abilities_addr)?.into_primitive()?, InvocationArg::try_from(get_location_addr)?.into_primitive()?, + InvocationArg::try_from(free_string_addr)?.into_primitive()?, + InvocationArg::try_from(get_world_addr)?.into_primitive()?, ], )?; diff --git a/rust/src/java/native_callbacks/world.rs b/rust/src/java/native_callbacks/world.rs index 544c9f4..d9403e5 100644 --- a/rust/src/java/native_callbacks/world.rs +++ b/rust/src/java/native_callbacks/world.rs @@ -4,24 +4,36 @@ use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector}; use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; -pub extern "C" fn rust_get_world(uuid_ptr: *const c_char) -> *const c_char { - let uuid_str = get_string(uuid_ptr); - if let Some(ctx) = CALLBACK_CONTEXT.get() { - let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); - let entity = ctx - .plugin_context - .server - .select_entities(&TargetSelector::new(EntitySelectorType::Uuid(uuid)), None); - if entity.len() == 1 { - let entity = entity.first().unwrap().get_entity(); - let world = entity.world.load(); - - let cstring = match CString::new(world.dimension.minecraft_name) { - Ok(s) => s, - Err(_) => return std::ptr::null(), - }; - } +pub extern "C" fn rust_get_world(entity_uuid_ptr: *const c_char) -> *mut c_char { + if entity_uuid_ptr.is_null() { + return std::ptr::null_mut(); } - std::ptr::null() + let entity_uuid = get_string(entity_uuid_ptr); + + let Some(ctx) = CALLBACK_CONTEXT.get() else { + return std::ptr::null_mut(); + }; + + let Ok(uuid) = uuid::Uuid::parse_str(&entity_uuid) else { + return std::ptr::null_mut(); + }; + + let entities = ctx + .plugin_context + .server + .select_entities(&TargetSelector::new(EntitySelectorType::Uuid(uuid)), None); + + if entities.len() != 1 { + return std::ptr::null_mut(); + } + + let entity = entities.first().unwrap().get_entity(); + let world = entity.world.load(); + let world_uuid = world.uuid.to_string(); + + match CString::new(world_uuid) { + Ok(cstring) => cstring.into_raw(), // Transfers ownership to caller + Err(_) => std::ptr::null_mut(), + } }