Remove last of NativePatchBukkit commands
This commit is contained in:
parent
4829d59c1e
commit
8e4b13d42b
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<B extends Keyed> implements Registry<B> {
|
||||
public class PatchBukkitRegistry<P, B extends Keyed> implements Registry<B> {
|
||||
|
||||
private final Map<NamespacedKey, B> entries = new LinkedHashMap<>();
|
||||
private final Map<String, PatchBukkitTag<B>> tags = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Build a registry by fetching JSON from the native side and parsing
|
||||
* entries and tags from the combined response.
|
||||
*
|
||||
* Expected JSON shape:
|
||||
* {
|
||||
* "entries": [{"name": "...", "id": ...}, ...],
|
||||
* "tags": {"minecraft:logs": ["minecraft:oak_log", ...], ...}
|
||||
* }
|
||||
*
|
||||
* @param registryName The native registry name (e.g. "sound_event")
|
||||
* @param registryKey The Paper RegistryKey for this registry
|
||||
* @param factory Converts a single JsonObject into a B instance
|
||||
*/
|
||||
public PatchBukkitRegistry(
|
||||
String registryName,
|
||||
RegistryKey<B> registryKey,
|
||||
Function<JsonObject, B> factory
|
||||
RegistryType registryType,
|
||||
Function<GetRegistryDataResponse, List<P>> extractor,
|
||||
Function<P, B> 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<P> 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<B> registryKey) {
|
||||
for (Map.Entry<String, JsonElement> entry : tagMap.entrySet()) {
|
||||
String tagName = entry.getKey();
|
||||
@ -116,6 +103,14 @@ public class PatchBukkitRegistry<B extends Keyed> implements Registry<B> {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static <B extends Keyed> PatchBukkitRegistry<Object, B> empty(RegistryKey<B> registryKey) {
|
||||
return new PatchBukkitRegistry<>(
|
||||
null,
|
||||
response -> Collections.emptyList(),
|
||||
obj -> null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Collection<Tag<B>> getTags() {
|
||||
return Collections.unmodifiableCollection(tags.values());
|
||||
|
||||
@ -6,11 +6,13 @@ import org.bukkit.Keyed;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.Sound;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.patchbukkit.bridge.NativePatchBukkit;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import patchbukkit.registry.GetRegistryDataResponse;
|
||||
import patchbukkit.registry.RegistryType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
@ -22,21 +24,21 @@ public class PatchBukkitRegistryAccess implements RegistryAccess {
|
||||
* Maps a RegistryKey to (native registry name, factory function).
|
||||
* Add an entry here for each registry you support.
|
||||
*/
|
||||
private static final Map<RegistryKey<?>, RegistryFactory<?>> FACTORIES = Map.of(
|
||||
private static final Map<RegistryKey<?>, RegistryFactory<?, ?>> FACTORIES = Map.of(
|
||||
RegistryKey.SOUND_EVENT, new RegistryFactory<>(
|
||||
"sound_event",
|
||||
RegistryKey.SOUND_EVENT,
|
||||
json -> new PatchBukkitSound(
|
||||
json.get("name").getAsString(),
|
||||
json.get("id").getAsInt()
|
||||
RegistryType.SOUND_EVENT,
|
||||
response -> response.getSoundEvent().getSoundEventsList(),
|
||||
data -> new PatchBukkitSound(
|
||||
data.getName(),
|
||||
data.getId()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
private record RegistryFactory<B extends Keyed>(
|
||||
String nativeRegistryName,
|
||||
RegistryKey<B> registryKey,
|
||||
Function<JsonObject, B> factory
|
||||
private record RegistryFactory<P, B extends Keyed>(
|
||||
RegistryType registryType,
|
||||
Function<GetRegistryDataResponse, List<P>> extractor,
|
||||
Function<P, B> factory
|
||||
) {}
|
||||
|
||||
@Override
|
||||
@ -51,25 +53,21 @@ public class PatchBukkitRegistryAccess implements RegistryAccess {
|
||||
if (registryKey == null) return null;
|
||||
|
||||
return (Registry<T>) instances.computeIfAbsent(registryKey, key -> {
|
||||
RegistryFactory<?> factoryEntry = FACTORIES.get(key);
|
||||
RegistryFactory<?, ?> factoryEntry = FACTORIES.get(key);
|
||||
if (factoryEntry != null) {
|
||||
return buildRegistry(factoryEntry);
|
||||
}
|
||||
|
||||
// We return an empty registry for unsupported types, since if we don't the server will crash immediately when the registry class is first loaded
|
||||
return new PatchBukkitRegistry<>(
|
||||
key.key().value(),
|
||||
(RegistryKey) key,
|
||||
json -> null
|
||||
);
|
||||
// Return an empty registry for unsupported types
|
||||
return PatchBukkitRegistry.empty((RegistryKey) key);
|
||||
});
|
||||
}
|
||||
|
||||
private static <B extends Keyed> PatchBukkitRegistry<B> buildRegistry(RegistryFactory<B> factory) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <P, B extends Keyed> PatchBukkitRegistry<P, B> buildRegistry(RegistryFactory<P, B> factory) {
|
||||
return new PatchBukkitRegistry<>(
|
||||
factory.nativeRegistryName(),
|
||||
factory.registryKey(),
|
||||
factory.factory()
|
||||
factory.registryType(),
|
||||
factory.extractor(),
|
||||
factory.factory()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1589,10 +1589,7 @@ public class PatchBukkitWorld
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getUID() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException(
|
||||
"Unimplemented method 'getUID'"
|
||||
);
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -7,6 +7,8 @@ import "patchbukkit/abilities.proto";
|
||||
import "patchbukkit/common/types.proto";
|
||||
import "patchbukkit/events/event.proto";
|
||||
import "patchbukkit/message.proto";
|
||||
import "patchbukkit/registry.proto";
|
||||
import "patchbukkit/sound.proto";
|
||||
|
||||
service NativeBridge {
|
||||
rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.abilities.Abilities);
|
||||
@ -18,4 +20,9 @@ service NativeBridge {
|
||||
rpc SendMessage(patchbukkit.message.SendMessageRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc GetLocation(patchbukkit.common.UUID) returns (patchbukkit.common.Location);
|
||||
|
||||
rpc GetRegistryData(patchbukkit.registry.GetRegistryDataRequest) returns (patchbukkit.registry.GetRegistryDataResponse);
|
||||
|
||||
rpc PlayerEntityPlaySound(patchbukkit.sound.PlayerEntityPlaySoundRequest) returns (google.protobuf.Empty);
|
||||
rpc PlayerPlaySound(patchbukkit.sound.PlayerPlaySoundRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
31
proto/patchbukkit/registry.proto
Normal file
31
proto/patchbukkit/registry.proto
Normal file
@ -0,0 +1,31 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package patchbukkit.registry;
|
||||
|
||||
import "patchbukkit/common/types.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "patchbukkit.registry";
|
||||
|
||||
enum RegistryType {
|
||||
SOUND_EVENT = 0;
|
||||
}
|
||||
|
||||
message GetRegistryDataRequest {
|
||||
RegistryType registry = 1;
|
||||
}
|
||||
|
||||
message GetRegistryDataResponse {
|
||||
oneof registry {
|
||||
SoundEventRegistryData sound_event = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message SoundEvent {
|
||||
uint32 id = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message SoundEventRegistryData {
|
||||
repeated SoundEvent sound_events = 1;
|
||||
}
|
||||
31
proto/patchbukkit/sound.proto
Normal file
31
proto/patchbukkit/sound.proto
Normal file
@ -0,0 +1,31 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package patchbukkit.sound;
|
||||
|
||||
import "patchbukkit/common/types.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "patchbukkit.sound";
|
||||
|
||||
message Sound {
|
||||
string name = 1;
|
||||
string category = 2;
|
||||
}
|
||||
|
||||
message PlayerEntityPlaySoundRequest {
|
||||
patchbukkit.common.UUID player_uuid = 1;
|
||||
Sound sound = 2;
|
||||
patchbukkit.common.UUID entity_uuid = 3;
|
||||
float volume = 4;
|
||||
float pitch = 5;
|
||||
optional int64 seed = 6;
|
||||
}
|
||||
|
||||
message PlayerPlaySoundRequest {
|
||||
patchbukkit.common.UUID player_uuid = 1;
|
||||
Sound sound = 2;
|
||||
patchbukkit.common.Location location = 3;
|
||||
float volume = 4;
|
||||
float pitch = 5;
|
||||
optional int64 seed = 6;
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
use std::ffi::c_char;
|
||||
|
||||
use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector};
|
||||
|
||||
use crate::{
|
||||
java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string},
|
||||
java::native_callbacks::CALLBACK_CONTEXT,
|
||||
proto::patchbukkit::common::{Location, Uuid, Vec3, World},
|
||||
};
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
use std::ffi::{CString, c_char};
|
||||
|
||||
pub extern "C" fn rust_free_string(ptr: *mut c_char) {
|
||||
if !ptr.is_null() {
|
||||
unsafe {
|
||||
drop(CString::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use j4rs::{InvocationArg, Jvm};
|
||||
use j4rs::Jvm;
|
||||
use pumpkin::plugin::Context;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@ -16,9 +16,6 @@ pub use events::*;
|
||||
pub mod location;
|
||||
pub use location::*;
|
||||
|
||||
pub mod memory;
|
||||
pub use memory::*;
|
||||
|
||||
pub mod message;
|
||||
pub use message::*;
|
||||
|
||||
@ -28,9 +25,6 @@ pub use registry::*;
|
||||
pub mod sound;
|
||||
pub use sound::*;
|
||||
|
||||
pub mod world;
|
||||
pub use world::*;
|
||||
|
||||
pub mod utils;
|
||||
|
||||
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();
|
||||
@ -58,47 +52,6 @@ pub fn init_callback_context(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn encode_with_length(data: Vec<u8>) -> *mut u8 {
|
||||
let len = data.len() as u32;
|
||||
let mut result = Vec::with_capacity(4 + data.len());
|
||||
result.extend_from_slice(&len.to_le_bytes());
|
||||
result.extend(data);
|
||||
|
||||
let ptr = result.as_mut_ptr();
|
||||
std::mem::forget(result);
|
||||
ptr
|
||||
}
|
||||
|
||||
pub extern "C" fn rust_free_bytes(ptr: *mut u8, len: u32) {
|
||||
if !ptr.is_null() {
|
||||
unsafe {
|
||||
let total_len = 4 + len as usize;
|
||||
drop(Vec::from_raw_parts(ptr, total_len, total_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_player_entity_play_sound_addr =
|
||||
sound::rust_player_entity_play_sound as *const () as i64;
|
||||
let rust_player_play_sound_addr = sound::rust_player_play_sound as *const () as i64;
|
||||
|
||||
jvm.invoke_static(
|
||||
"org.patchbukkit.bridge.NativePatchBukkit",
|
||||
"initCallbacks",
|
||||
&[
|
||||
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_player_entity_play_sound_addr)?.into_primitive()?,
|
||||
InvocationArg::try_from(rust_player_play_sound_addr)?.into_primitive()?,
|
||||
],
|
||||
)?;
|
||||
|
||||
initialize_ffi_callbacks(jvm)?;
|
||||
|
||||
Ok(())
|
||||
initialize_ffi_callbacks(jvm)
|
||||
}
|
||||
|
||||
@ -1,60 +1,30 @@
|
||||
use std::{collections::HashMap, ffi::CString, os::raw::c_char};
|
||||
use crate::proto::patchbukkit::registry::{
|
||||
GetRegistryDataRequest, GetRegistryDataResponse, RegistryType, SoundEvent,
|
||||
SoundEventRegistryData, get_registry_data_response::Registry,
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::java::native_callbacks::utils::get_string;
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
struct Registry {
|
||||
pub entries: Vec<Value>,
|
||||
pub tags: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
pub const fn new(entries: Vec<Value>, tags: HashMap<String, Vec<String>>) -> Self {
|
||||
Self { entries, tags }
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub extern "C" fn rust_get_registry_data(registry_name: *const c_char) -> *const c_char {
|
||||
let name = get_string(registry_name);
|
||||
|
||||
let registry: Option<Registry> = match name.as_ref() {
|
||||
"sound_event" => {
|
||||
let entries: Vec<serde_json::Value> = pumpkin_data::sound::Sound::slice()
|
||||
pub fn ffi_native_bridge_get_registry_data_impl(
|
||||
request: GetRegistryDataRequest,
|
||||
) -> Option<GetRegistryDataResponse> {
|
||||
let registry = match request.registry {
|
||||
val if val == RegistryType::SoundEvent as i32 => {
|
||||
let sounds = pumpkin_data::sound::Sound::slice()
|
||||
.iter()
|
||||
.map(|s| {
|
||||
serde_json::json!({
|
||||
"name": s.to_name(),
|
||||
"id": *s as u16
|
||||
})
|
||||
let mut sound_event = SoundEvent::default();
|
||||
sound_event.id = *s as u32;
|
||||
sound_event.name = s.to_name().to_string();
|
||||
sound_event
|
||||
})
|
||||
.collect();
|
||||
Some(Registry::new(entries, HashMap::new()))
|
||||
.collect::<Vec<_>>();
|
||||
let mut data = SoundEventRegistryData::default();
|
||||
data.sound_events = sounds;
|
||||
Registry::SoundEvent(data)
|
||||
}
|
||||
_ => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let registry = match registry {
|
||||
Some(registry) => registry,
|
||||
None => return std::ptr::null(),
|
||||
};
|
||||
|
||||
let json_str = match serde_json::to_string(®istry) {
|
||||
Ok(json_str) => json_str,
|
||||
Err(err) => {
|
||||
log::error!("Failed to serialize registry: {err}");
|
||||
return std::ptr::null();
|
||||
}
|
||||
};
|
||||
|
||||
match CString::new(json_str) {
|
||||
Ok(cstring) => cstring.into_raw(),
|
||||
Err(err) => {
|
||||
log::error!("Failed to convert registry string to CString: {err}");
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
let mut response = GetRegistryDataResponse::default();
|
||||
response.registry = Some(registry);
|
||||
Some(response)
|
||||
}
|
||||
|
||||
@ -1,111 +1,89 @@
|
||||
use std::ffi::{c_char, c_double, c_float};
|
||||
|
||||
use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector};
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_protocol::{IdOr, java::client::play::CEntitySoundEffect};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::{RngExt, rng};
|
||||
|
||||
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
|
||||
use crate::{
|
||||
java::native_callbacks::CALLBACK_CONTEXT,
|
||||
proto::patchbukkit::sound::{PlayerEntityPlaySoundRequest, PlayerPlaySoundRequest},
|
||||
};
|
||||
|
||||
pub extern "C" fn rust_player_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);
|
||||
pub fn ffi_native_bridge_player_entity_play_sound_impl(
|
||||
request: PlayerEntityPlaySoundRequest,
|
||||
) -> Option<()> {
|
||||
log::error!("PlayerEntityPlaySoundRequest sent");
|
||||
let ctx = CALLBACK_CONTEXT.get()?;
|
||||
let player_uuid = uuid::Uuid::parse_str(&request.player_uuid?.value).ok()?;
|
||||
let entity_uuid = uuid::Uuid::parse_str(&request.entity_uuid?.value).ok()?;
|
||||
|
||||
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();
|
||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
|
||||
|
||||
ctx.runtime.spawn(async move {
|
||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid);
|
||||
let sound = request.sound?;
|
||||
let pumpkin_sound = Sound::from_name(&sound.name)?;
|
||||
let category = SoundCategory::from_name(&sound.category.to_lowercase())?;
|
||||
|
||||
let entity = ctx.plugin_context.server.select_entities(
|
||||
&TargetSelector::new(EntitySelectorType::Uuid(entity_uuid)),
|
||||
None,
|
||||
);
|
||||
if entity.len() != 1 {
|
||||
return;
|
||||
}
|
||||
let seed = rng().random::<i64>();
|
||||
|
||||
let entity = entity.first().unwrap().get_entity();
|
||||
ctx.runtime.spawn(async move {
|
||||
let entity = ctx.plugin_context.server.select_entities(
|
||||
&TargetSelector::new(EntitySelectorType::Uuid(entity_uuid)),
|
||||
None,
|
||||
);
|
||||
|
||||
if let Some(player) = player {
|
||||
let sound = match Sound::from_name(&sound_name) {
|
||||
Some(sound) => sound,
|
||||
None => return,
|
||||
};
|
||||
if entity.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let category = match SoundCategory::from_name(&sound_category) {
|
||||
Some(category) => category,
|
||||
None => return,
|
||||
};
|
||||
let entity = entity.first()?.get_entity();
|
||||
|
||||
let seed = rng().random::<i64>();
|
||||
player
|
||||
.client
|
||||
.enqueue_packet(&CEntitySoundEffect::new(
|
||||
IdOr::Id(pumpkin_sound as u16),
|
||||
category,
|
||||
entity.entity_id.into(),
|
||||
request.volume,
|
||||
request.pitch,
|
||||
seed,
|
||||
))
|
||||
.await;
|
||||
|
||||
player
|
||||
.client
|
||||
.enqueue_packet(&CEntitySoundEffect::new(
|
||||
IdOr::Id(sound as u16),
|
||||
category,
|
||||
entity.entity_id.into(),
|
||||
volume,
|
||||
pitch,
|
||||
seed,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(())
|
||||
});
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub extern "C" fn rust_player_play_sound(
|
||||
player_uuid_ptr: *const c_char,
|
||||
sound_name_ptr: *const c_char,
|
||||
sound_category_ptr: *const c_char,
|
||||
x: c_double,
|
||||
y: c_double,
|
||||
z: c_double,
|
||||
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);
|
||||
pub fn ffi_native_bridge_player_play_sound_impl(request: PlayerPlaySoundRequest) -> Option<()> {
|
||||
let ctx = CALLBACK_CONTEXT.get()?;
|
||||
let player_uuid = uuid::Uuid::parse_str(&request.player_uuid?.value).ok()?;
|
||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
|
||||
|
||||
if let Some(ctx) = CALLBACK_CONTEXT.get() {
|
||||
let player_uuid = uuid::Uuid::parse_str(&player_uuid_str).unwrap();
|
||||
let sound = request.sound?;
|
||||
let pumpkin_sound = Sound::from_name(&sound.name)?;
|
||||
let category = SoundCategory::from_name(&sound.category.to_lowercase())?;
|
||||
|
||||
ctx.runtime.spawn(async move {
|
||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid);
|
||||
let seed = match request.seed {
|
||||
Some(seed) => seed as f64,
|
||||
None => rng().random::<f64>(),
|
||||
};
|
||||
|
||||
if let Some(player) = player {
|
||||
let sound = match Sound::from_name(&sound_name) {
|
||||
Some(sound) => sound,
|
||||
None => return,
|
||||
};
|
||||
let position = request.location?.position?;
|
||||
let position: Vector3<f64> = Vector3::new(position.x, position.y, position.z);
|
||||
|
||||
let category = match SoundCategory::from_name(&sound_category.to_lowercase()) {
|
||||
Some(category) => category,
|
||||
None => return,
|
||||
};
|
||||
ctx.runtime.spawn(async move {
|
||||
player
|
||||
.play_sound(
|
||||
pumpkin_sound as u16,
|
||||
category,
|
||||
&position,
|
||||
request.volume,
|
||||
request.pitch,
|
||||
seed,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
|
||||
let seed = rng().random::<f64>();
|
||||
|
||||
let position: Vector3<f64> = Vector3::new(x, y, z);
|
||||
|
||||
player
|
||||
.play_sound(sound as u16, category, &position, volume, pitch, seed)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
use std::ffi::{CString, c_char};
|
||||
|
||||
use pumpkin::command::args::entities::{EntitySelectorType, TargetSelector};
|
||||
|
||||
use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::get_string};
|
||||
|
||||
pub extern "C" fn rust_get_world(entity_uuid_ptr: *const c_char) -> *mut c_char {
|
||||
if entity_uuid_ptr.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let entity_uuid = get_string(entity_uuid_ptr);
|
||||
|
||||
let Some(ctx) = CALLBACK_CONTEXT.get() else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
let Ok(uuid) = uuid::Uuid::parse_str(&entity_uuid) else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
let entities = ctx
|
||||
.plugin_context
|
||||
.server
|
||||
.select_entities(&TargetSelector::new(EntitySelectorType::Uuid(uuid)), None);
|
||||
|
||||
if entities.len() != 1 {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let entity = entities.first().unwrap().get_entity();
|
||||
let world = entity.world.load();
|
||||
let world_uuid = world.uuid.to_string();
|
||||
|
||||
match CString::new(world_uuid) {
|
||||
Ok(cstring) => cstring.into_raw(), // Transfers ownership to caller
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,14 @@ pub mod patchbukkit {
|
||||
pub mod message {
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.message.rs"));
|
||||
}
|
||||
|
||||
pub mod registry {
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.registry.rs"));
|
||||
}
|
||||
|
||||
pub mod sound {
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.sound.rs"));
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user