Re-implement register event ffm

This commit is contained in:
Bjorn Beishline 2026-02-03 15:55:27 -07:00
parent 5e6de69913
commit d382421557
8 changed files with 42 additions and 66 deletions

View File

@ -15,7 +15,6 @@ 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;
private static MethodHandle registerEventNative;
private static MethodHandle callEventNative; private static MethodHandle callEventNative;
private static MethodHandle getLocationNative; private static MethodHandle getLocationNative;
private static MethodHandle getWorldNative; private static MethodHandle getWorldNative;
@ -184,7 +183,6 @@ public class NativePatchBukkit {
*/ */
public static void initCallbacks( public static void initCallbacks(
long sendMessageAddr, long sendMessageAddr,
long registerEventAddr,
long callEventAddr, long callEventAddr,
long getLocationAddr, long getLocationAddr,
long freeStringAddr, long freeStringAddr,
@ -199,17 +197,6 @@ public class NativePatchBukkit {
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS) FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)
); );
// void rust_register_event(const char* event_type, const char* plugin_name, int32_t priority, bool blocking)
registerEventNative = LINKER.downcallHandle(
MemorySegment.ofAddress(registerEventAddr),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, // event_type string
ValueLayout.ADDRESS, // plugin_name string
ValueLayout.JAVA_INT, // priority ordinal
ValueLayout.JAVA_BOOLEAN // blocking
)
);
// bool rust_call_event(const char* event_type, const char* event_data_json) // bool rust_call_event(const char* event_type, const char* event_data_json)
// Returns true if Pumpkin handled it, false if unknown event type // Returns true if Pumpkin handled it, false if unknown event type
callEventNative = LINKER.downcallHandle( callEventNative = LINKER.downcallHandle(
@ -294,28 +281,6 @@ public class NativePatchBukkit {
} }
} }
/**
* Register an event listener with Pumpkin's event system.
*
* Called by PatchBukkitEventManager when a plugin registers for an event.
* Rust will create a PatchBukkitEventHandler that listens for the corresponding
* Pumpkin event and dispatches back to Java when it fires.
*
* @param eventType Fully qualified Bukkit event class name (e.g. "org.bukkit.event.player.PlayerJoinEvent")
* @param pluginName Name of the plugin registering this listener
* @param priority Bukkit EventPriority ordinal (0=LOWEST through 5=MONITOR), clamped to 0-4 for Pumpkin
* @param blocking Whether the handler should block
*/
public static void registerEvent(String eventType, String pluginName, int priority, boolean blocking) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment eventTypeStr = arena.allocateFrom(eventType);
MemorySegment pluginNameStr = arena.allocateFrom(pluginName);
registerEventNative.invokeExact(eventTypeStr, pluginNameStr, priority, blocking);
} catch (Throwable t) {
throw new RuntimeException("Failed to register event: " + eventType + " for plugin " + pluginName, t);
}
}
/** /**
* Get a player's location by UUID. * Get a player's location by UUID.
* *

View File

@ -19,6 +19,8 @@ import com.google.common.collect.Sets;
import co.aikar.timings.TimedEventExecutor; import co.aikar.timings.TimedEventExecutor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.common.RegisterEventRequest;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
@ -128,12 +130,8 @@ public class PatchBukkitEventManager {
for (RegisteredListener rl : entry.getValue()) { for (RegisteredListener rl : entry.getValue()) {
int priorityOrdinal = Math.min(rl.getPriority().ordinal(), 4); int priorityOrdinal = Math.min(rl.getPriority().ordinal(), 4);
NativePatchBukkit.registerEvent( var request = RegisterEventRequest.newBuilder().setEventType(entry.getKey().getName()).setPluginName(plugin.getName()).setPriority(priorityOrdinal).setBlocking(true).build();
entry.getKey().getName(), NativeBridgeFfi.registerEvent(request);
plugin.getName(),
priorityOrdinal,
true
);
} }
} }
} }
@ -151,7 +149,8 @@ public class PatchBukkitEventManager {
this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
int priorityOrdinal = Math.min(priority.ordinal(), 4); int priorityOrdinal = Math.min(priority.ordinal(), 4);
NativePatchBukkit.registerEvent(event.getName(), plugin.getName(), priorityOrdinal, true); // We default to having all events blocking. var request = RegisterEventRequest.newBuilder().setEventType(event.getName()).setPluginName(plugin.getName()).setPriority(priorityOrdinal).setBlocking(true).build();
NativeBridgeFfi.registerEvent(request);
} }

View File

@ -1,11 +1,14 @@
syntax = "proto3"; syntax = "proto3";
package patchbukkit.bridge; package patchbukkit.bridge;
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto"; import "google/protobuf/wrappers.proto";
import "patchbukkit/common/abilities.proto"; import "patchbukkit/common/abilities.proto";
import "patchbukkit/common/events.proto";
import "patchbukkit/common/types.proto"; import "patchbukkit/common/types.proto";
service NativeBridge { service NativeBridge {
rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.common.Abilities); rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.common.Abilities);
rpc SetAbilities(patchbukkit.common.SetAbilitiesRequest) returns (google.protobuf.BoolValue); rpc SetAbilities(patchbukkit.common.SetAbilitiesRequest) returns (google.protobuf.BoolValue);
rpc RegisterEvent(patchbukkit.common.RegisterEventRequest) returns (google.protobuf.Empty);
} }

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package patchbukkit.common;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.common";
message RegisterEventRequest {
string event_type = 1;
string plugin_name = 2;
int32 priority = 3;
bool blocking = 4;
}

View File

@ -162,7 +162,6 @@ pub fn setup_protobufs(base: PathBuf) {
config.service_generator(Box::new(generator)); config.service_generator(Box::new(generator));
config.compile_protos(&paths, &[proto_path]).unwrap(); config.compile_protos(&paths, &[proto_path]).unwrap();
// Generate the shared initialization file
let out_dir = std::env::var("OUT_DIR").unwrap(); let out_dir = std::env::var("OUT_DIR").unwrap();
let init_path = PathBuf::from(&out_dir).join("ffi_init.rs"); let init_path = PathBuf::from(&out_dir).join("ffi_init.rs");

View File

@ -7,22 +7,11 @@ use pumpkin_util::text::TextComponent;
use crate::events::handler::PatchBukkitEventHandler; use crate::events::handler::PatchBukkitEventHandler;
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string}; use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
use crate::proto::patchbukkit::common::RegisterEventRequest;
pub extern "C" fn rust_register_event( pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> Option<()> {
event_type_ptr: *const c_char, let ctx = CALLBACK_CONTEXT.get()?;
plugin_name_ptr: *const c_char, let pumpkin_priority = match request.priority {
priority: i32,
blocking: bool,
) {
let event_type = get_string(event_type_ptr);
let plugin_name = get_string(plugin_name_ptr);
let Some(ctx) = CALLBACK_CONTEXT.get() else {
log::error!("CallbackContext not initialized when registering event");
return;
};
let pumpkin_priority = match priority {
0 => EventPriority::Lowest, 0 => EventPriority::Lowest,
1 => EventPriority::Low, 1 => EventPriority::Low,
2 => EventPriority::Normal, 2 => EventPriority::Normal,
@ -31,16 +20,19 @@ pub extern "C" fn rust_register_event(
}; };
log::info!( log::info!(
"Plugin '{plugin_name}' registering listener for '{event_type}' (priority={priority:?}, blocking={blocking})" "Plugin '{}' registering listener for '{}' (priority={:?}, blocking={})",
request.plugin_name,
request.event_type,
request.priority,
request.blocking
); );
let command_tx = ctx.command_tx.clone(); let command_tx = ctx.command_tx.clone();
let context = ctx.plugin_context.clone(); let context = ctx.plugin_context.clone();
let event_type_owned = event_type;
tokio::task::block_in_place(|| { tokio::task::block_in_place(|| {
ctx.runtime.block_on(async { ctx.runtime.block_on(async {
match event_type_owned.as_str() { match request.event_type.as_str() {
"org.bukkit.event.player.PlayerJoinEvent" => { "org.bukkit.event.player.PlayerJoinEvent" => {
context context
.register_event::< .register_event::<
@ -48,22 +40,25 @@ pub extern "C" fn rust_register_event(
PatchBukkitEventHandler<pumpkin::plugin::player::player_join::PlayerJoinEvent>, PatchBukkitEventHandler<pumpkin::plugin::player::player_join::PlayerJoinEvent>,
>( >(
Arc::new(PatchBukkitEventHandler::new( Arc::new(PatchBukkitEventHandler::new(
plugin_name.clone(), request.plugin_name.clone(),
command_tx.clone(), command_tx.clone(),
)), )),
pumpkin_priority, pumpkin_priority,
blocking, request.blocking,
) )
.await; .await;
} }
_ => { _ => {
log::warn!( log::warn!(
"Unsupported Bukkit event type '{event_type_owned}' from plugin '{plugin_name}'" "Unsupported Bukkit event type '{}' from plugin '{}'",
request.event_type, request.plugin_name
); );
} }
} }
}); });
}); });
Some(())
} }
pub extern "C" fn rust_call_event( pub extern "C" fn rust_call_event(

View File

@ -11,6 +11,8 @@ mod abilities;
pub use abilities::*; pub use abilities::*;
pub mod events; pub mod events;
pub use events::*;
pub mod location; pub mod location;
pub mod memory; pub mod memory;
pub mod message; pub mod message;
@ -66,7 +68,6 @@ pub extern "C" fn rust_free_bytes(ptr: *mut u8, len: u32) {
pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
let send_message_addr = message::rust_send_message as *const () as i64; let send_message_addr = message::rust_send_message as *const () as i64;
let register_event_addr = events::rust_register_event as *const () as i64;
let call_event_addr = events::rust_call_event as *const () as i64; let call_event_addr = events::rust_call_event as *const () as i64;
let get_location_addr = location::rust_get_location 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 free_string_addr = memory::rust_free_string as *const () as i64;
@ -81,7 +82,6 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
"initCallbacks", "initCallbacks",
&[ &[
InvocationArg::try_from(send_message_addr)?.into_primitive()?, InvocationArg::try_from(send_message_addr)?.into_primitive()?,
InvocationArg::try_from(register_event_addr)?.into_primitive()?,
InvocationArg::try_from(call_event_addr)?.into_primitive()?, InvocationArg::try_from(call_event_addr)?.into_primitive()?,
InvocationArg::try_from(get_location_addr)?.into_primitive()?, InvocationArg::try_from(get_location_addr)?.into_primitive()?,
InvocationArg::try_from(free_string_addr)?.into_primitive()?, InvocationArg::try_from(free_string_addr)?.into_primitive()?,