Add get world and free string

This commit is contained in:
Bjorn Beishline 2026-01-31 13:06:04 -07:00
parent 4a906ae3dc
commit 03e762cd45
4 changed files with 95 additions and 18 deletions

View File

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

View File

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

View File

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

View File

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