Make use of MethodHandles for paper component getting

This commit is contained in:
Vankka 2022-06-09 20:05:43 +03:00
parent 2b5c8b9d2f
commit 888f376846
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
7 changed files with 147 additions and 103 deletions

View File

@ -0,0 +1,83 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.bukkit.component;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.ComponentFactory;
import com.discordsrv.common.component.util.ComponentUtil;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
public class PaperComponentHandle<T> {
public static final boolean IS_PAPER_ADVENTURE;
private static final MethodHandles.Lookup LOOKUP;
static {
boolean isPaperAdventure = false;
try {
Class.forName("io.papermc.paper.event.player.AsyncChatEvent");
isPaperAdventure = true;
} catch (ClassNotFoundException ignored) {}
IS_PAPER_ADVENTURE = isPaperAdventure;
LOOKUP = IS_PAPER_ADVENTURE ? MethodHandles.lookup() : null;
}
private final MethodHandle handle;
private final Function<T, String> legacy;
public PaperComponentHandle(Class<T> targetClass, String methodName, Function<T, String> legacy) {
this.legacy = legacy;
MethodHandle handle = null;
if (IS_PAPER_ADVENTURE) {
try {
MethodType methodType = MethodType.methodType(ComponentFactory.UNRELOCATED_ADVENTURE_COMPONENT);
handle = LOOKUP.findVirtual(targetClass, methodName, methodType);
} catch (Throwable ignored) {}
}
this.handle = handle;
}
public MinecraftComponent getComponent(DiscordSRV discordSRV, T target) {
if (handle != null) {
Object unrelocated = null;
try {
unrelocated = handle.invoke(target);
} catch (Throwable ignored) {}
if (unrelocated != null) {
MinecraftComponent component = discordSRV.componentFactory().empty();
component.unrelocatedAdapter()
.orElseThrow(() -> new IllegalStateException("Unrelocated adventure unavailable"))
.setComponent(unrelocated);
return component;
}
}
String legacyOutput = legacy.apply(target);
return legacyOutput != null
? ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(legacyOutput))
: null;
}
}

View File

@ -1,79 +0,0 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.bukkit.component.util;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
public final class PaperComponentUtil {
public static final boolean IS_PAPER_ADVENTURE;
static {
boolean isPaperAdventure = false;
try {
Class.forName("io.papermc.paper.event.player.AsyncChatEvent");
isPaperAdventure = true;
} catch (ClassNotFoundException ignored) {}
IS_PAPER_ADVENTURE = isPaperAdventure;
}
private PaperComponentUtil() {}
public static <T> MinecraftComponent getComponent(
DiscordSRV discordSRV, T source, String methodName, Function<T, String> legacy) {
if (!IS_PAPER_ADVENTURE) {
return getLegacy(legacy.apply(source));
}
return getComponent(discordSRV, source, methodName);
}
private static MinecraftComponent getLegacy(String legacy) {
return ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(legacy));
}
public static MinecraftComponent getComponent(DiscordSRV discordSRV, Object source, String methodName) {
if (!IS_PAPER_ADVENTURE) {
return null;
}
Class<?> eventClass = source.getClass();
Object unrelocated;
try {
Method method = eventClass.getMethod(methodName);
unrelocated = method.invoke(source);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Failed to invoke method reflectively", e);
}
MinecraftComponent component = discordSRV.componentFactory().empty();
component.unrelocatedAdapter()
.orElseThrow(() -> new IllegalStateException("Unrelocated adventure unavailable"))
.setComponent(unrelocated);
return component;
}
}

View File

