From 8e4b13d42b39ccfed7ae91dae7759d6e1a86b7aa Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:50:39 -0700 Subject: [PATCH] Remove last of NativePatchBukkit commands --- .../patchbukkit/bridge/NativePatchBukkit.java | 374 ------------------ .../patchbukkit/entity/PatchBukkitEntity.java | 8 +- .../patchbukkit/entity/PatchBukkitPlayer.java | 75 ++-- .../events/PatchBukkitEventManager.java | 1 - .../registry/PatchBukkitRegistry.java | 63 ++- .../registry/PatchBukkitRegistryAccess.java | 44 +-- .../patchbukkit/world/PatchBukkitWorld.java | 5 +- proto/patchbukkit/bridge.proto | 7 + proto/patchbukkit/registry.proto | 31 ++ proto/patchbukkit/sound.proto | 31 ++ rust/src/java/native_callbacks/location.rs | 4 +- rust/src/java/native_callbacks/memory.rs | 9 - rust/src/java/native_callbacks/mod.rs | 51 +-- rust/src/java/native_callbacks/registry.rs | 74 ++-- rust/src/java/native_callbacks/sound.rs | 154 ++++---- rust/src/java/native_callbacks/world.rs | 39 -- rust/src/proto/mod.rs | 8 + 17 files changed, 274 insertions(+), 704 deletions(-) delete mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java create mode 100644 proto/patchbukkit/registry.proto create mode 100644 proto/patchbukkit/sound.proto delete mode 100644 rust/src/java/native_callbacks/memory.rs delete mode 100644 rust/src/java/native_callbacks/world.rs diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java deleted file mode 100644 index d8ff5a9..0000000 --- a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/NativePatchBukkit.java +++ /dev/null @@ -1,374 +0,0 @@ -package org.patchbukkit.bridge; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.VarHandle; -import java.util.UUID; - -import org.bukkit.event.Event; -import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; -import org.patchbukkit.events.PatchBukkitEventSerializer; - -public class NativePatchBukkit { - - private static final Linker LINKER = Linker.nativeLinker(); - - private static MethodHandle getWorldNative; - private static MethodHandle freeStringNative; - private static MethodHandle getRegistryDataNative; - private static MethodHandle playerEntityPlaySoundNative; - private static MethodHandle playerPlaySoundNative; - - // Struct layout matching Rust's #[repr(C)] AbilitiesFFI - private static final StructLayout ABILITIES_LAYOUT = - MemoryLayout.structLayout( - ValueLayout.JAVA_BOOLEAN.withName("invulnerable"), - ValueLayout.JAVA_BOOLEAN.withName("flying"), - ValueLayout.JAVA_BOOLEAN.withName("allow_flying"), - ValueLayout.JAVA_BOOLEAN.withName("creative"), - ValueLayout.JAVA_BOOLEAN.withName("allow_modify_world"), - MemoryLayout.paddingLayout(3), // Padding to align f32 to 4-byte boundary - ValueLayout.JAVA_FLOAT.withName("fly_speed"), - ValueLayout.JAVA_FLOAT.withName("walk_speed") - ); - - // VarHandles for accessing struct fields - private static final VarHandle INVULNERABLE = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("invulnerable") - ); - private static final VarHandle FLYING = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("flying") - ); - private static final VarHandle ALLOW_FLYING = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("allow_flying") - ); - private static final VarHandle CREATIVE = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("creative") - ); - private static final VarHandle ALLOW_MODIFY_WORLD = - ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("allow_modify_world") - ); - private static final VarHandle FLY_SPEED = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("fly_speed") - ); - private static final VarHandle WALK_SPEED = ABILITIES_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("walk_speed") - ); - - // Struct layout for Vec3 - private static final StructLayout VEC3_LAYOUT = MemoryLayout.structLayout( - ValueLayout.JAVA_DOUBLE.withName("x"), - ValueLayout.JAVA_DOUBLE.withName("y"), - ValueLayout.JAVA_DOUBLE.withName("z") - ); - - // VarHandles for Vec3 fields - private static final VarHandle VEC3_X = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("x") - ); - private static final VarHandle VEC3_Y = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("y") - ); - private static final VarHandle VEC3_Z = VEC3_LAYOUT.varHandle( - MemoryLayout.PathElement.groupElement("z") - ); - - public record Vec3(double x, double y, double z) {} - - /** - * Java record to hold player abilities. - */ - public record Abilities( - boolean invulnerable, - boolean flying, - boolean allowFlying, - boolean creative, - boolean allowModifyWorld, - float flySpeed, - float walkSpeed - ) { - // "Wither" methods - return a copy with one field changed - public Abilities withInvulnerable(boolean value) { - return new Abilities( - value, - flying, - allowFlying, - creative, - allowModifyWorld, - flySpeed, - walkSpeed - ); - } - - public Abilities withFlying(boolean value) { - return new Abilities( - invulnerable, - value, - allowFlying, - creative, - allowModifyWorld, - flySpeed, - walkSpeed - ); - } - - public Abilities withAllowFlying(boolean value) { - return new Abilities( - invulnerable, - flying, - value, - creative, - allowModifyWorld, - flySpeed, - walkSpeed - ); - } - - public Abilities withCreative(boolean value) { - return new Abilities( - invulnerable, - flying, - allowFlying, - value, - allowModifyWorld, - flySpeed, - walkSpeed - ); - } - - public Abilities withAllowModifyWorld(boolean value) { - return new Abilities( - invulnerable, - flying, - allowFlying, - creative, - value, - flySpeed, - walkSpeed - ); - } - - public Abilities withFlySpeed(float value) { - return new Abilities( - invulnerable, - flying, - allowFlying, - creative, - allowModifyWorld, - value, - walkSpeed - ); - } - - public Abilities withWalkSpeed(float value) { - return new Abilities( - invulnerable, - flying, - allowFlying, - creative, - allowModifyWorld, - flySpeed, - value - ); - } - } - - /** - * Called from Rust during initialization to register native function pointers. - */ - public static void initCallbacks( - long freeStringAddr, - long getWorldAddr, - long getRegistryDataAddr, - long playerEntityPlaySoundAddr, - long playerPlaySoundAddr - ) { - - // const char* rust_get_world(const char* uuid) - getWorldNative = LINKER.downcallHandle( - MemorySegment.ofAddress(getWorldAddr), - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS) - ); - - // void rust_free_string(const char* str) - freeStringNative = LINKER.downcallHandle( - MemorySegment.ofAddress(freeStringAddr), - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS) - ); - - // const char* rust_get_registry_entries(const char* registry_name) - getRegistryDataNative = LINKER.downcallHandle( - MemorySegment.ofAddress(getRegistryDataAddr), - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS) - ); - - // void rust_player_entity_play_sound(const char* player_uuid, const char* sound_name, - // const char* sound_category, const char* entity_uuid, float volume, float pitch) - playerEntityPlaySoundNative = LINKER.downcallHandle( - MemorySegment.ofAddress(playerEntityPlaySoundAddr), - 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 - ) - ); - - // void rust_player_play_sound(const char* player_uuid, const char* sound_name, - // const char* sound_category, double x, double y, double z, float volume, float pitch) - playerPlaySoundNative = LINKER.downcallHandle( - MemorySegment.ofAddress(playerPlaySoundAddr), - FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS, // player_uuid - ValueLayout.ADDRESS, // sound_name - ValueLayout.ADDRESS, // sound_category - ValueLayout.JAVA_DOUBLE, // x - ValueLayout.JAVA_DOUBLE, // y - ValueLayout.JAVA_DOUBLE, // z - ValueLayout.JAVA_FLOAT, // volume - ValueLayout.JAVA_FLOAT // pitch - ) - ); - } - - /** - * Free a string allocated by the Rust side. - */ - private static void freeRustString(MemorySegment ptr) { - try { - freeStringNative.invokeExact(ptr); - } catch (Throwable t) { - throw new RuntimeException("Failed to free Rust string", t); - } - } - - /** - * Get the world UUID for an entity. - */ - public static String getWorld(UUID entityUuid) { - try (Arena arena = Arena.ofConfined()) { - MemorySegment uuidStr = arena.allocateFrom(entityUuid.toString()); - MemorySegment resultPtr = (MemorySegment) getWorldNative.invokeExact(uuidStr); - - if (resultPtr.equals(MemorySegment.NULL)) { - return null; - } - - // Read the string, then free the Rust-allocated memory - try { - String result = resultPtr.reinterpret(Long.MAX_VALUE).getString(0); - return result; - } finally { - freeRustString(resultPtr); - } - } catch (Throwable t) { - throw new RuntimeException("Failed to call native getWorld", t); - } - } - - /** - * Get all data for a registry (entries + tags) as JSON. - * - * @param registryName The registry identifier (e.g. "sound_event", "block") - * @return JSON string with "entries" and "tags" fields, or null if registry unknown - */ - public static String getRegistryData(String registryName) { - try (Arena arena = Arena.ofConfined()) { - MemorySegment nameStr = arena.allocateFrom(registryName); - MemorySegment resultPtr = (MemorySegment) getRegistryDataNative.invokeExact(nameStr); - - if (resultPtr.equals(MemorySegment.NULL)) return null; - - try { - return resultPtr.reinterpret(Long.MAX_VALUE).getString(0); - } finally { - freeRustString(resultPtr); - } - } catch (Throwable t) { - 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 playerEntityPlaySound( - 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()); - - playerEntityPlaySoundNative.invokeExact( - playerUuidStr, - soundNameStr, - soundCategoryStr, - entityUuidStr, - volume, - pitch - ); - } catch (Throwable t) { - throw new RuntimeException("Failed to call native entityPlaySound", t); - } - } - - /** - * Play a sound effect at a location, 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 x X coordinate - * @param y Y coordinate - * @param z Z coordinate - * @param volume Volume (1.0 = normal) - * @param pitch Pitch (1.0 = normal) - */ - public static void playerPlaySound( - UUID playerUuid, - String soundName, - String soundCategory, - double x, - double y, - double z, - 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); - - playerPlaySoundNative.invokeExact( - playerUuidStr, - soundNameStr, - soundCategoryStr, - x, - y, - z, - volume, - pitch - ); - } catch (Throwable t) { - throw new RuntimeException("Failed to call native playerPlaySound", t); - } - } -} 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 1a5978f..c950b98 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -38,7 +38,6 @@ import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.patchbukkit.bridge.BridgeUtils; -import org.patchbukkit.bridge.NativePatchBukkit; import org.patchbukkit.world.PatchBukkitWorld; import io.papermc.paper.datacomponent.DataComponentType; @@ -325,11 +324,8 @@ public class PatchBukkitEntity implements Entity { @Override public @NotNull World getWorld() { - var world_uuid_str = NativePatchBukkit.getWorld(this.uuid); - UUID world_uuid = UUID.fromString(world_uuid_str); - var world = PatchBukkitWorld.getOrCreate(world_uuid); - - return world; + var location = NativeBridgeFfi.getLocation(BridgeUtils.convertUuid(this.uuid)); + return PatchBukkitWorld.getOrCreate(BridgeUtils.convertUuid(location.getWorld().getUuid())); } @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 571608c..458bbb9 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java @@ -5,15 +5,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.time.Duration; import java.time.Instant; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import org.bukkit.BanEntry; @@ -63,7 +55,6 @@ import org.jetbrains.annotations.Range; import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; import org.patchbukkit.bridge.BridgeUtils; -import org.patchbukkit.bridge.NativePatchBukkit; import org.patchbukkit.registry.PatchBukkitSound; import com.destroystokyo.paper.ClientOption; @@ -82,6 +73,8 @@ import net.md_5.bungee.api.chat.BaseComponent; import patchbukkit.bridge.NativeBridgeFfi; import patchbukkit.message.SendMessageRequest; import patchbukkit.abilities.SetAbilitiesRequest; +import patchbukkit.sound.PlayerEntityPlaySoundRequest; +import patchbukkit.sound.PlayerPlaySoundRequest; @SuppressWarnings({ "deprecation", "removal" }) public class PatchBukkitPlayer @@ -694,49 +687,87 @@ public class PatchBukkitPlayer @Override public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) { var patchBukkitSound = (PatchBukkitSound) sound; - NativePatchBukkit.playerPlaySound(this.uuid, patchBukkitSound.getOriginalName(), category.name(), location.x(), location.y(), location.z(), volume, pitch); + this.playSound0(location, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.empty()); } @Override public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) { - NativePatchBukkit.playerPlaySound(this.uuid, sound, category.name(), location.x(), location.y(), location.z(), volume, pitch); + this.playSound0(location, sound, category, volume, pitch, OptionalLong.empty()); } @Override public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch, long seed) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playSound'"); + var patchBukkitSound = (PatchBukkitSound) sound; + this.playSound0(location, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.of(seed)); } @Override public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch, long seed) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playSound'"); + this.playSound0(location, sound, category, volume, pitch, OptionalLong.of(seed)); + } + + private void playSound0(Location location, String sound, SoundCategory category, float volume, float pitch, OptionalLong seed) { + var request = PlayerPlaySoundRequest.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(this.uuid)) + .setLocation( + patchbukkit.common.Location.newBuilder() + .setPosition( + patchbukkit.common.Vec3.newBuilder() + .setX(location.x()) + .setY(location.y()) + .setZ(location.z()) + ).setWorld( + patchbukkit.common.World.newBuilder().setUuid(BridgeUtils.convertUuid(location.getWorld().getUID())) + ).setPitch(location.getPitch()) + .setYaw(location.getYaw()) + ).setSound( + patchbukkit.sound.Sound.newBuilder() + .setCategory(category.name()) + .setName(sound) + ).setVolume(volume) + .setPitch(pitch); + + if (seed.isPresent()) request.setSeed(seed.getAsLong()); + NativeBridgeFfi.playerPlaySound(request.build()); } @Override public void playSound(Entity entity, Sound sound, SoundCategory category, float volume, float pitch) { var patchBukkitSound = (PatchBukkitSound) sound; - NativePatchBukkit.playerEntityPlaySound(this.uuid, patchBukkitSound.getOriginalName(), category.name(), entity.getUniqueId(), volume, pitch); + this.playSound0(entity, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.empty()); } @Override public void playSound(Entity entity, String sound, SoundCategory category, float volume, float pitch) { - NativePatchBukkit.playerEntityPlaySound(this.uuid, sound, category.name(), entity.getUniqueId(), volume, pitch); + this.playSound0(entity, sound, category, volume, pitch, OptionalLong.empty()); } @Override public void playSound(Entity entity, Sound sound, SoundCategory category, float volume, float pitch, long seed) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playSound'"); + var patchBukkitSound = (PatchBukkitSound) sound; + this.playSound0(entity, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.of(seed)); } @Override public void playSound(Entity entity, String sound, SoundCategory category, float volume, float pitch, long seed) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playSound'"); + this.playSound0(entity, sound, category, volume, pitch, OptionalLong.of(seed)); + } + + private void playSound0(Entity entity, String sound, SoundCategory category, float volume, float pitch, OptionalLong seed) { + var request = PlayerEntityPlaySoundRequest.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(this.uuid)) + .setEntityUuid(BridgeUtils.convertUuid(entity.getUniqueId())) + .setSound( + patchbukkit.sound.Sound.newBuilder() + .setCategory(category.name()) + .setName(sound) + ).setVolume(volume) + .setPitch(pitch); + + if (seed.isPresent()) request.setSeed(seed.getAsLong()); + NativeBridgeFfi.playerEntityPlaySound(request.build()); } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java index f744a29..4478f2a 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java @@ -13,7 +13,6 @@ 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; diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java index b734ad2..ee66859 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java @@ -12,57 +12,44 @@ import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.patchbukkit.bridge.NativePatchBukkit; +import patchbukkit.bridge.NativeBridgeFfi; +import patchbukkit.registry.GetRegistryDataRequest; +import patchbukkit.registry.GetRegistryDataResponse; +import patchbukkit.registry.RegistryType; import java.util.*; import java.util.function.Function; import java.util.stream.Stream; -public class PatchBukkitRegistry implements Registry { +public class PatchBukkitRegistry
implements Registry {
private final Map factory
) {
- String json = NativePatchBukkit.getRegistryData(registryName);
- if (json == null) return;
+ if (registryType == null) return;
- JsonObject root = JsonParser.parseString(json).getAsJsonObject();
+ GetRegistryDataRequest request = GetRegistryDataRequest.newBuilder()
+ .setRegistry(registryType)
+ .build();
- JsonArray entriesArray = root.getAsJsonArray("entries");
- if (entriesArray != null) {
- for (JsonElement element : entriesArray) {
- B value = factory.apply(element.getAsJsonObject());
- if (value != null) {
- entries.put(value.getKey(), value);
- }
+ GetRegistryDataResponse response = NativeBridgeFfi.getRegistryData(request);
+ if (response == null) return;
+
+ List protoEntries = extractor.apply(response);
+ for (P protoEntry : protoEntries) {
+ B value = factory.apply(protoEntry);
+ if (value != null) {
+ entries.put(value.getKey(), value);
}
}
-
- JsonElement tagsElement = root.get("tags");
- if (tagsElement != null && tagsElement.isJsonObject()) {
- loadTags(tagsElement.getAsJsonObject(), registryKey);
- }
}
+
private void loadTags(JsonObject tagMap, RegistryKey registryKey) {
for (Map.Entry