diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/NativeCallbacks.java b/java/patchbukkit/src/main/java/org/patchbukkit/NativeCallbacks.java index aeb9887..5ece48e 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/NativeCallbacks.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/NativeCallbacks.java @@ -1,6 +1,8 @@ // No longer used currently, keeping around for reference in the short term package org.patchbukkit; +import java.util.UUID; + import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; @@ -37,4 +39,13 @@ public class NativeCallbacks extends NativeCallbackToRustChannelSupport { ) ); } + + public void sendMessage(@NotNull UUID uuid, String message) { + NativeCallbacks.getInstance().doCallback( + new CallbackValue( + "SEND_MESSAGE", + new Object[] { uuid.toString(), message } + ) + ); + } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java index 24a5582..64b1e52 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -103,27 +103,23 @@ public class PatchBukkitEntity implements Entity { } @Override - public void sendMessage(@NotNull String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMessage'"); + public void sendMessage(String message) { + } @Override - public void sendMessage(@NotNull String... messages) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMessage'"); + public void sendMessage(String... messages) { + } @Override - public void sendMessage(@Nullable UUID sender, @NotNull String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMessage'"); + public void sendMessage(UUID sender, String message) { + this.sendMessage(message); // Most entities don't know about senders } @Override - public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMessage'"); + public void sendMessage(UUID sender, String... messages) { + this.sendMessage(messages); // Most entities don't know about senders } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java index 06c1262..46441db 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java @@ -60,11 +60,13 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Range; import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; +import org.patchbukkit.NativeCallbacks; import org.patchbukkit.PatchBukkitServer; import com.destroystokyo.paper.ClientOption; import com.destroystokyo.paper.Title; import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.base.Preconditions; import io.papermc.paper.connection.PlayerGameConnection; import io.papermc.paper.entity.LookAnchor; @@ -84,6 +86,44 @@ public class PatchBukkitPlayer super(uuid, name); } + @Override + public void sendRawMessage(String message) { + this.sendRawMessage(null, message); + } + + @Override + public void sendRawMessage(UUID sender, String message) { + if (sender == null) { + sender = this.getUniqueId(); + } + + NativeCallbacks.getInstance().sendMessage(sender, message); + } + + @Override + public void sendMessage(String message) { + this.sendRawMessage(message); + } + + @Override + public void sendMessage(String... messages) { + for (String message : messages) { + this.sendMessage(message); + } + } + + @Override + public void sendMessage(UUID sender, String message) { + this.sendRawMessage(sender, message); + } + + @Override + public void sendMessage(UUID sender, String... messages) { + for (String message : messages) { + this.sendMessage(sender, message); + } + } + @Override public Player.Spigot spigot() { throw new UnsupportedOperationException("Unimplemented method 'spigot'"); @@ -119,12 +159,6 @@ public class PatchBukkitPlayer throw new UnsupportedOperationException("Unimplemented method 'abandonConversation'"); } - @Override - public void sendRawMessage(@Nullable UUID sender, @NotNull String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendRawMessage'"); - } - @Override public boolean isOnline() { // TODO Auto-generated method stub @@ -517,12 +551,6 @@ public class PatchBukkitPlayer throw new UnsupportedOperationException("Unimplemented method 'transfer'"); } - @Override - public void sendRawMessage(String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendRawMessage'"); - } - @Override public void kickPlayer(@org.jspecify.annotations.Nullable String message) { // TODO Auto-generated method stub diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3fe964b..449c11c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1701,6 +1701,7 @@ dependencies = [ "serde", "serde-saphyr", "tokio", + "uuid", "walkdir", "zip", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b2788bd..7b9f2a0 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -25,6 +25,7 @@ serde-saphyr = "0.0.16" serde = { version = "1", features = ["derive"] } anyhow = "1.0" +uuid = "1.20.0" [build-dependencies] j4rs = { version = "0.24", artifact = "cdylib", lib = true } diff --git a/rust/src/java/jvm/commands.rs b/rust/src/java/jvm/commands.rs index 441190b..6041bda 100644 --- a/rust/src/java/jvm/commands.rs +++ b/rust/src/java/jvm/commands.rs @@ -23,6 +23,7 @@ pub enum JvmCommand { Initialize { j4rs_path: PathBuf, respond_to: oneshot::Sender>, + context: Arc, }, JavaCallback { instance: Instance, diff --git a/rust/src/java/jvm/worker.rs b/rust/src/java/jvm/worker.rs index 76e6186..7d03df0 100644 --- a/rust/src/java/jvm/worker.rs +++ b/rust/src/java/jvm/worker.rs @@ -1,8 +1,9 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, str::FromStr, sync::Arc}; use anyhow::bail; use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder}; -use pumpkin::{net::bedrock::play, server::Server}; +use pumpkin::{net::bedrock::play, plugin::Context, server::Server}; +use pumpkin_util::text::TextComponent; use tokio::sync::{mpsc, oneshot}; use crate::{ @@ -20,6 +21,7 @@ pub struct JvmWorker { pub plugin_manager: PluginManager, pub event_manager: EventManager, jvm: Option, + context: Option>, } impl JvmWorker { @@ -33,18 +35,21 @@ impl JvmWorker { plugin_manager: PluginManager::new(), event_manager: EventManager::new(), jvm: None, + context: None, } } - pub fn attach_thread(mut self) { + pub async fn attach_thread(mut self) { log::info!("JVM worker thread started"); - while let Some(command) = self.command_rx.blocking_recv() { + while let Some(command) = self.command_rx.recv().await { match command { JvmCommand::Initialize { j4rs_path, respond_to, + context, } => { + self.context = Some(context); let result = self.initialize_jvm(&j4rs_path); let _ = respond_to.send(result); } @@ -52,7 +57,7 @@ impl JvmWorker { instance, respond_to, } => { - let result = self.process_java_callback(instance); + let result = self.process_java_callback(instance).await; let _ = respond_to.send(result); } JvmCommand::LoadPlugin { @@ -223,7 +228,7 @@ impl JvmWorker { log::info!("JVM worker thread exited"); } - fn process_java_callback(&mut self, instance: Instance) -> anyhow::Result<()> { + async fn process_java_callback(&mut self, instance: Instance) -> anyhow::Result<()> { let jvm = match self.jvm { Some(ref jvm) => jvm, None => bail!("JVM not initialized"), @@ -256,6 +261,29 @@ impl JvmWorker { None => todo!(), }; } + "SEND_MESSAGE" => { + let uuid: String = jvm.to_rust(jvm.cast( + &jvm.invoke(&instance, "getArg", &[&InvocationArg::try_from(0_i32)?])?, + "java.lang.String", + )?)?; + + let message: String = jvm.to_rust(jvm.cast( + &jvm.invoke(&instance, "getArg", &[&InvocationArg::try_from(1_i32)?])?, + "java.lang.String", + )?)?; + + if let Some(player) = self + .context + .as_ref() + .unwrap() + .server + .get_player_by_uuid(uuid::Uuid::from_str(&uuid).unwrap()) + { + player + .send_system_message(&TextComponent::text(message)) + .await; + } + } _ => log::warn!("Received unknown callback: {:?}", callback_name), } @@ -302,8 +330,10 @@ impl JvmWorker { break; } - if let Err(e) = receiver.await { - log::error!("Callback failed: {}", e); + match receiver.await { + Ok(Ok(_)) => (), + Ok(Err(e)) => log::error!("{}", e), + Err(e) => log::error!("Callback failed: {}", e), } } Err(e) => { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 055e962..fb8576e 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -14,7 +14,10 @@ use java::{ jar::discover_jar_files, resources::{cleanup_stale_files, sync_embedded_resources}, }; -use tokio::sync::{mpsc, oneshot}; +use tokio::sync::{ + mpsc::{self, Receiver, Sender}, + oneshot, +}; use crate::{ events::register_handlers, @@ -108,6 +111,7 @@ async fn on_load_inner(plugin: &mut PatchBukkitPlugin, server: Arc) -> .send(JvmCommand::Initialize { j4rs_path: dirs.j4rs, respond_to: tx, + context: server.clone(), }) .await .map_err(|e| format!("Failed to send command to initialize J4RS: {}", e))?; @@ -197,10 +201,16 @@ impl PatchBukkitPlugin { pub fn new() -> Self { let (tx, rx) = mpsc::channel(100); let command_tx = tx.clone(); + + #[tokio::main] + pub async fn jvm_thread_task(command_tx: Sender, rx: Receiver) { + JvmWorker::new(command_tx, rx).attach_thread().await; + } + std::thread::Builder::new() .name("patchbukkit-jvm-worker".to_string()) .spawn(move || { - JvmWorker::new(command_tx.clone(), rx).attach_thread(); + jvm_thread_task(command_tx.clone(), rx); }) .unwrap(); PatchBukkitPlugin { command_tx: tx }