Add entity play sound

This commit is contained in:
Bjorn Beishline 2026-02-01 11:04:32 -07:00
parent 18442c2298
commit 185822a5fe
8 changed files with 159 additions and 91 deletions

View File

@ -19,6 +19,7 @@ public class NativePatchBukkit {
private static MethodHandle getWorldNative;
private static MethodHandle freeStringNative;
private static MethodHandle getRegistryDataNative;
private static MethodHandle entityPlaySoundNative;
// Struct layout matching Rust's #[repr(C)] AbilitiesFFI
private static final StructLayout ABILITIES_LAYOUT =
@ -186,7 +187,8 @@ public class NativePatchBukkit {
long getLocationAddr,
long freeStringAddr,
long getWorldAddr,
long getRegistryDataAddr
long getRegistryDataAddr,
long entityPlaySoundAddr
) {
// void rust_send_message(const char* uuid, const char* message)
sendMessageNative = LINKER.downcallHandle(
@ -247,6 +249,20 @@ public class NativePatchBukkit {
MemorySegment.ofAddress(getRegistryDataAddr),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)
);
// void rust_entity_play_sound(const char* player_uuid, const char* sound_name,
// const char* sound_category, const char* entity_uuid, float volume, float pitch)
entityPlaySoundNative = LINKER.downcallHandle(
MemorySegment.ofAddress(entityPlaySoundAddr),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, // player_uuid
ValueLayout.ADDRESS, // sound_name
ValueLayout.ADDRESS, // sound_category
ValueLayout.ADDRESS, // entity_uuid
ValueLayout.JAVA_FLOAT, // volume
ValueLayout.JAVA_FLOAT // pitch
)
);
}
/**
@ -430,4 +446,41 @@ public class NativePatchBukkit {
throw new RuntimeException("Failed to get registry data: " + registryName, t);
}
}
/**
* Play a sound effect attached to an entity, heard by a specific player.
*
* @param playerUuid The player who will hear the sound
* @param soundName Sound identifier (e.g. "block.note_block.chime")
* @param soundCategory Category name (e.g. "master", "music", "blocks")
* @param entityUuid The entity the sound is attached to
* @param volume Volume (1.0 = normal)
* @param pitch Pitch (1.0 = normal)
*/
public static void entityPlaySound(
UUID playerUuid,
String soundName,
String soundCategory,
UUID entityUuid,
float volume,
float pitch
) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment playerUuidStr = arena.allocateFrom(playerUuid.toString());
MemorySegment soundNameStr = arena.allocateFrom(soundName);
MemorySegment soundCategoryStr = arena.allocateFrom(soundCategory);
MemorySegment entityUuidStr = arena.allocateFrom(entityUuid.toString());
entityPlaySoundNative.invokeExact(
playerUuidStr,
soundNameStr,
soundCategoryStr,
entityUuidStr,
volume,
pitch
);
} catch (Throwable t) {
throw new RuntimeException("Failed to call native entityPlaySound", t);
}
}
}

View File

@ -711,8 +711,7 @@ public class PatchBukkitPlayer
@Override
public void playSound(Entity entity, Sound sound, SoundCategory category, float volume, float pitch) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'playSound'");
NativePatchBukkit.entityPlaySound(this.uuid, sound.toString(), category.name(), entity.getUniqueId(), volume, pitch);
}
@Override

115
rust/Cargo.lock generated
View File