@ -21,7 +21,7 @@ package com.discordsrv.bukkit.listener;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.event.events.message.receive.game.GameChatMessageReceiveEvent;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
import com.discordsrv.bukkit.component.PaperComponentHandle;
import com.discordsrv.common.channel.DefaultGlobalChannel;
import com.discordsrv.common.component.util.ComponentUtil;
import io.papermc.paper.event.player.AsyncChatEvent;
@ -36,7 +36,7 @@ public abstract class BukkitChatListener implements Listener {
public static BukkitChatListener get(BukkitDiscordSRV discordSRV) {
// TODO: config option
//noinspection ConstantConditions,PointlessBooleanExpression
if (1 == 2 && PaperComponentUtil.IS_PAPER_ADVENTURE) {
if (1 == 2 && PaperComponentHandle.IS_PAPER_ADVENTURE) {
return new Paper(discordSRV);
}
@ -78,13 +78,20 @@ public abstract class BukkitChatListener implements Listener {
static class Paper extends BukkitChatListener {
private final PaperComponentHandle<AsyncChatEvent> componentHandle;
public Paper(BukkitDiscordSRV discordSRV) {
super(discordSRV);
this.componentHandle = new PaperComponentHandle<>(
AsyncChatEvent.class,
"message",
null
);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onAsyncChat(AsyncChatEvent event) {
MinecraftComponent component = PaperComponentUtil.getComponent(discordSRV, event, "message");
MinecraftComponent component = componentHandle.getComponent(discordSRV, event);
publishEvent(event.getPlayer(), component, event.isCancelled());
}
}

View File

@ -22,7 +22,7 @@ import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.event.events.message.receive.game.DeathMessageReceiveEvent;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
import com.discordsrv.bukkit.component.PaperComponentHandle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@ -31,16 +31,22 @@ import org.bukkit.event.entity.PlayerDeathEvent;
public class BukkitDeathListener implements Listener {
private final BukkitDiscordSRV discordSRV;
public BukkitDeathListener(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
private final PaperComponentHandle<PlayerDeathEvent> componentHandle;
@SuppressWarnings("deprecation") // Paper
public BukkitDeathListener(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.componentHandle = new PaperComponentHandle<>(
PlayerDeathEvent.class,
"deathMessage",
PlayerDeathEvent::getDeathMessage
);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerDeath(PlayerDeathEvent event) {
DiscordSRVPlayer player = discordSRV.playerProvider().player(event.getEntity());
MinecraftComponent component = PaperComponentUtil.getComponent(discordSRV, event, "deathMessage", PlayerDeathEvent::getDeathMessage);
MinecraftComponent component = componentHandle.getComponent(discordSRV, event);
discordSRV.scheduler().run(() -> discordSRV.eventBus().publish(
new DeathMessageReceiveEvent(player, null, component, event.isCancelled())));

View File

@ -23,7 +23,7 @@ import com.discordsrv.api.event.events.message.receive.game.JoinMessageReceiveEv
import com.discordsrv.api.event.events.message.receive.game.LeaveMessageReceiveEvent;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
import com.discordsrv.bukkit.component.PaperComponentHandle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@ -33,17 +33,28 @@ import org.bukkit.event.player.PlayerQuitEvent;
public class BukkitStatusMessageListener implements Listener {
private final BukkitDiscordSRV discordSRV;
public BukkitStatusMessageListener(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
private final PaperComponentHandle<PlayerJoinEvent> joinHandle;
private final PaperComponentHandle<PlayerQuitEvent> quitHandle;
@SuppressWarnings("deprecation") // Paper
public BukkitStatusMessageListener(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.joinHandle = new PaperComponentHandle<>(
PlayerJoinEvent.class,
"joinMessage",
PlayerJoinEvent::getJoinMessage
);
this.quitHandle = new PaperComponentHandle<>(
PlayerQuitEvent.class,
"quitMessage",
PlayerQuitEvent::getQuitMessage
);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
DiscordSRVPlayer player = discordSRV.playerProvider().player(event.getPlayer());
MinecraftComponent component = PaperComponentUtil.getComponent(
discordSRV, event, "joinMessage", PlayerJoinEvent::getJoinMessage);
MinecraftComponent component = joinHandle.getComponent(discordSRV, event);
boolean firstJoin = !event.getPlayer().hasPlayedBefore();
discordSRV.scheduler().run(() -> discordSRV.eventBus().publish(
@ -51,12 +62,10 @@ public class BukkitStatusMessageListener implements Listener {
));
}
@SuppressWarnings("deprecation") // Paper
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerQuit(PlayerQuitEvent event) {
DiscordSRVPlayer player = discordSRV.playerProvider().player(event.getPlayer());
MinecraftComponent component = PaperComponentUtil.getComponent(
discordSRV, event, "quitMessage", PlayerQuitEvent::getQuitMessage);
MinecraftComponent component = quitHandle.getComponent(discordSRV, event);
discordSRV.scheduler().run(() -> discordSRV.eventBus().publish(
new LeaveMessageReceiveEvent(player, null, component, false)

View File

@ -20,7 +20,7 @@ package com.discordsrv.bukkit.player;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.bukkit.command.game.sender.BukkitCommandSender;
import com.discordsrv.bukkit.component.util.PaperComponentUtil;
import com.discordsrv.bukkit.component.PaperComponentHandle;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.player.IPlayer;
@ -31,6 +31,17 @@ import org.jetbrains.annotations.NotNull;
public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
private static final PaperComponentHandle<Player> DISPLAY_NAME_HANDLE = makeDisplayNameHandle();
@SuppressWarnings("deprecation") // Paper
private static PaperComponentHandle<Player> makeDisplayNameHandle() {
return new PaperComponentHandle<>(
Player.class,
"displayName",
Player::getDisplayName
);
}
private final Player player;
private final Identity identity;
@ -50,12 +61,9 @@ public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
return player.getName();
}
@SuppressWarnings("deprecation") // Paper
@Override
public @NotNull Component displayName() {
return ComponentUtil.fromAPI(
PaperComponentUtil.getComponent(discordSRV, player, "displayName", Player::getDisplayName)
);
return ComponentUtil.fromAPI(DISPLAY_NAME_HANDLE.getComponent(discordSRV, player));
}
@Override

View File

@ -35,6 +35,16 @@ import java.util.Locale;
public class ComponentFactory implements MinecraftComponentFactory {
public static final Class<?> UNRELOCATED_ADVENTURE_COMPONENT;
static {
Class<?> clazz = null;
try {
clazz = Class.forName("net.kyo".concat("ri.adventure.text.Component"));
} catch (ClassNotFoundException ignored) {}
UNRELOCATED_ADVENTURE_COMPONENT = clazz;
}
private final DiscordSRV discordSRV;
private final MinecraftSerializer minecraftSerializer;
private final DiscordSerializer discordSerializer;