Refactor call event

This commit is contained in:
Bjorn Beishline 2026-02-03 16:54:07 -07:00
parent dfbdab7e95
commit 605858e14f
15 changed files with 106 additions and 123 deletions

View File

@ -14,7 +14,6 @@ public class NativePatchBukkit {
private static final Linker LINKER = Linker.nativeLinker();
private static MethodHandle callEventNative;
private static MethodHandle getLocationNative;
private static MethodHandle getWorldNative;
private static MethodHandle freeStringNative;
@ -181,7 +180,6 @@ public class NativePatchBukkit {
* Called from Rust during initialization to register native function pointers.
*/
public static void initCallbacks(
long callEventAddr,
long getLocationAddr,
long freeStringAddr,
long getWorldAddr,
@ -189,18 +187,6 @@ public class NativePatchBukkit {
long playerEntityPlaySoundAddr,
long playerPlaySoundAddr
) {
// 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(
MemorySegment.ofAddress(callEventAddr),
FunctionDescriptor.of(
ValueLayout.JAVA_BOOLEAN, // return: was handled by Pumpkin
ValueLayout.ADDRESS, // event_type string
ValueLayout.ADDRESS // event_data_json string
)
);
// bool rust_get_location(const char* uuid, Vec3FFI* out)
getLocationNative = LINKER.downcallHandle(
MemorySegment.ofAddress(getLocationAddr),
@ -426,24 +412,4 @@ public class NativePatchBukkit {
throw new RuntimeException("Failed to call native playerPlaySound", t);
}
}
public static boolean callEvent(Event event) {
try (Arena arena = Arena.ofConfined()) {
// Serialize event data to JSON for Rust
String eventDataJson = PatchBukkitEventSerializer.serialize(event);
MemorySegment eventTypeStr = arena.allocateFrom(event.getEventName());
MemorySegment eventDataStr = arena.allocateFrom(eventDataJson);
boolean handled = (boolean) callEventNative.invokeExact(eventTypeStr, eventDataStr);
// If Pumpkin handled it and event is Cancellable, we need to read back
// the cancelled state. This is handled by Rust calling back to update
// the event object directly.
return handled;
} catch (Throwable t) {
throw new RuntimeException("Failed to call event: " + event.getEventName(), t);
}
}
}

View File

@ -80,8 +80,8 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.TriState;
import net.md_5.bungee.api.chat.BaseComponent;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.common.SendMessageRequest;
import patchbukkit.common.SetAbilitiesRequest;
import patchbukkit.message.SendMessageRequest;
import patchbukkit.abilities.SetAbilitiesRequest;
@SuppressWarnings({ "deprecation", "removal" })
public class PatchBukkitPlayer

View File

@ -12,6 +12,7 @@ import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.IllegalPluginAccessException;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener;
import org.patchbukkit.bridge.BridgeUtils;
import org.patchbukkit.bridge.NativePatchBukkit;
import com.google.common.collect.Sets;
@ -20,7 +21,9 @@ import co.aikar.timings.TimedEventExecutor;
import org.jetbrains.annotations.NotNull;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.common.RegisterEventRequest;
import patchbukkit.events.CallEventRequest;
import patchbukkit.events.PlayerJoinEvent;
import patchbukkit.events.RegisterEventRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -45,7 +48,22 @@ public class PatchBukkitEventManager {
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
}
boolean handledByPumpkin = NativePatchBukkit.callEvent(event);
var request = CallEventRequest.newBuilder();
switch (event.getEventName()) {
case "org.bukkit.event.player.PlayerJoinEvent":
var castedEvent = (org.bukkit.event.player.PlayerJoinEvent) event;
request.setPlayerJoin(
PlayerJoinEvent.newBuilder()
.setJoinMessage(castedEvent.joinMessage().toString())
.setPlayerUuid(BridgeUtils.convertUuid(castedEvent.getPlayer().getUniqueId()))
);
break;
}
var response = NativeBridgeFfi.callEvent(request.build());
boolean handledByPumpkin;
if (response == null) handledByPumpkin = false;
else handledByPumpkin = response.getHandled();
if (!handledByPumpkin) {
// Pumpkin doesn't know this event type, dispatch Java-only

View File

@ -1,15 +1,19 @@
syntax = "proto3";
package patchbukkit.common;
package patchbukkit.abilities;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.common";
option java_package = "patchbukkit.abilities";
message SetAbilitiesRequest {
patchbukkit.common.UUID uuid = 1;
patchbukkit.common.Abilities abilities = 2;
Abilities abilities = 2;
}
message SetAbilitiesResponse {
bool success = 1;
}
message Abilities {

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package patchbukkit.bridge;
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
import "patchbukkit/abilities.proto";
import "patchbukkit/common/types.proto";
import "patchbukkit/events/event.proto";
import "patchbukkit/message.proto";
service NativeBridge {
rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.abilities.Abilities);
rpc SetAbilities(patchbukkit.abilities.SetAbilitiesRequest) returns (patchbukkit.abilities.SetAbilitiesResponse);
rpc RegisterEvent(patchbukkit.events.RegisterEventRequest) returns (google.protobuf.Empty);
rpc CallEvent(patchbukkit.events.CallEventRequest) returns (patchbukkit.events.CallEventResponse);
rpc SendMessage(patchbukkit.message.SendMessageRequest) returns (google.protobuf.Empty);
}

View File

@ -1,16 +0,0 @@
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/message.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);
rpc SendMessage(patchbukkit.common.SendMessageRequest) returns (google.protobuf.Empty);
}

View File

@ -1,15 +0,0 @@
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

@ -2,16 +2,25 @@ syntax = "proto3";
package patchbukkit.events;
import "patchbukkit/common/types.proto";
import "patchbukkit/events/player_events.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.events";
message Event {
string event_id = 1;
bool cancellable = 2;
message RegisterEventRequest {
string event_type = 1;
string plugin_name = 2;
int32 priority = 3;
bool blocking = 4;
}
message CallEventRequest {
oneof event_data {
PlayerJoinEvent player_join = 10;
PlayerJoinEvent player_join = 1;
}
}
message CallEventResponse {
bool handled = 1;
}

View File

@ -7,6 +7,6 @@ option java_multiple_files = true;
option java_package = "patchbukkit.events";
message PlayerJoinEvent {
patchbukkit.common.UUID player = 1;
patchbukkit.common.UUID player_uuid = 1;
string join_message = 2;
}

View File

@ -1,11 +1,11 @@
syntax = "proto3";
package patchbukkit.common;
package patchbukkit.message;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.common";
option java_package = "patchbukkit.message";
message SendMessageRequest {
patchbukkit.common.UUID uuid = 1;

View File

@ -1,5 +1,8 @@
use crate::java::native_callbacks::CALLBACK_CONTEXT;
use crate::proto::patchbukkit::common::{Abilities, SetAbilitiesRequest, Uuid};
use crate::proto::patchbukkit::{
abilities::{Abilities, SetAbilitiesRequest},
common::Uuid,
};
pub fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
let ctx = CALLBACK_CONTEXT.get()?;

View File

@ -1,4 +1,3 @@
use std::ffi::c_char;
use std::sync::Arc;
use pumpkin::plugin::EventPriority;
@ -6,8 +5,11 @@ use pumpkin::plugin::player::player_join::PlayerJoinEvent;
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;
use crate::java::native_callbacks::CALLBACK_CONTEXT;
use crate::proto::patchbukkit::events::call_event_request::EventData;
use crate::proto::patchbukkit::events::{
CallEventRequest, CallEventResponse, RegisterEventRequest,
};
pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
@ -61,44 +63,32 @@ pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> O
Some(())
}
pub extern "C" fn rust_call_event(
event_type_ptr: *const c_char,
event_data_ptr: *const c_char,
) -> bool {
let event_type = get_string(event_type_ptr);
let event_data_json = get_string(event_data_ptr);
let Some(ctx) = CALLBACK_CONTEXT.get() else {
log::error!("CallbackContext not initialized when calling event");
return false;
};
log::debug!("Java calling event '{event_type}' with data: {event_data_json}");
let event_data: serde_json::Value = match serde_json::from_str(&event_data_json) {
Ok(v) => v,
Err(e) => {
log::error!("Failed to parse event data JSON: {e}");
return false;
}
};
pub fn ffi_native_bridge_call_event_impl(request: CallEventRequest) -> Option<CallEventResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let event_data = request.event_data?;
log::debug!("Java calling event {:?}", event_data);
let context = ctx.plugin_context.clone();
tokio::task::block_in_place(|| {
ctx.runtime.block_on(async {
if event_type.as_str() == "org.bukkit.event.player.PlayerJoinEvent" {
let player_uuid_str = event_data["playerUuid"].as_str().unwrap_or("");
let join_message_str = event_data["joinMessage"].as_str().unwrap_or("");
if let Ok(uuid) = uuid::Uuid::parse_str(player_uuid_str)
&& let Some(player) = context.server.get_player_by_uuid(uuid)
{
let pumpkin_event = PlayerJoinEvent::new(
player,
TextComponent::from_legacy_string(join_message_str),
);
context.server.plugin_manager.fire(pumpkin_event).await;
return true;
match event_data {
EventData::PlayerJoin(player_join_event_data) => {
if let Ok(uuid) =
uuid::Uuid::parse_str(&player_join_event_data.player_uuid?.value)
{
if let Some(player) = context.server.get_player_by_uuid(uuid) {
let pumpkin_event = PlayerJoinEvent::new(
player,
TextComponent::from_legacy_string(
&player_join_event_data.join_message,
),
);
context.server.plugin_manager.fire(pumpkin_event).await;
return Some(true);
}
}
Some(false)
}
false
} else {
@ -106,5 +96,9 @@ pub extern "C" fn rust_call_event(
false
}
})
})
});
let mut response = CallEventResponse::default();
response.handled = handled?;
Some(response)
}

View File

@ -1,10 +1,7 @@
use std::ffi::c_char;
use pumpkin_util::text::TextComponent;
use crate::{
java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string},
proto::patchbukkit::common::SendMessageRequest,
java::native_callbacks::CALLBACK_CONTEXT, proto::patchbukkit::message::SendMessageRequest,
};
pub fn ffi_native_bridge_send_message_impl(request: SendMessageRequest) -> Option<()> {

View File

@ -79,7 +79,6 @@ pub extern "C" fn rust_free_bytes(ptr: *mut u8, len: u32) {
}
pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
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;
let get_world_addr = world::rust_get_world as *const () as i64;
@ -92,7 +91,6 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
"org.patchbukkit.bridge.NativePatchBukkit",
"initCallbacks",
&[
InvocationArg::try_from(call_event_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

@ -10,6 +10,14 @@ pub mod patchbukkit {
pub mod events {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.events.rs"));
}
pub mod abilities {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.abilities.rs"));
}
pub mod message {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.message.rs"));
}
}
include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));