@ -1695,8 +1695,10 @@ dependencies = [
"log",
"pumpkin",
"pumpkin-api-macros",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master)",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"pumpkin-protocol",
"pumpkin-util",
"rand",
"rust-embed",
"serde",
"serde-saphyr",
@ -1964,7 +1966,7 @@ dependencies = [
[[package]]
name = "pumpkin"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"arc-swap",
"base64",
@ -1979,12 +1981,12 @@ dependencies = [
"num-bigint",
"pkcs8 0.11.0-rc.10",
"pumpkin-config",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"pumpkin-inventory",
"pumpkin-macros",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-nbt",
"pumpkin-protocol",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util",
"pumpkin-world",
"rand",
"rsa",
@ -2007,7 +2009,7 @@ dependencies = [
[[package]]
name = "pumpkin-api-macros"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"proc-macro-error2",
"proc-macro2",
@ -2018,10 +2020,10 @@ dependencies = [
[[package]]
name = "pumpkin-config"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"log",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util",
"serde",
"toml",
"uuid",
@ -2030,35 +2032,15 @@ dependencies = [
[[package]]
name = "pumpkin-data"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master#007b97eed8243cf744cb0353b12438db8eab2938"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"crc-fast",
"heck",
"indexmap",
"phf",
"proc-macro2",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master)",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master)",
"quote",
"rayon",
"serde",
"serde_json",
"serde_json5",
"syn",
]
[[package]]
name = "pumpkin-data"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
dependencies = [
"crc-fast",
"heck",
"indexmap",
"phf",
"proc-macro2",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-nbt",
"pumpkin-util",
"quote",
"rayon",
"serde",
@ -2070,13 +2052,13 @@ dependencies = [
[[package]]
name = "pumpkin-inventory"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"crossbeam-utils",
"log",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"pumpkin-protocol",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util",
"pumpkin-world",
"thiserror",
"tokio",
@ -2085,11 +2067,11 @@ dependencies = [
[[package]]
name = "pumpkin-macros"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"quote",
"syn",
]
@ -2097,19 +2079,7 @@ dependencies = [
[[package]]
name = "pumpkin-nbt"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master#007b97eed8243cf744cb0353b12438db8eab2938"
dependencies = [
"bytes",
"cesu8",
"flate2",
"serde",
"thiserror",
]
[[package]]
name = "pumpkin-nbt"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"bytes",
"cesu8",
@ -2121,17 +2091,17 @@ dependencies = [
[[package]]
name = "pumpkin-protocol"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"aes",
"async-compression",
"bitflags",
"bytes",
"cfb8",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"pumpkin-macros",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-nbt",
"pumpkin-util",
"pumpkin-world",
"serde",
"take_mut",
@ -2143,7 +2113,7 @@ dependencies = [
[[package]]
name = "pumpkin-util"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master#007b97eed8243cf744cb0353b12438db8eab2938"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"base64",
"bytes",
@ -2155,32 +2125,7 @@ dependencies = [
"num-traits",
"p384",
"proc-macro2",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/BjornTheProgrammer/Pumpkin.git?branch=master)",
"quote",
"serde",
"serde_json",
"syn",
"thiserror",
"tokio",
"uuid",
]
[[package]]
name = "pumpkin-util"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
dependencies = [
"base64",
"bytes",
"colored",
"ecdsa",
"enum_dispatch",
"md5",
"num-derive",
"num-traits",
"p384",
"proc-macro2",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-nbt",
"quote",
"serde",
"serde_json",
@ -2193,7 +2138,7 @@ dependencies = [
[[package]]
name = "pumpkin-world"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#f93be68caa3969502c13d5d284da59fe1017ed9a"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa"
dependencies = [
"bitflags",
"bytes",
@ -2211,9 +2156,9 @@ dependencies = [
"num-traits",
"num_cpus",
"pumpkin-config",
"pumpkin-data 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-nbt 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-util 0.1.0-dev+1.21.11 (git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master)",
"pumpkin-data",
"pumpkin-nbt",
"pumpkin-util",
"rand",
"rustc-hash",
"ruzstd",

View File

@ -8,7 +8,8 @@ crate-type = ["cdylib"]
[dependencies]
pumpkin = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin" }
pumpkin-data = { git = "https://github.com/BjornTheProgrammer/Pumpkin.git", branch = "master", package = "pumpkin-data" }
pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-data" }
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-protocol" }
pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-util" }
pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-api-macros" }
@ -28,6 +29,8 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde-saphyr = "0.0.16"
rand = { git = "https://github.com/rust-random/rand"}
[build-dependencies]
j4rs = { version = "0.24", artifact = "cdylib", lib = true }
env_logger = "0.11.8"

View File

@ -4,7 +4,6 @@ use pumpkin_util::text::TextComponent;
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
#[unsafe(no_mangle)]
pub extern "C" fn rust_send_message(uuid_ptr: *const c_char, message_ptr: *const c_char) {
let uuid_str = get_string(uuid_ptr);
let message = get_string(message_ptr);

View File

@ -10,6 +10,7 @@ pub mod location;
pub mod memory;
pub mod message;
pub mod registry;
pub mod sound;
pub mod utils;
pub mod world;
@ -44,6 +45,7 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
let free_string_addr = memory::rust_free_string as *const () as i64;
let get_world_addr = world::rust_get_world as *const () as i64;
let rust_get_registry_data_addr = registry::rust_get_registry_data as *const () as i64;
let rust_entity_play_sound_addr = sound::rust_entity_play_sound as *const () as i64;
jvm.invoke_static(
"org.patchbukkit.bridge.NativePatchBukkit",
@ -57,6 +59,7 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
InvocationArg::try_from(free_string_addr)?.into_primitive()?,
InvocationArg::try_from(get_world_addr)?.into_primitive()?,
InvocationArg::try_from(rust_get_registry_data_addr)?.into_primitive()?,
InvocationArg::try_from(rust_entity_play_sound_addr)?.into_primitive()?,
],
)?;

View File

@ -17,7 +17,6 @@ impl Registry {
}
}
#[unsafe(no_mangle)]
pub extern "C" fn rust_get_registry_data(registry_name: *const c_char) -> *const c_char {
let name = get_string(registry_name);

View File

@ -0,0 +1,67 @@
use std::ffi::{c_char, c_float};
use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector};
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_protocol::{IdOr, java::client::play::CEntitySoundEffect};
use rand::{RngExt, rng};
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
pub extern "C" fn rust_entity_play_sound(
player_uuid_ptr: *const c_char,
sound_name_ptr: *const c_char,
sound_category_ptr: *const c_char,
entity_uuid_ptr: *const c_char,
volume: c_float,
pitch: c_float,
) {
let player_uuid_str = get_string(player_uuid_ptr);
let sound_name = get_string(sound_name_ptr);
let sound_category = get_string(sound_category_ptr);
let entity_uuid_str = get_string(entity_uuid_ptr);
if let Some(ctx) = CALLBACK_CONTEXT.get() {
let player_uuid = uuid::Uuid::parse_str(&player_uuid_str).unwrap();
let entity_uuid = uuid::Uuid::parse_str(&entity_uuid_str).unwrap();
ctx.runtime.spawn(async move {
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid);
let entity = ctx.plugin_context.server.select_entities(
&TargetSelector::new(EntitySelectorType::Uuid(entity_uuid)),
None,
);
if entity.len() != 1 {
return;
}
let entity = entity.first().unwrap().get_entity();
if let Some(player) = player {
let sound = match Sound::from_name(&sound_name) {
Some(sound) => sound,
None => return,
};
let category = match SoundCategory::from_name(&sound_category) {
Some(category) => category,
None => return,
};
let seed = rng().random::<i64>();
player
.client
.enqueue_packet(&CEntitySoundEffect::new(
IdOr::Id(sound as u16),
category,
entity.entity_id.into(),
volume,
pitch,
seed,
))
.await;
}
});
}
}