Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix craftbukkit entity sound facet for 1.19.3 #117

Merged
merged 3 commits into from Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -68,6 +68,7 @@ final class BukkitAudience extends FacetAudience<CommandSender> {
() -> new BukkitFacet.SoundWithCategory(),
() -> new BukkitFacet.Sound());
private static final Collection<Facet.EntitySound<Player, Object>> ENTITY_SOUND = Facet.of(
() -> new CraftBukkitFacet.EntitySound_1_19_3(),
() -> new CraftBukkitFacet.EntitySound()
);
private static final Collection<Facet.Book<Player, ?, ?>> BOOK = Facet.of(
Expand Down
Expand Up @@ -25,6 +25,7 @@

import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import java.lang.invoke.MethodHandle;
import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
Expand Down Expand Up @@ -52,8 +53,10 @@

import static net.kyori.adventure.platform.bukkit.BukkitComponentSerializer.legacy;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findClass;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findMethod;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.hasClass;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.hasMethod;
import static net.kyori.adventure.platform.facet.Knob.logError;
import static net.kyori.adventure.platform.facet.Knob.logUnsupported;

class BukkitFacet<V extends CommandSender> extends FacetBase<V> {
Expand Down Expand Up @@ -102,6 +105,7 @@ protected Position() {
static class Sound extends Position implements Facet.Sound<Player, Vector> {
private static final boolean KEY_SUPPORTED = hasClass("org.bukkit.NamespacedKey"); // Added MC 1.13
private static final boolean STOP_SUPPORTED = hasMethod(Player.class, "stopSound", String.class); // Added MC 1.9
private static final MethodHandle STOP_ALL_SUPPORTED = findMethod(Player.class, "stopAllSounds", void.class);

@Override
public void playSound(final @NotNull Player viewer, final net.kyori.adventure.sound.@NotNull Sound sound, final @NotNull Vector vector) {
Expand All @@ -115,6 +119,14 @@ public void playSound(final @NotNull Player viewer, final net.kyori.adventure.so
public void stopSound(final @NotNull Player viewer, final @NotNull SoundStop stop) {
if (STOP_SUPPORTED) {
final String name = name(stop.sound());
if (name.isEmpty() && STOP_ALL_SUPPORTED != null) {
try {
STOP_ALL_SUPPORTED.invoke(viewer);
} catch (final Throwable error) {
logError(error, "Could not invoke stopAllSounds on %s", viewer);
}
return;
}
viewer.stopSound(name);
}
}
Expand Down
Expand Up @@ -24,14 +24,18 @@
package net.kyori.adventure.platform.bukkit;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;

import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findClass;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findConstructor;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findField;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findMcClassName;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.findNmsClassName;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.lookup;
import static net.kyori.adventure.platform.bukkit.MinecraftReflection.searchMethod;
import static net.kyori.adventure.platform.facet.Knob.logError;

Expand All @@ -42,6 +46,7 @@ final class CraftBukkitAccess {
findMcClassName("network.chat.Component")
);
static final @Nullable Class<?> CLASS_REGISTRY = findClass(
findNmsClassName("IRegistry"),
findMcClassName("core.IRegistry"),
findMcClassName("core.Registry")
);
Expand All @@ -55,9 +60,21 @@ final class CraftBukkitAccess {
);
static final @Nullable Class<?> CLASS_RESOURCE_KEY = findClass(findMcClassName("resources.ResourceKey"));
static final @Nullable Class<?> CLASS_RESOURCE_LOCATION = findClass(
findNmsClassName("MinecraftKey"),
findMcClassName("resources.MinecraftKey"),
findMcClassName("resources.ResourceLocation")
);
static final @Nullable Class<?> CLASS_NMS_ENTITY = findClass(
findNmsClassName("Entity"),
findMcClassName("world.entity.Entity")
);
static final @Nullable Class<?> CLASS_BUILT_IN_REGISTRIES = findClass(findMcClassName("core.registries.BuiltInRegistries"));
static final @Nullable Class<?> CLASS_HOLDER = findClass(findMcClassName("core.Holder"));
static final @Nullable Class<?> CLASS_WRITABLE_REGISTRY = findClass(
findNmsClassName("IRegistryWritable"),
findMcClassName("core.IRegistryWritable"),
findMcClassName("core.WritableRegistry")
);

private CraftBukkitAccess() {
}
Expand Down Expand Up @@ -122,4 +139,97 @@ static boolean isSupported() {
return SERVER_LEVEL_GET_REGISTRY_ACCESS != null && REGISTRY_ACCESS_GET_REGISTRY_OPTIONAL != null && REGISTRY_GET_OPTIONAL != null && CHAT_TYPE_BOUND_NETWORK_CONSTRUCTOR != null && DISGUISED_CHAT_PACKET_CONSTRUCTOR != null && CHAT_TYPE_RESOURCE_KEY != null;
}
}

static final class EntitySound {
static final @Nullable Class<?> CLASS_CLIENTBOUND_ENTITY_SOUND = findClass(
findNmsClassName("PacketPlayOutEntitySound"),
findMcClassName("network.protocol.game.PacketPlayOutEntitySound"),
findMcClassName("network.protocol.game.ClientboundSoundEntityPacket")
);
static final @Nullable Class<?> CLASS_SOUND_SOURCE = findClass(
findNmsClassName("SoundCategory"),
findMcClassName("sounds.SoundCategory"),
findMcClassName("sounds.SoundSource")
);
static final @Nullable Class<?> CLASS_SOUND_EVENT = findClass(
findNmsClassName("SoundEffect"),
findMcClassName("sounds.SoundEffect"),
findMcClassName("sounds.SoundEvent")
);

static final @Nullable MethodHandle SOUND_SOURCE_GET_NAME;

static {
MethodHandle soundSourceGetName = null;
if (CLASS_SOUND_SOURCE != null) {
for (final Method method : CLASS_SOUND_SOURCE.getDeclaredMethods()) {
if (method.getReturnType().equals(String.class)
&& method.getParameterCount() == 0
&& !"name".equals(method.getName())
&& Modifier.isPublic(method.getModifiers())
) {
try {
soundSourceGetName = lookup().unreflect(method);
} catch (final IllegalAccessException ex) {
// ignored, getName is public
}
break;
}
}
}
SOUND_SOURCE_GET_NAME = soundSourceGetName;
}

private EntitySound() {
}

static boolean isSupported() {
return SOUND_SOURCE_GET_NAME != null;
}
}

static final class EntitySound_1_19_3 {

static final @Nullable MethodHandle NEW_RESOURCE_LOCATION = findConstructor(CLASS_RESOURCE_LOCATION, String.class, String.class);
static final @Nullable MethodHandle REGISTRY_GET_OPTIONAL = searchMethod(CLASS_REGISTRY, Modifier.PUBLIC, "getOptional", Optional.class, CLASS_RESOURCE_LOCATION);
static final @Nullable MethodHandle REGISTRY_WRAP_AS_HOLDER = searchMethod(CLASS_REGISTRY, Modifier.PUBLIC, "wrapAsHolder", CLASS_HOLDER, Object.class);
static final @Nullable MethodHandle SOUND_EVENT_CREATE_VARIABLE_RANGE = searchMethod(EntitySound.CLASS_SOUND_EVENT, Modifier.PUBLIC | Modifier.STATIC, "createVariableRangeEvent", EntitySound.CLASS_SOUND_EVENT, CLASS_RESOURCE_LOCATION);
static final @Nullable MethodHandle NEW_CLIENTBOUND_ENTITY_SOUND = findConstructor(EntitySound.CLASS_CLIENTBOUND_ENTITY_SOUND, CLASS_HOLDER, EntitySound.CLASS_SOUND_SOURCE, CLASS_NMS_ENTITY, float.class, float.class, long.class);

static final @Nullable Object SOUND_EVENT_REGISTRY;

static {
Object soundEventRegistry = null;
try {
final Field soundEventRegistryField = findField(CLASS_BUILT_IN_REGISTRIES, CLASS_REGISTRY, "SOUND_EVENT");
if (soundEventRegistryField != null) {
soundEventRegistry = soundEventRegistryField.get(null);
} else if (CLASS_BUILT_IN_REGISTRIES != null && REGISTRY_GET_OPTIONAL != null && NEW_RESOURCE_LOCATION != null) {
Object rootRegistry = null;
for (final Field field : CLASS_BUILT_IN_REGISTRIES.getDeclaredFields()) {
final int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
if ((field.getModifiers() & mask) == mask
&& field.getType().equals(CLASS_WRITABLE_REGISTRY)) {
field.setAccessible(true);
rootRegistry = field.get(null);
break;
}
}
if (rootRegistry != null) {
soundEventRegistry = ((Optional<?>) REGISTRY_GET_OPTIONAL.invoke(rootRegistry, NEW_RESOURCE_LOCATION.invoke("minecraft", "sound_event"))).orElse(null);
}
}
} catch (final Throwable error) {
logError(error, "Failed to initialize EntitySound_1_19_3 CraftBukkit facet");
}
SOUND_EVENT_REGISTRY = soundEventRegistry;
}

private EntitySound_1_19_3() {
}

static boolean isSupported() {
return NEW_CLIENTBOUND_ENTITY_SOUND != null && SOUND_EVENT_REGISTRY != null && NEW_RESOURCE_LOCATION != null && REGISTRY_GET_OPTIONAL != null && REGISTRY_WRAP_AS_HOLDER != null && SOUND_EVENT_CREATE_VARIABLE_RANGE != null;
}
}
}