Use entity instead of player

This commit is contained in:
Bjorn Beishline 2026-01-31 12:50:51 -07:00
parent 26b9727166
commit 4a906ae3dc
5 changed files with 199 additions and 78 deletions

View File

@ -4,11 +4,11 @@ import java.lang.foreign.*;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.UUID; import java.util.UUID;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
public class NativePatchBukkit { public class NativePatchBukkit {
private static final Linker LINKER = Linker.nativeLinker(); private static final Linker LINKER = Linker.nativeLinker();
private static MethodHandle sendMessageNative; private static MethodHandle sendMessageNative;
@ -18,33 +18,41 @@ public class NativePatchBukkit {
private static MethodHandle getLocationNative; private static MethodHandle getLocationNative;
// Struct layout matching Rust's #[repr(C)] AbilitiesFFI // Struct layout matching Rust's #[repr(C)] AbilitiesFFI
private static final StructLayout ABILITIES_LAYOUT = MemoryLayout.structLayout( private static final StructLayout ABILITIES_LAYOUT =
ValueLayout.JAVA_BOOLEAN.withName("invulnerable"), MemoryLayout.structLayout(
ValueLayout.JAVA_BOOLEAN.withName("flying"), ValueLayout.JAVA_BOOLEAN.withName("invulnerable"),
ValueLayout.JAVA_BOOLEAN.withName("allow_flying"), ValueLayout.JAVA_BOOLEAN.withName("flying"),
ValueLayout.JAVA_BOOLEAN.withName("creative"), ValueLayout.JAVA_BOOLEAN.withName("allow_flying"),
ValueLayout.JAVA_BOOLEAN.withName("allow_modify_world"), ValueLayout.JAVA_BOOLEAN.withName("creative"),
MemoryLayout.paddingLayout(3), // Padding to align f32 to 4-byte boundary ValueLayout.JAVA_BOOLEAN.withName("allow_modify_world"),
ValueLayout.JAVA_FLOAT.withName("fly_speed"), MemoryLayout.paddingLayout(3), // Padding to align f32 to 4-byte boundary
ValueLayout.JAVA_FLOAT.withName("walk_speed") ValueLayout.JAVA_FLOAT.withName("fly_speed"),
); ValueLayout.JAVA_FLOAT.withName("walk_speed")
);
// VarHandles for accessing struct fields // VarHandles for accessing struct fields
private static final VarHandle INVULNERABLE = ABILITIES_LAYOUT.varHandle( private static final VarHandle INVULNERABLE = ABILITIES_LAYOUT.varHandle(
MemoryLayout.PathElement.groupElement("invulnerable")); MemoryLayout.PathElement.groupElement("invulnerable")
);
private static final VarHandle FLYING = ABILITIES_LAYOUT.varHandle( 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( 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( private static final VarHandle CREATIVE = ABILITIES_LAYOUT.varHandle(
MemoryLayout.PathElement.groupElement("creative")); MemoryLayout.PathElement.groupElement("creative")
private static final VarHandle ALLOW_MODIFY_WORLD = ABILITIES_LAYOUT.varHandle( );
MemoryLayout.PathElement.groupElement("allow_modify_world")); 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( 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( private static final VarHandle WALK_SPEED = ABILITIES_LAYOUT.varHandle(
MemoryLayout.PathElement.groupElement("walk_speed")); MemoryLayout.PathElement.groupElement("walk_speed")
);
// Struct layout for Vec3 // Struct layout for Vec3
private static final StructLayout VEC3_LAYOUT = MemoryLayout.structLayout( private static final StructLayout VEC3_LAYOUT = MemoryLayout.structLayout(
@ -55,61 +63,125 @@ public class NativePatchBukkit {
// VarHandles for Vec3 fields // VarHandles for Vec3 fields
private static final VarHandle VEC3_X = VEC3_LAYOUT.varHandle( 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( 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( 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) {} public record Vec3(double x, double y, double z) {}
/** /**
* Java record to hold player abilities. * Java record to hold player abilities.
*/ */
public record Abilities( public record Abilities(
boolean invulnerable, boolean invulnerable,
boolean flying, boolean flying,
boolean allowFlying, boolean allowFlying,
boolean creative, boolean creative,
boolean allowModifyWorld, boolean allowModifyWorld,
float flySpeed, float flySpeed,
float walkSpeed float walkSpeed
) { ) {
// "Wither" methods - return a copy with one field changed // "Wither" methods - return a copy with one field changed
public Abilities withInvulnerable(boolean value) { public Abilities withInvulnerable(boolean value) {
return new Abilities(value, flying, allowFlying, creative, allowModifyWorld, flySpeed, walkSpeed); return new Abilities(
} value,
flying,
allowFlying,
creative,
allowModifyWorld,
flySpeed,
walkSpeed
);
}
public Abilities withFlying(boolean value) { public Abilities withFlying(boolean value) {
return new Abilities(invulnerable, value, allowFlying, creative, allowModifyWorld, flySpeed, walkSpeed); return new Abilities(
} invulnerable,
value,
allowFlying,
creative,
allowModifyWorld,
flySpeed,
walkSpeed
);
}
public Abilities withAllowFlying(boolean value) { public Abilities withAllowFlying(boolean value) {
return new Abilities(invulnerable, flying, value, creative, allowModifyWorld, flySpeed, walkSpeed); return new Abilities(
} invulnerable,
flying,
value,
creative,
allowModifyWorld,
flySpeed,
walkSpeed
);
}
public Abilities withCreative(boolean value) { public Abilities withCreative(boolean value) {
return new Abilities(invulnerable, flying, allowFlying, value, allowModifyWorld, flySpeed, walkSpeed); return new Abilities(
} invulnerable,
flying,
allowFlying,
value,
allowModifyWorld,
flySpeed,
walkSpeed
);
}
public Abilities withAllowModifyWorld(boolean value) { public Abilities withAllowModifyWorld(boolean value) {
return new Abilities(invulnerable, flying, allowFlying, creative, value, flySpeed, walkSpeed); return new Abilities(
} invulnerable,
flying,
allowFlying,
creative,
value,
flySpeed,
walkSpeed
);
}
public Abilities withFlySpeed(float value) { public Abilities withFlySpeed(float value) {
return new Abilities(invulnerable, flying, allowFlying, creative, allowModifyWorld, value, walkSpeed); return new Abilities(
} invulnerable,
flying,
allowFlying,
creative,
allowModifyWorld,
value,
walkSpeed
);
}
public Abilities withWalkSpeed(float value) { public Abilities withWalkSpeed(float value) {
return new Abilities(invulnerable, flying, allowFlying, creative, allowModifyWorld, flySpeed, value); return new Abilities(
} invulnerable,
} flying,
allowFlying,
creative,
allowModifyWorld,
flySpeed,
value
);
}
}
/** /**
* Called from Rust during initialization to register native function pointers. * 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) // void rust_send_message(const char* uuid, const char* message)
sendMessageNative = LINKER.downcallHandle( sendMessageNative = LINKER.downcallHandle(
MemorySegment.ofAddress(sendMessageAddr), MemorySegment.ofAddress(sendMessageAddr),
@ -126,9 +198,9 @@ public class NativePatchBukkit {
getAbilitiesNative = LINKER.downcallHandle( getAbilitiesNative = LINKER.downcallHandle(
MemorySegment.ofAddress(getAbilitiesAddr), MemorySegment.ofAddress(getAbilitiesAddr),
FunctionDescriptor.of( FunctionDescriptor.of(
ValueLayout.JAVA_BOOLEAN, // return type ValueLayout.JAVA_BOOLEAN, // return type
ValueLayout.ADDRESS, // uuid string ValueLayout.ADDRESS, // uuid string
ValueLayout.ADDRESS // out pointer to AbilitiesFFI ValueLayout.ADDRESS // out pointer to AbilitiesFFI
) )
); );
@ -136,9 +208,9 @@ public class NativePatchBukkit {
setAbilitiesNative = LINKER.downcallHandle( setAbilitiesNative = LINKER.downcallHandle(
MemorySegment.ofAddress(setAbilitiesAddr), MemorySegment.ofAddress(setAbilitiesAddr),
FunctionDescriptor.of( FunctionDescriptor.of(
ValueLayout.JAVA_BOOLEAN, // return type ValueLayout.JAVA_BOOLEAN, // return type
ValueLayout.ADDRESS, // uuid string ValueLayout.ADDRESS, // uuid string
ValueLayout.ADDRESS // pointer to AbilitiesFFI ValueLayout.ADDRESS // pointer to AbilitiesFFI
) )
); );
@ -146,9 +218,9 @@ public class NativePatchBukkit {
getLocationNative = LINKER.downcallHandle( getLocationNative = LINKER.downcallHandle(
MemorySegment.ofAddress(getLocationAddr), MemorySegment.ofAddress(getLocationAddr),
FunctionDescriptor.of( FunctionDescriptor.of(
ValueLayout.JAVA_BOOLEAN, // return type ValueLayout.JAVA_BOOLEAN, // return type
ValueLayout.ADDRESS, // uuid string ValueLayout.ADDRESS, // uuid string
ValueLayout.ADDRESS // out pointer to Vec3FFI ValueLayout.ADDRESS // out pointer to Vec3FFI
) )
); );
} }
@ -177,7 +249,9 @@ public class NativePatchBukkit {
// Simple approach - pass plugin name, handle lookup in Rust // Simple approach - pass plugin name, handle lookup in Rust
try (Arena arena = Arena.ofConfined()) { try (Arena arena = Arena.ofConfined()) {
MemorySegment pluginName = arena.allocateFrom(plugin.getName()); MemorySegment pluginName = arena.allocateFrom(plugin.getName());
MemorySegment listenerClass = arena.allocateFrom(listener.getClass().getName()); MemorySegment listenerClass = arena.allocateFrom(
listener.getClass().getName()
);
registerEventNative.invokeExact(listenerClass, pluginName); registerEventNative.invokeExact(listenerClass, pluginName);
} catch (Throwable t) { } catch (Throwable t) {
throw new RuntimeException("Failed to register event", t); throw new RuntimeException("Failed to register event", t);
@ -195,7 +269,10 @@ public class NativePatchBukkit {
MemorySegment uuidStr = arena.allocateFrom(uuid.toString()); MemorySegment uuidStr = arena.allocateFrom(uuid.toString());
MemorySegment outStruct = arena.allocate(ABILITIES_LAYOUT); MemorySegment outStruct = arena.allocate(ABILITIES_LAYOUT);
boolean success = (boolean) getAbilitiesNative.invokeExact(uuidStr, outStruct); boolean success = (boolean) getAbilitiesNative.invokeExact(
uuidStr,
outStruct
);
if (!success) { if (!success) {
return null; return null;
@ -225,11 +302,18 @@ public class NativePatchBukkit {
FLYING.set(abilitiesStruct, 0L, abilities.flying()); FLYING.set(abilitiesStruct, 0L, abilities.flying());
ALLOW_FLYING.set(abilitiesStruct, 0L, abilities.allowFlying()); ALLOW_FLYING.set(abilitiesStruct, 0L, abilities.allowFlying());
CREATIVE.set(abilitiesStruct, 0L, abilities.creative()); 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()); FLY_SPEED.set(abilitiesStruct, 0L, abilities.flySpeed());
WALK_SPEED.set(abilitiesStruct, 0L, abilities.walkSpeed()); WALK_SPEED.set(abilitiesStruct, 0L, abilities.walkSpeed());
return (boolean) setAbilitiesNative.invokeExact(uuidStr, abilitiesStruct); return (boolean) setAbilitiesNative.invokeExact(
uuidStr,
abilitiesStruct
);
} catch (Throwable t) { } catch (Throwable t) {
throw new RuntimeException("Failed to call native setAbilities", t); throw new RuntimeException("Failed to call native setAbilities", t);
} }
@ -246,7 +330,10 @@ public class NativePatchBukkit {
MemorySegment uuidStr = arena.allocateFrom(uuid.toString()); MemorySegment uuidStr = arena.allocateFrom(uuid.toString());
MemorySegment outStruct = arena.allocate(VEC3_LAYOUT); MemorySegment outStruct = arena.allocate(VEC3_LAYOUT);
boolean success = (boolean) getLocationNative.invokeExact(uuidStr, outStruct); boolean success = (boolean) getLocationNative.invokeExact(
uuidStr,
outStruct
);
if (!success) { if (!success) {
return null; return null;

8
rust/Cargo.lock generated
View File

@ -1875,15 +1875,15 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.13.0" version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]] [[package]]
name = "portable-atomic-util" name = "portable-atomic-util"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"
dependencies = [ dependencies = [
"portable-atomic", "portable-atomic",
] ]

View File

@ -1,5 +1,7 @@
use std::ffi::c_char; use std::ffi::c_char;
use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector};
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
#[repr(C)] #[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); let uuid_str = get_string(uuid_ptr);
if let Some(ctx) = CALLBACK_CONTEXT.get() { if let Some(ctx) = CALLBACK_CONTEXT.get() {
let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap(); let uuid = uuid::Uuid::parse_str(&uuid_str).unwrap();
let player = ctx.plugin_context.server.get_player_by_uuid(uuid); let entity = ctx
if let Some(player) = player { .plugin_context
.server
.select_entities(&TargetSelector::new(EntitySelectorType::Uuid(uuid)), None);
if entity.len() == 1 {
let entity = entity.first().unwrap().get_entity();
let position = 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 { unsafe {
(*out).x = position.x; (*out).x = position.x;

View File

@ -9,6 +9,7 @@ pub mod events;
pub mod location; pub mod location;
pub mod message; pub mod message;
pub mod utils; pub mod utils;
pub mod world;
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new(); static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();

View File

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