From 4a906ae3dc076c3a06aeb5ed9feac5bf45c55a2a Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:50:51 -0700 Subject: [PATCH] Use entity instead of player --- .../patchbukkit/bridge/NativePatchBukkit.java | 229 ++++++++++++------ rust/Cargo.lock | 8 +- rust/src/java/native_callbacks/location.rs | 12 +- rust/src/java/native_callbacks/mod.rs | 1 + rust/src/java/native_callbacks/world.rs | 27 +++ 5 files changed, 199 insertions(+), 78 deletions(-) create mode 100644 rust/src/java/native_callbacks/world.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 a5bcc7d..1496cc4 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java @@ -4,11 +4,11 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.VarHandle; import java.util.UUID; - import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; public class NativePatchBukkit { + private static final Linker LINKER = Linker.nativeLinker(); private static MethodHandle sendMessageNative; @@ -18,33 +18,41 @@ public class NativePatchBukkit { private static MethodHandle getLocationNative; // Struct layout matching Rust's #[repr(C)] AbilitiesFFI - private static final StructLayout ABILITIES_LAYOUT = MemoryLayout.structLayout( - ValueLayout.JAVA_BOOLEAN.withName("invulnerable"), - ValueLayout.JAVA_BOOLEAN.withName("flying"), - ValueLayout.JAVA_BOOLEAN.withName("allow_flying"), - ValueLayout.JAVA_BOOLEAN.withName("creative"), - ValueLayout.JAVA_BOOLEAN.withName("allow_modify_world"), - MemoryLayout.paddingLayout(3), // Padding to align f32 to 4-byte boundary - ValueLayout.JAVA_FLOAT.withName("fly_speed"), - ValueLayout.JAVA_FLOAT.withName("walk_speed") - ); + private static final StructLayout ABILITIES_LAYOUT = + MemoryLayout.structLayout( + ValueLayout.JAVA_BOOLEAN.withName("invulnerable"), + ValueLayout.JAVA_BOOLEAN.withName("flying"), + ValueLayout.JAVA_BOOLEAN.withName("allow_flying"), + ValueLayout.JAVA_BOOLEAN.withName("creative"), + ValueLayout.JAVA_BOOLEAN.withName("allow_modify_world"), + MemoryLayout.paddingLayout(3), // Padding to align f32 to 4-byte boundary + ValueLayout.JAVA_FLOAT.withName("fly_speed"), + ValueLayout.JAVA_FLOAT.withName("walk_speed") + ); // VarHandles for accessing struct fields private static final VarHandle INVULNERABLE = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("invulnerable")); + MemoryLayout.PathElement.groupElement("invulnerable") + ); private static final VarHandle FLYING = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("flying")); + MemoryLayout.PathElement.groupElement("flying") + ); private static final VarHandle ALLOW_FLYING = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("allow_flying")); + MemoryLayout.PathElement.groupElement("allow_flying") + ); private static final VarHandle CREATIVE = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("creative")); - private static final VarHandle ALLOW_MODIFY_WORLD = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("allow_modify_world")); + MemoryLayout.PathElement.groupElement("creative") + ); + private static final VarHandle ALLOW_MODIFY_WORLD = + ABILITIES_LAYOUT.varHandle( + MemoryLayout.PathElement.groupElement("allow_modify_world") + ); private static final VarHandle FLY_SPEED = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("fly_speed")); + MemoryLayout.PathElement.groupElement("fly_speed") + ); private static final VarHandle WALK_SPEED = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("walk_speed")); - + MemoryLayout.PathElement.groupElement("walk_speed") + ); // Struct layout for Vec3 private static final StructLayout VEC3_LAYOUT = MemoryLayout.structLayout( @@ -55,61 +63,125 @@ public class NativePatchBukkit { // VarHandles for Vec3 fields private static final VarHandle VEC3_X = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("x")); + MemoryLayout.PathElement.groupElement("x") + ); private static final VarHandle VEC3_Y = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("y")); + MemoryLayout.PathElement.groupElement("y") + ); private static final VarHandle VEC3_Z = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("z")); + MemoryLayout.PathElement.groupElement("z") + ); public record Vec3(double x, double y, double z) {} - /** * Java record to hold player abilities. */ - public record Abilities( - boolean invulnerable, - boolean flying, - boolean allowFlying, - boolean creative, - boolean allowModifyWorld, - float flySpeed, - float walkSpeed - ) { - // "Wither" methods - return a copy with one field changed - public Abilities withInvulnerable(boolean value) { - return new Abilities(value, flying, allowFlying, creative, allowModifyWorld, flySpeed, walkSpeed); - } + public record Abilities( + boolean invulnerable, + boolean flying, + boolean allowFlying, + boolean creative, + boolean allowModifyWorld, + float flySpeed, + float walkSpeed + ) { + // "Wither" methods - return a copy with one field changed + public Abilities withInvulnerable(boolean value) { + return new Abilities( + value, + flying, + allowFlying, + creative, + allowModifyWorld, + flySpeed, + walkSpeed + ); + } - public Abilities withFlying(boolean value) { - return new Abilities(invulnerable, value, allowFlying, creative, allowModifyWorld, flySpeed, walkSpeed); - } + public Abilities withFlying(boolean value) { + return new Abilities( + invulnerable, + value, + allowFlying, + creative, + allowModifyWorld, + flySpeed, + walkSpeed + ); + } - public Abilities withAllowFlying(boolean value) { - return new Abilities(invulnerable, flying, value, creative, allowModifyWorld, flySpeed, walkSpeed); - } + public Abilities withAllowFlying(boolean value) { + return new Abilities( + invulnerable, + flying, + value, + creative, + allowModifyWorld, + flySpeed, + walkSpeed + ); + } - public Abilities withCreative(boolean value) { - return new Abilities(invulnerable, flying, allowFlying, value, allowModifyWorld, flySpeed, walkSpeed); - } + public Abilities withCreative(boolean value) { + return new Abilities( + invulnerable, + flying, + allowFlying, + value, + allowModifyWorld, + flySpeed, + walkSpeed + ); + } - public Abilities withAllowModifyWorld(boolean value) { - return new Abilities(invulnerable, flying, allowFlying, creative, value, flySpeed, walkSpeed); - } + public Abilities withAllowModifyWorld(boolean value) { + return new Abilities( + invulnerable, + flying, + allowFlying, + creative, + value, + flySpeed, + walkSpeed + ); + } - public Abilities withFlySpeed(float value) { - return new Abilities(invulnerable, flying, allowFlying, creative, allowModifyWorld, value, walkSpeed); - } + public Abilities withFlySpeed(float value) { + return new Abilities( + invulnerable, + flying, + allowFlying, + creative, + allowModifyWorld, + value, + walkSpeed + ); + } - public Abilities withWalkSpeed(float value) { - return new Abilities(invulnerable, flying, allowFlying, creative, allowModifyWorld, flySpeed, value); - } - } + public Abilities withWalkSpeed(float value) { + return new Abilities( + invulnerable, + flying, + allowFlying, + creative, + allowModifyWorld, + flySpeed, + value + ); + } + } /** * Called from Rust during initialization to register native function pointers. */ - public static void initCallbacks(long sendMessageAddr, long registerEventAddr, long getAbilitiesAddr, long setAbilitiesAddr, long getLocationAddr) { + 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), @@ -126,9 +198,9 @@ public class NativePatchBukkit { getAbilitiesNative = LINKER.downcallHandle( MemorySegment.ofAddress(getAbilitiesAddr), FunctionDescriptor.of( - ValueLayout.JAVA_BOOLEAN, // return type - ValueLayout.ADDRESS, // uuid string - ValueLayout.ADDRESS // out pointer to AbilitiesFFI + ValueLayout.JAVA_BOOLEAN, // return type + ValueLayout.ADDRESS, // uuid string + ValueLayout.ADDRESS // out pointer to AbilitiesFFI ) ); @@ -136,9 +208,9 @@ public class NativePatchBukkit { setAbilitiesNative = LINKER.downcallHandle( MemorySegment.ofAddress(setAbilitiesAddr), FunctionDescriptor.of( - ValueLayout.JAVA_BOOLEAN, // return type - ValueLayout.ADDRESS, // uuid string - ValueLayout.ADDRESS // pointer to AbilitiesFFI + ValueLayout.JAVA_BOOLEAN, // return type + ValueLayout.ADDRESS, // uuid string + ValueLayout.ADDRESS // pointer to AbilitiesFFI ) ); @@ -146,9 +218,9 @@ public class NativePatchBukkit { getLocationNative = LINKER.downcallHandle( MemorySegment.ofAddress(getLocationAddr), FunctionDescriptor.of( - ValueLayout.JAVA_BOOLEAN, // return type - ValueLayout.ADDRESS, // uuid string - ValueLayout.ADDRESS // out pointer to Vec3FFI + ValueLayout.JAVA_BOOLEAN, // return type + ValueLayout.ADDRESS, // uuid string + ValueLayout.ADDRESS // out pointer to Vec3FFI ) ); } @@ -177,7 +249,9 @@ public class NativePatchBukkit { // Simple approach - pass plugin name, handle lookup in Rust try (Arena arena = Arena.ofConfined()) { MemorySegment pluginName = arena.allocateFrom(plugin.getName()); - MemorySegment listenerClass = arena.allocateFrom(listener.getClass().getName()); + MemorySegment listenerClass = arena.allocateFrom( + listener.getClass().getName() + ); registerEventNative.invokeExact(listenerClass, pluginName); } catch (Throwable t) { throw new RuntimeException("Failed to register event", t); @@ -195,7 +269,10 @@ public class NativePatchBukkit { MemorySegment uuidStr = arena.allocateFrom(uuid.toString()); MemorySegment outStruct = arena.allocate(ABILITIES_LAYOUT); - boolean success = (boolean) getAbilitiesNative.invokeExact(uuidStr, outStruct); + boolean success = (boolean) getAbilitiesNative.invokeExact( + uuidStr, + outStruct + ); if (!success) { return null; @@ -225,11 +302,18 @@ public class NativePatchBukkit { FLYING.set(abilitiesStruct, 0L, abilities.flying()); ALLOW_FLYING.set(abilitiesStruct, 0L, abilities.allowFlying()); CREATIVE.set(abilitiesStruct, 0L, abilities.creative()); - ALLOW_MODIFY_WORLD.set(abilitiesStruct, 0L, abilities.allowModifyWorld()); + ALLOW_MODIFY_WORLD.set( + abilitiesStruct, + 0L, + abilities.allowModifyWorld() + ); FLY_SPEED.set(abilitiesStruct, 0L, abilities.flySpeed()); WALK_SPEED.set(abilitiesStruct, 0L, abilities.walkSpeed()); - return (boolean) setAbilitiesNative.invokeExact(uuidStr, abilitiesStruct); + return (boolean) setAbilitiesNative.invokeExact( + uuidStr, + abilitiesStruct + ); } catch (Throwable t) { throw new RuntimeException("Failed to call native setAbilities", t); } @@ -246,7 +330,10 @@ public class NativePatchBukkit { MemorySegment uuidStr = arena.allocateFrom(uuid.toString()); MemorySegment outStruct = arena.allocate(VEC3_LAYOUT); - boolean success = (boolean) getLocationNative.invokeExact(uuidStr, outStruct); + boolean success = (boolean) getLocationNative.invokeExact( + uuidStr, + outStruct + ); if (!success) { return null; diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3b92cdd..c513d38 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1875,15 +1875,15 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] diff --git a/rust/src/java/native_callbacks/location.rs b/rust/src/java/native_callbacks/location.rs index 32e1437..8526385 100644 --- a/rust/src/java/native_callbacks/location.rs +++ b/rust/src/java/native_callbacks/location.rs @@ -1,5 +1,7 @@ use std::ffi::c_char; +use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector}; + use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; #[repr(C)] @@ -13,10 +15,14 @@ pub extern "C" fn rust_get_location(uuid_ptr: *const c_char, out: *mut Vec3FFI) 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 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 position = - tokio::task::block_in_place(|| ctx.runtime.block_on(async { player.position() })); + tokio::task::block_in_place(|| ctx.runtime.block_on(async { entity.pos.load() })); unsafe { (*out).x = position.x; diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index 1131b07..84306d5 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -9,6 +9,7 @@ pub mod events; pub mod location; pub mod message; pub mod utils; +pub mod world; static CALLBACK_CONTEXT: OnceLock = OnceLock::new(); diff --git a/rust/src/java/native_callbacks/world.rs b/rust/src/java/native_callbacks/world.rs new file mode 100644 index 0000000..544c9f4 --- /dev/null +++ b/rust/src/java/native_callbacks/world.rs @@ -0,0 +1,27 @@ +use std::ffi::{CString, c_char}; + +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(), + }; + } + } + + std::ptr::null() +}