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 MethodHandle sendMessageNative;
private static MethodHandle registerEventNative;
private static MethodHandle callEventNative;
private static MethodHandle getLocationNative;
private static MethodHandle getWorldNative;
@ -184,7 +183,6 @@ public class NativePatchBukkit {
*/
public static void initCallbacks(
long sendMessageAddr,
long registerEventAddr,
long callEventAddr,
long getLocationAddr,
long freeStringAddr,
@ -199,17 +197,6 @@ public class NativePatchBukkit {
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)
// Returns true if Pumpkin handled it, false if unknown event type
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.
*

View File

@ -19,6 +19,8 @@ import com.google.common.collect.Sets;
import co.aikar.timings.TimedEventExecutor;
import org.jetbrains.annotations.NotNull;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.common.RegisterEventRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -128,12 +130,8 @@ public class PatchBukkitEventManager {
for (RegisteredListener rl : entry.getValue()) {
int priorityOrdinal = Math.min(rl.getPriority().ordinal(), 4);
NativePatchBukkit.registerEvent(
entry.getKey().getName(),
plugin.getName(),
priorityOrdinal,
true
);
var request = RegisterEventRequest.newBuilder().setEventType(entry.getKey().getName()).setPluginName(plugin.getName()).setPriority(priorityOrdinal).setBlocking(true).build();
NativeBridgeFfi.registerEvent(request);
}
}
}
@ -151,7 +149,8 @@ public class PatchBukkitEventManager {
this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
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";
package patchbukkit.bridge;
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
import "patchbukkit/common/abilities.proto";
import "patchbukkit/common/events.proto";
import "patchbukkit/common/types.proto";
service NativeBridge {
rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.common.Abilities);
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.compile_protos(&paths, &[proto_path]).unwrap();
// Generate the shared initialization file
let out_dir = std::env::var("OUT_DIR").unwrap();
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::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
use crate::proto::patchbukkit::common::RegisterEventRequest;
pub extern "C" fn rust_register_event(
event_type_ptr: *const c_char,
plugin_name_ptr: *const c_char,
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 {
pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let pumpkin_priority = match request.priority {
0 => EventPriority::Lowest,
1 => EventPriority::Low,
2 => EventPriority::Normal,
@ -31,16 +20,19 @@ pub extern "C" fn rust_register_event(
};
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 context = ctx.plugin_context.clone();
let event_type_owned = event_type;
tokio::task::block_in_place(|| {
ctx.runtime.block_on(async {
match event_type_owned.as_str() {
match request.event_type.as_str() {
"org.bukkit.event.player.PlayerJoinEvent" => {
context
.register_event::<
@ -48,22 +40,25 @@ pub extern "C" fn rust_register_event(
PatchBukkitEventHandler<pumpkin::plugin::player::player_join::PlayerJoinEvent>,
>(
Arc::new(PatchBukkitEventHandler::new(
plugin_name.clone(),
request.plugin_name.clone(),
command_tx.clone(),
)),
pumpkin_priority,
blocking,
request.blocking,
)
.await;
}
_ => {
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(

View File

@ -11,6 +11,8 @@ mod abilities;
pub use abilities::*;
pub mod events;
pub use events::*;
pub mod location;
pub mod memory;
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<()> {
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 get_location_addr = location::rust_get_location 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",
&[
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(get_location_addr)?.into_primitive()?,
InvocationArg::try_from(free_string_addr)?.into_primitive()?,