Park these files for now if I ever want to go this route...

Signed-off-by: Irmo van den Berge <irmo.vandenberge@mailbox.org>
This commit is contained in:
Irmo van den Berge committed 2025-10-14 01:00:23 +02:00
1 parent 4f5752a20c
commit f1379401f7
8 files changed
+1445

No files matched your search

@@ -13,11 +13,17 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.bergerkiller.bukkit.common.cloud.audiences.PaperAdventureAdapter;
import com.bergerkiller.bukkit.common.cloud.captions.BKCommonLibCaptionRegistry;
import com.bergerkiller.bukkit.common.cloud.parsers.SoundEffectParser;
import com.bergerkiller.bukkit.common.internal.CommonBootstrap;
import com.bergerkiller.bukkit.common.resources.ResourceKey;
import com.bergerkiller.bukkit.common.resources.SoundEffect;
import com.bergerkiller.mountiplex.reflection.declarations.Template;
import net.kyori.adventure.chat.ChatType;
import net.kyori.adventure.chat.SignedMessage;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
@@ -224,6 +230,34 @@ public class CloudSimpleHandler {
// Used by the help system
this.bukkitAudiences = BukkitAudiences.create(plugin);
/*
// Testing!
PaperAdventureAdapter adapter = Template.Class.create(PaperAdventureAdapter.class, Common.TEMPLATE_RESOLVER);
adapter.forceInitialization();
{
Object o = adapter.adaptKey(Key.key("test", "world"));
System.out.println("RESULT: " + o);
Key key = adapter.reverseKey(o);
System.out.println("RESULT REV: " + key);
}
{
Object o = adapter.adaptChatType(ChatType.SAY_COMMAND);
System.out.println("RESULT: " + o);
ChatType type = adapter.reverseChatType(o);
System.out.println("RESULT REV: " + type);
}
{
Object o = adapter.adaptSignedMessage(SignedMessage.system("cool", Component.text("test")));
System.out.println("RESULT: " + o);
SignedMessage type = adapter.reverseSignedMessage(o);
System.out.println("RESULT REV: " + type + " " + type.message() + " " + type.unsignedContent());
}
*/
}
private void handleException(CommandSender sender, Throwable exception) throws Throwable {
@@ -0,0 +1,202 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
import com.bergerkiller.mountiplex.reflection.ReflectionUtil;
import com.bergerkiller.mountiplex.reflection.util.ExtendedClassWriter;
import com.bergerkiller.mountiplex.reflection.util.FastConstructor;
import com.bergerkiller.mountiplex.reflection.util.UniqueHash;
import com.bergerkiller.mountiplex.reflection.util.asm.MPLType;
import net.kyori.adventure.audience.Audience;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.objectweb.asm.Opcodes.*;
/**
* Generates an implementation type of Audience that calls all the methods on
* another (adapted) Audience implementation. All parameters for these function
* calls are adapted using {@link PaperAdventureAdapter}.
*/
final class AudienceAdapterImpl implements PaperAdventureAdapter.AudienceAdapter {
private static final UniqueHash generatedClassCtr = new UniqueHash();
private final PaperAdventureAdapter adapter;
private final FastConstructor<Audience> ctor;
public AudienceAdapterImpl(PaperAdventureAdapter adapter) {
this.adapter = adapter;
final Class<?> adaptedAudienceType = adapter.getAdventureClass(
/* String join to avoid the gradle remapper changing it */
String.join(".", "net", "kyori", "adventure", "audience", "Audience"));
ExtendedClassWriter<? extends Audience> cw = ExtendedClassWriter.builder(Audience.class)
.setFlags(ClassWriter.COMPUTE_MAXS)
.setAccess(ACC_FINAL)
.setExactName(AudienceAdapterImpl.class.getName() + "$AudienceImpl" + generatedClassCtr.nextHex())
.build();
FieldVisitor fieldVisitor;
MethodVisitor methodVisitor;
// Add field storing the PaperAdventureAdapter
{
fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, "adapter",
MPLType.getDescriptor(PaperAdventureAdapter.class), null, null);
fieldVisitor.visitEnd();
}
// Add field storing the adapted original Audience
{
fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, "aud",
MPLType.getDescriptor(adaptedAudienceType), null, null);
fieldVisitor.visitEnd();
}
// Add constructor assigning both fields
{
String ctorDesc = "(" + MPLType.getDescriptor(PaperAdventureAdapter.class) + MPLType.getDescriptor(adaptedAudienceType) + ")V";
methodVisitor = cw.visitMethod(ACC_PUBLIC, "<init>", ctorDesc, null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), "adapter", MPLType.getDescriptor(PaperAdventureAdapter.class));
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 2);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), "aud", MPLType.getDescriptor(adaptedAudienceType));
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
// Implement all methods that feature parameter types that we can adapt
// If we cannot, leave the default implementation unless it is abstract, then throw a NotSupported exception
List<Method> methodsToImplement = ReflectionUtil.getAllMethods(Audience.class)
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.collect(Collectors.toList());
for (Method method : methodsToImplement) {
// Go by all method parameters and the return type and verify that we know how to adapt it
boolean canAdapt = true;
List<PaperAdventureAdapter.TypeAdapter> adaptedParamTypes = new ArrayList<>();
PaperAdventureAdapter.TypeAdapter adaptedReturnType = null;
for (Class<?> type : method.getParameterTypes()) {
PaperAdventureAdapter.TypeAdapter paramTypeAdapter = adapter.getTypeAdapter(type);
if (paramTypeAdapter == null) {
canAdapt = false;
break;
} else {
adaptedParamTypes.add(paramTypeAdapter);
}
}
if (canAdapt) {
adaptedReturnType = adapter.getTypeAdapter(method.getReturnType());
if (adaptedReturnType == null) {
canAdapt = false;
}
}
// Find the exact same method name on the other audience implementation, with the same (adapted) args
Method adaptedMethod = null;
if (canAdapt) {
try {
Class<?>[] params = adaptedParamTypes.stream()
.map(PaperAdventureAdapter.TypeAdapter::getAdaptedType)
.toArray(Class<?>[]::new);
adaptedMethod = adaptedAudienceType.getMethod(method.getName(), params);
if (!adaptedMethod.getReturnType().equals(adaptedReturnType.getAdaptedType())) {
canAdapt = false;
}
} catch (NoSuchMethodException e) {
canAdapt = false;
}
}
// Fail if we can't implement it
if (!canAdapt) {
System.out.println("Cannot implement: " + method);
if (Modifier.isAbstract(method.getModifiers())) {
cw.visitMethodUnsupported(method, "Not implemented by this Paper Audience adapter");
}
continue;
}
System.out.println("Implementing: " + method);
// Implement the method
// Start by reading the adapter and aud field
methodVisitor = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, MPLType.getName(method), MPLType.getMethodDescriptor(method), null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, cw.getInternalName(), "adapter", MPLType.getDescriptor(PaperAdventureAdapter.class));
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, cw.getInternalName(), "aud", MPLType.getDescriptor(adaptedAudienceType));
// Next, go by all parameters and process them with the parameter type mapping we got
int register = 1;
for (PaperAdventureAdapter.TypeAdapter parameterType : adaptedParamTypes) {
register = parameterType.applyParameter(cw, methodVisitor, register);
}
// Call the same method on the adapted audience
methodVisitor.visitMethodInsn(INVOKEINTERFACE, MPLType.getInternalName(adaptedAudienceType),
method.getName(), MPLType.getMethodDescriptor(adaptedMethod), true);
// Adapt the return type as well (could emit no instructions if void)
{
adaptedReturnType.applyReturn(cw, methodVisitor);
}
// Return the value and done
methodVisitor.visitInsn(MPLType.getOpcode(method.getReturnType(), IRETURN));
methodVisitor.visitMaxs(6, 5);
methodVisitor.visitEnd();
};
// public MyReturnType method(long l, MyParamType type, int k)
/*
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "method", "(JLcom/bergerkiller/mountiplex/ASMPlaygroundTest$MyParamType;I)Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$MyReturnType;", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/bergerkiller/mountiplex/ASMPlaygroundTest$AdaptedAudience", "adapter", "Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$Adapter;");
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/bergerkiller/mountiplex/ASMPlaygroundTest$AdaptedAudience", "aud", "Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$OtherAudience;");
methodVisitor.visitVarInsn(LLOAD, 1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/bergerkiller/mountiplex/ASMPlaygroundTest$AdaptedAudience", "adapter", "Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$Adapter;");
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/bergerkiller/mountiplex/ASMPlaygroundTest$Adapter", "adaptParamType", "(Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$MyParamType;)Ljava/lang/Object;", false);
methodVisitor.visitTypeInsn(CHECKCAST, "com/bergerkiller/mountiplex/ASMPlaygroundTest$OtherMyParamType");
methodVisitor.visitVarInsn(ILOAD, 4);
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "com/bergerkiller/mountiplex/ASMPlaygroundTest$OtherAudience", "method", "(JLcom/bergerkiller/mountiplex/ASMPlaygroundTest$OtherMyParamType;I)Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$OtherMyReturnType;", true);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/bergerkiller/mountiplex/ASMPlaygroundTest$Adapter", "reverseReturnType", "(Ljava/lang/Object;)Lcom/bergerkiller/mountiplex/ASMPlaygroundTest$MyReturnType;", false);
methodVisitor.visitInsn(ARETURN);
methodVisitor.visitMaxs(6, 5);
methodVisitor.visitEnd();
*/
// Generate and done
this.ctor = new FastConstructor<>(cw.generateConstructor(PaperAdventureAdapter.class, adaptedAudienceType));
}
@Override
public Audience reverseAudience(Object audience) {
return this.ctor.newInstance(this.adapter, audience);
}
}
@@ -0,0 +1,186 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
import com.bergerkiller.mountiplex.reflection.ReflectionUtil;
import com.bergerkiller.mountiplex.reflection.util.ExtendedClassWriter;
import com.bergerkiller.mountiplex.reflection.util.FastConstructor;
import com.bergerkiller.mountiplex.reflection.util.UniqueHash;
import com.bergerkiller.mountiplex.reflection.util.asm.MPLType;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.AudienceProvider;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
/**
* Generates an implementation type of AudienceProvider that calls all the methods on
* another (adapted) AudienceProvider implementation. All parameters for these function
* calls are adapted using {@link PaperAdventureAdapter}.
*/
final class AudienceProviderAdapterImpl implements PaperAdventureAdapter.AudienceProviderAdapter {
private static final UniqueHash generatedClassCtr = new UniqueHash();
private final PaperAdventureAdapter adapter;
private final FastConstructor<AudienceProvider> ctor;
public AudienceProviderAdapterImpl(PaperAdventureAdapter adapter) {
this.adapter = adapter;
final Class<?> adaptedAudienceProviderType = adapter.getAdventureClass(
/* String join to avoid the gradle remapper changing it */
String.join(".", "net", "kyori", "adventure", "platform", "AudienceProvider"));
ExtendedClassWriter<? extends AudienceProvider> cw = ExtendedClassWriter.builder(AudienceProvider.class)
.setFlags(ClassWriter.COMPUTE_MAXS)
.setAccess(ACC_FINAL)
.setExactName(AudienceProviderAdapterImpl.class.getName() + "$AudienceProviderImpl" + generatedClassCtr.nextHex())
.build();
FieldVisitor fieldVisitor;
MethodVisitor methodVisitor;
// Add field storing the PaperAdventureAdapter
{
fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, "adapter",
MPLType.getDescriptor(PaperAdventureAdapter.class), null, null);
fieldVisitor.visitEnd();
}
// Add field storing the adapted original Audience
{
fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, "aud",
MPLType.getDescriptor(adaptedAudienceProviderType), null, null);
fieldVisitor.visitEnd();
}
// Add constructor assigning both fields
{
String ctorDesc = "(" + MPLType.getDescriptor(PaperAdventureAdapter.class) + MPLType.getDescriptor(adaptedAudienceProviderType) + ")V";
methodVisitor = cw.visitMethod(ACC_PUBLIC, "<init>", ctorDesc, null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), "adapter", MPLType.getDescriptor(PaperAdventureAdapter.class));
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 2);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), "aud", MPLType.getDescriptor(adaptedAudienceProviderType));
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
// Implement all methods that feature parameter types that we can adapt
// If we cannot, leave the default implementation unless it is abstract, then throw a NotSupported exception
List<Method> methodsToImplement = ReflectionUtil.getAllMethods(Audience.class)
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.collect(Collectors.toList());
for (Method method : methodsToImplement) {
// Go by all method parameters and the return type and verify that we know how to adapt it
boolean canAdapt = true;
List<PaperAdventureAdapter.TypeAdapter> adaptedParamTypes = new ArrayList<>();
PaperAdventureAdapter.TypeAdapter adaptedReturnType = null;
for (Class<?> type : method.getParameterTypes()) {
PaperAdventureAdapter.TypeAdapter paramTypeAdapter = adapter.getTypeAdapter(type);
if (paramTypeAdapter == null) {
canAdapt = false;
break;
} else {
adaptedParamTypes.add(paramTypeAdapter);
}
}
if (canAdapt) {
adaptedReturnType = adapter.getTypeAdapter(method.getReturnType());
if (adaptedReturnType == null) {
canAdapt = false;
}
}
// Find the exact same method name on the other audience implementation, with the same (adapted) args
Method adaptedMethod = null;
if (canAdapt) {
try {
Class<?>[] params = adaptedParamTypes.stream()
.map(PaperAdventureAdapter.TypeAdapter::getAdaptedType)
.toArray(Class<?>[]::new);
adaptedMethod = adaptedAudienceProviderType.getMethod(method.getName(), params);
if (!adaptedMethod.getReturnType().equals(adaptedReturnType.getAdaptedType())) {
canAdapt = false;
}
} catch (NoSuchMethodException e) {
canAdapt = false;
}
}
// Fail if we can't implement it
if (!canAdapt) {
System.out.println("Cannot implement: " + method);
if (Modifier.isAbstract(method.getModifiers())) {
cw.visitMethodUnsupported(method, "Not implemented by this Paper Audience adapter");
}
continue;
}
System.out.println("Implementing: " + method);
// Implement the method
// Start by reading the adapter and aud field
methodVisitor = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, MPLType.getName(method), MPLType.getMethodDescriptor(method), null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, cw.getInternalName(), "adapter", MPLType.getDescriptor(PaperAdventureAdapter.class));
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, cw.getInternalName(), "aud", MPLType.getDescriptor(adaptedAudienceProviderType));
// Next, go by all parameters and process them with the parameter type mapping we got
int register = 1;
for (PaperAdventureAdapter.TypeAdapter parameterType : adaptedParamTypes) {
register = parameterType.applyParameter(cw, methodVisitor, register);
}
// Call the same method on the adapted audience
methodVisitor.visitMethodInsn(INVOKEINTERFACE, MPLType.getInternalName(adaptedAudienceProviderType),
method.getName(), MPLType.getMethodDescriptor(adaptedMethod), true);
// Adapt the return type as well (could emit no instructions if void)
{
adaptedReturnType.applyReturn(cw, methodVisitor);
}
// Return the value and done
methodVisitor.visitInsn(MPLType.getOpcode(method.getReturnType(), IRETURN));
methodVisitor.visitMaxs(6, 5);
methodVisitor.visitEnd();
};
// Generate and done
this.ctor = new FastConstructor<>(cw.generateConstructor(PaperAdventureAdapter.class, adaptedAudienceProviderType));
}
@Override
public AudienceProvider reverseAudienceProvider(Object audienceProvider) {
return this.ctor.newInstance(this.adapter, audienceProvider);
}
}
@@ -0,0 +1,51 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
/**
* Type Adapter. These implementations exist:
* <ul>
* <li>net.kyori.adventure.key.Key</li>
* <li>net.kyori.adventure.text.ComponentLike</li>
* <li>net.kyori.adventure.text.Component</li>
* <li>net.kyori.adventure.audience.MessageType</li>
* <li>net.kyori.adventure.identity.Identified</li>
* <li>net.kyori.adventure.identity.Identity</li>
* <li>net.kyori.adventure.chat.ChatType.Bound</li>
* <li>net.kyori.adventure.chat.SignedMessage</li>
* <li>net.kyori.adventure.chat.SignedMessage.Signature</li>
* <li>net.kyori.adventure.title.Title</li>
* <li>net.kyori.adventure.title.TitlePart</li>
* </ul>
*
* These are not implemented and these functionalities will not work as a result:
* <ul>
* <li>net.kyori.adventure.bossbar.BossBar</li>
* <li>net.kyori.adventure.sound.Sound</li>
* <li>net.kyori.adventure.sound.SoundStop</li>
* <li>net.kyori.adventure.sound.Sound.Emitter</li>
* <li>net.kyori.adventure.inventory.Book.Builder</li>
* <li>net.kyori.adventure.inventory.Book</li>
* <li>net.kyori.adventure.resource.ResourcePackInfoLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequestLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequest</li>
* <li>net.kyori.adventure.dialog.DialogLike</li>
* </ul>
*
* @param <A> Type we know
* @param <B> Unknown type
*/
public interface AudienceTypeAdapter<A, B> {
/**
* Adapts the type we know into the unknown type
*
* @param value Known type
* @return Unknown type
*/
B adapt(A value);
/**
* Restores the unknown type back into a type we know (reverse)
*
* @param value Unknown type
* @return Known type
*/
A restore(B value);
}
@@ -0,0 +1,691 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
import com.bergerkiller.mountiplex.reflection.declarations.Template;
import com.bergerkiller.mountiplex.reflection.util.BoxedType;
import com.bergerkiller.mountiplex.reflection.util.ExtendedClassWriter;
import com.bergerkiller.mountiplex.reflection.util.asm.MPLType;
import org.objectweb.asm.MethodVisitor;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
/**
* Converts types from Adventure between the BKCommonLib shaded types and the
* types declared by Paper in the server.<br>
* <br>
* These Adventure types can be adapted:
* <ul>
* <li>net.kyori.adventure.key.audience.Audience</li>
* <li>net.kyori.adventure.key.Key</li>
* <li>net.kyori.adventure.text.Component</li>
* <li>net.kyori.adventure.audience.MessageType</li>
* <li>net.kyori.adventure.identity.Identity</li>
* <li>net.kyori.adventure.chat.ChatType.Bound</li>
* <li>net.kyori.adventure.chat.SignedMessage</li>
* <li>net.kyori.adventure.title.TitlePart</li>
* </ul>
*
* These are not implemented and these functionalities will not work as a result:
* <ul>
* <li>net.kyori.adventure.bossbar.BossBar</li>
* <li>net.kyori.adventure.sound.Sound</li>
* <li>net.kyori.adventure.sound.SoundStop</li>
* <li>net.kyori.adventure.sound.Sound.Emitter</li>
* <li>net.kyori.adventure.inventory.Book.Builder</li>
* <li>net.kyori.adventure.inventory.Book</li>
* <li>net.kyori.adventure.resource.ResourcePackInfoLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequestLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequest</li>
* <li>net.kyori.adventure.dialog.DialogLike</li>
* </ul>
*/
@Template.Import("com.bergerkiller.bukkit.common.cloud.audiences.PaperAdventureAdapter")
@Template.InstanceType("net.kyori.adventure.audience.Audience")
public abstract class PaperAdventureAdapter extends Template.Class<Template.Handle> {
private final Function<String, Class<?>> adventureClassLoader = getAdventureClassLoader();
private final Map<Class<?>, TypeAdapter> typeAdapters = new HashMap<>();
public final SignedMessageFactory<Object> adaptSignedMessageFactory =
new SignedMessageFactoryImpl<>(getAdventureClass(
/* String join to avoid the gradle remapper changing it */
String.join(".", "net", "kyori", "adventure", "chat", "SignedMessage")));
public final SignedMessageFactory<net.kyori.adventure.chat.SignedMessage> reverseSignedMessageFactory =
new SignedMessageFactoryImpl<>(net.kyori.adventure.chat.SignedMessage.class);
public final AudienceAdapter adaptAudience = new AudienceAdapterImpl(this);
public final AudienceProviderAdapter adaptAudienceProvider = new AudienceProviderAdapterImpl(this);
public PaperAdventureAdapter() {
// Register all the pass-through (no adapting) types that can be safely used
// When these types are encountered, it is assumed the adapted method will use the same type
BoxedType.getUnboxedTypes().forEach(this::registerPassThroughAdapter);
BoxedType.getBoxedTypes().forEach(this::registerPassThroughAdapter);
registerPassThroughAdapter(UUID.class);
registerPassThroughAdapter(String.class);
registerPassThroughAdapter(Predicate.class);
registerPassThroughAdapter(Object.class);
// Register all the type adapters that this class supports
registerStandardAdapter(
net.kyori.adventure.key.Key.class,
String.join(".", "net", "kyori", "adventure", "key", "Key"),
"adaptKey", "reverseKey");
registerStandardAdapter(
net.kyori.adventure.identity.Identity.class,
String.join(".", "net", "kyori", "adventure", "identity", "Identity"),
"adaptIdentity", "reverseIdentity");
registerStandardAdapter(
net.kyori.adventure.text.Component.class,
String.join(".", "net", "kyori", "adventure", "text", "Component"),
"adaptComponent", "reverseComponent");
registerStandardAdapter(
net.kyori.adventure.audience.Audience.class,
String.join(".", "net", "kyori", "adventure", "audience", "Audience"),
"adaptAudience", "reverseAudience");
registerStandardAdapter(
net.kyori.adventure.platform.AudienceProvider.class,
String.join(".", "net", "kyori", "adventure", "platform", "AudienceProvider"),
"adaptAudienceProvider", "reverseAudienceProvider");
}
/**
* Retrieves a (foreign) Adventure library class by name
*
* @param name Name of the class
* @return Class
* @throws UnsupportedOperationException If this type does not exist (somehow?)
*/
public final Class<?> getAdventureClass(String name) {
return adventureClassLoader.apply(name);
}
/**
* Gets type adapter information for an API Type.
* Returns null if the type is unknown and not supported for adapting.
*
* @param type API Type
* @return TypeAdapter
*/
public final TypeAdapter getTypeAdapter(Class<?> type) {
return typeAdapters.get(type);
}
/*
* <ADAPT_KEY>
* public static net.kyori.adventure.key.Key adaptKey(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key key) {
* return net.kyori.adventure.key.Key.key(key.namespace(), key.value());
* }
*/
@Template.Generated("%ADAPT_KEY%")
public abstract Object adaptKey(net.kyori.adventure.key.Key key);
/*
* <REVERSE_KEY>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key reverseKey(net.kyori.adventure.key.Key key) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key.key(key.namespace(), key.value());
* }
*/
@Template.Generated("%REVERSE_KEY%")
public abstract net.kyori.adventure.key.Key reverseKey(Object key);
/*
* <ADAPT_MESSAGE_TYPE>
* public static net.kyori.adventure.audience.MessageType adaptMessageType(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.audience.MessageType messageType) {
* if (messageType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.audience.MessageType.SYSTEM) {
* return net.kyori.adventure.audience.MessageType.SYSTEM;
* } else {
* return net.kyori.adventure.audience.MessageType.CHAT;
* }
* }
*/
@Template.Generated("%ADAPT_MESSAGE_TYPE%")
public abstract Object adaptMessageType(net.kyori.adventure.key.Key messageType);
/*
* <REVERSE_MESSAGE_TYPE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.audience.MessageType reverseMessageType(net.kyori.adventure.audience.MessageType messageType) {
* if (messageType == net.kyori.adventure.audience.MessageType.SYSTEM) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.audience.MessageType.SYSTEM;
* } else {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.audience.MessageType.CHAT;
* }
* }
*/
@Template.Generated("%REVERSE_MESSAGE_TYPE%")
public abstract net.kyori.adventure.key.Key reverseMessageType(Object messageType);
/*
* <ADAPT_IDENTITY>
* public static net.kyori.adventure.identity.Identity adaptIdentity(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity identity) {
* if (identity == null) {
* return null;
* } else if (identity == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity.nil()) {
* return net.kyori.adventure.identity.Identity.nil();
* } else {
* return net.kyori.adventure.identity.Identity.identity(identity.uuid());
* }
* }
*/
@Template.Generated("%ADAPT_IDENTITY%")
public abstract Object adaptIdentity(net.kyori.adventure.identity.Identity identity);
/*
* <REVERSE_IDENTITY>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity reverseIdentity(net.kyori.adventure.identity.Identity identity) {
* if (identity == null) {
* return null;
* } else if (identity == net.kyori.adventure.identity.Identity.nil()) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity.nil();
* } else {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity.identity(identity.uuid());
* }
* }
*/
@Template.Generated("%REVERSE_IDENTITY%")
public abstract net.kyori.adventure.identity.Identity reverseIdentity(Object identity);
/*
* <ADAPT_COMPONENT>
* public static net.kyori.adventure.text.Component adaptComponent(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component component) {
* if (component == null)
* return null;
* if (component == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component.empty())
* return net.kyori.adventure.text.Component.empty();
*
* String json = (String) com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serialize(component);
*
* return (net.kyori.adventure.text.Component)
* net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().deserialize(json);
* }
*/
@Template.Generated("%ADAPT_COMPONENT%")
public abstract Object adaptComponent(net.kyori.adventure.text.Component component);
/*
* <REVERSE_COMPONENT>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component reverseComponent(net.kyori.adventure.text.Component component) {
* if (component == null)
* return null;
* if (component == net.kyori.adventure.text.Component.empty())
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component.empty();
*
* String json = (String) net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serialize(component);
*
* return (com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component)
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().deserialize(json);
* }
*/
@Template.Generated("%REVERSE_COMPONENT%")
public abstract net.kyori.adventure.text.Component reverseComponent(Object component);
/*
* <ADAPT_CHAT_TYPE>
* public static net.kyori.adventure.chat.ChatType adaptChatType(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType chatType) {
* #if version >= 1.19
* if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.CHAT) {
* return net.kyori.adventure.chat.ChatType.CHAT;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.SAY_COMMAND) {
* return net.kyori.adventure.chat.ChatType.SAY_COMMAND;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.MSG_COMMAND_INCOMING) {
* return net.kyori.adventure.chat.ChatType.MSG_COMMAND_INCOMING;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.MSG_COMMAND_OUTGOING) {
* return net.kyori.adventure.chat.ChatType.MSG_COMMAND_OUTGOING;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_INCOMING) {
* return net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_INCOMING;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_OUTGOING) {
* return net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_OUTGOING;
* } else if (chatType == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.EMOTE_COMMAND) {
* return net.kyori.adventure.chat.ChatType.EMOTE_COMMAND;
* } else {
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key key = chatType.key();
* net.kyori.adventure.key.Key adaptedKey =
* net.kyori.adventure.key.Key.key(key.namespace(), key.value());
* return net.kyori.adventure.chat.ChatType.chatType(adaptedKey);
* }
* #else
* return null;
* #endif
* }
*/
@Template.Generated("%ADAPT_CHAT_TYPE%")
public abstract Object adaptChatType(net.kyori.adventure.chat.ChatType chatType);
/*
* <REVERSE_CHAT_TYPE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType reverseChatType(net.kyori.adventure.chat.ChatType chatType) {
* #if version >= 1.19
* if (chatType == net.kyori.adventure.chat.ChatType.CHAT) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.CHAT;
* } else if (chatType == net.kyori.adventure.chat.ChatType.SAY_COMMAND) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.SAY_COMMAND;
* } else if (chatType == net.kyori.adventure.chat.ChatType.MSG_COMMAND_INCOMING) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.MSG_COMMAND_INCOMING;
* } else if (chatType == net.kyori.adventure.chat.ChatType.MSG_COMMAND_OUTGOING) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.MSG_COMMAND_OUTGOING;
* } else if (chatType == net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_INCOMING) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_INCOMING;
* } else if (chatType == net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_OUTGOING) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.TEAM_MSG_COMMAND_OUTGOING;
* } else if (chatType == net.kyori.adventure.chat.ChatType.EMOTE_COMMAND) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.EMOTE_COMMAND;
* } else {
* net.kyori.adventure.key.Key key = chatType.key();
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key reversedKey =
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.key.Key.key(key.namespace(), key.value());
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.chatType(reversedKey);
* }
* #else
* return null;
* #endif
* }
*/
@Template.Generated("%REVERSE_CHAT_TYPE%")
public abstract net.kyori.adventure.chat.ChatType reverseChatType(Object chatType);
/*
* <ADAPT_BOUND_CHAT_TYPE>
* public static net.kyori.adventure.chat.ChatType.Bound reverseChatType(PaperAdventureAdapter adapter, com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.Bound boundChatType) {
* #if version >= 1.19
* net.kyori.adventure.chat.ChatType type = (net.kyori.adventure.chat.ChatType) adapter.adaptChatType(boundChatType.type());
* net.kyori.adventure.text.Component name = (net.kyori.adventure.text.Component) adapter.adaptComponent(boundChatType.name());
* net.kyori.adventure.text.Component target = (net.kyori.adventure.text.Component) adapter.adaptComponent(boundChatType.target());
* return type.bind(name, target);
* #else
* return null;
* #endif
* }
*/
@Template.Generated("%ADAPT_BOUND_CHAT_TYPE%")
public abstract Object adaptBoundChatType(PaperAdventureAdapter adapter, net.kyori.adventure.chat.ChatType.Bound boundChatType);
public final Object adaptBoundChatType(net.kyori.adventure.chat.ChatType.Bound boundChatType) {
return adaptBoundChatType(this, boundChatType);
}
/*
* <REVERSE_BOUND_CHAT_TYPE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType.Bound reverseChatType(PaperAdventureAdapter adapter, net.kyori.adventure.chat.ChatType.Bound boundChatType) {
* #if version >= 1.19
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.ChatType type = adapter.reverseChatType(boundChatType.type());
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component name = adapter.reverseComponent(boundChatType.name());
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component target = adapter.reverseComponent(boundChatType.target());
* return type.bind(name, target);
* #else
* return null;
* #endif
* }
*/
@Template.Generated("%REVERSE_BOUND_CHAT_TYPE%")
public abstract net.kyori.adventure.chat.ChatType.Bound reverseBoundChatType(PaperAdventureAdapter adapter, Object boundChatType);
public final net.kyori.adventure.chat.ChatType.Bound reverseBoundChatType(Object boundChatType) {
return reverseBoundChatType(this, boundChatType);
}
/*
* <ADAPT_SIGNED_MESSAGE_SIGNATURE>
* public static net.kyori.adventure.chat.SignedMessage.Signature adaptSignedMessageSignature(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage.Signature signature) {
* if (signature == null)
* return null;
* return net.kyori.adventure.chat.SignedMessage.signature(signature.bytes());
* }
*/
@Template.Generated("%ADAPT_SIGNED_MESSAGE_SIGNATURE%")
public abstract Object adaptSignedMessageSignature(net.kyori.adventure.chat.SignedMessage.Signature signature);
/*
* <REVERSE_SIGNED_MESSAGE_SIGNATURE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage.Signature reverseSignedMessageSignature(net.kyori.adventure.chat.SignedMessage.Signature signature) {
* if (signature == null)
* return null;
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage.signature(signature.bytes());
* }
*/
@Template.Generated("%REVERSE_SIGNED_MESSAGE_SIGNATURE%")
public abstract net.kyori.adventure.chat.SignedMessage.Signature reverseSignedMessageSignature(Object signature);
/*
* <ADAPT_SIGNED_MESSAGE>
* public static net.kyori.adventure.chat.SignedMessage adaptSignedMessage(PaperAdventureAdapter adapter, com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage signedMessage) {
* if (signedMessage == null)
* return null;
*
* java.time.Instant timestamp = signedMessage.timestamp();
* long salt = signedMessage.salt();
* String message = signedMessage.message();
*
* net.kyori.adventure.chat.SignedMessage$Signature signature =
* (net.kyori.adventure.chat.SignedMessage$Signature) adapter.adaptSignedMessageSignature(signedMessage.signature());
* net.kyori.adventure.text.Component unsignedContent =
* (net.kyori.adventure.text.Component) adapter.adaptComponent(signedMessage.unsignedContent());
* net.kyori.adventure.identity.Identity identity =
* (net.kyori.adventure.identity.Identity) adapter.adaptIdentity(signedMessage.identity());
*
* return (net.kyori.adventure.chat.SignedMessage) adapter.adaptSignedMessageFactory.create(
* timestamp, salt, message, signature, unsignedContent, identity );
* }
*/
@Template.Generated("%ADAPT_SIGNED_MESSAGE%")
public abstract Object adaptSignedMessage(PaperAdventureAdapter adapter, net.kyori.adventure.chat.SignedMessage signedMessage);
public final Object adaptSignedMessage(net.kyori.adventure.chat.SignedMessage signedMessage) {
return adaptSignedMessage(this, signedMessage);
}
/*
* <REVERSE_SIGNED_MESSAGE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage reverseSignedMessage(PaperAdventureAdapter adapter, net.kyori.adventure.chat.SignedMessage signedMessage) {
* if (signedMessage == null)
* return null;
*
* java.time.Instant timestamp = signedMessage.timestamp();
* long salt = signedMessage.salt();
* String message = signedMessage.message();
*
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage$Signature signature =
* adapter.reverseSignedMessageSignature(signedMessage.signature());
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component unsignedContent =
* adapter.reverseComponent(signedMessage.unsignedContent());
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.identity.Identity identity =
* adapter.reverseIdentity(signedMessage.identity());
*
* return (com.bergerkiller.bukkit.common.dep.net.kyori.adventure.chat.SignedMessage) adapter.reverseSignedMessageFactory.create(
* timestamp, salt, message, signature, unsignedContent, identity );
* }
*/
@Template.Generated("%REVERSE_SIGNED_MESSAGE%")
public abstract net.kyori.adventure.chat.SignedMessage reverseSignedMessage(PaperAdventureAdapter adapter, Object signedMessage);
public final net.kyori.adventure.chat.SignedMessage reverseSignedMessage(Object signedMessage) {
return reverseSignedMessage(this, signedMessage);
}
/*
* <ADAPT_TITLE>
* public static net.kyori.adventure.title.Title adaptTitle(PaperAdventureAdapter adapter, com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title title) {
* if (title == null)
* return null;
*
* // Adapt times
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title$Times origTimes = title.times();
* net.kyori.adventure.title.Title$Times times;
* if (origTimes == null) {
* times = null;
* } else if (origTimes == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title.DEFAULT_TIMES) {
* times = net.kyori.adventure.title.Title.DEFAULT_TIMES;
* } else {
* times = net.kyori.adventure.title.Title$Times.times(
* origTimes.fadeIn(), origTimes.stay(), origTimes.fadeOut());
* }
*
* // Adapt the other elements
* net.kyori.adventure.text.Component titleText =
* (net.kyori.adventure.text.Component) adapter.adaptComponent(title.title());
* net.kyori.adventure.text.Component subTitleText =
* (net.kyori.adventure.text.Component) adapter.adaptComponent(title.subtitle());
*
* // Build
* return net.kyori.adventure.title.Title.title(titleText, subTitleText, times);
* }
*/
@Template.Generated("%ADAPT_TITLE%")
public abstract Object adaptTitle(PaperAdventureAdapter adapter, net.kyori.adventure.title.Title title);
public final Object adaptTitle(net.kyori.adventure.title.Title title) {
return adaptTitle(this, title);
}
/*
* <REVERSE_TITLE>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title reverseTitle(PaperAdventureAdapter adapter, net.kyori.adventure.title.Title title) {
* if (title == null)
* return null;
*
* // Reverse times
* net.kyori.adventure.title.Title$Times origTimes = title.times();
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title$Times times;
* if (origTimes == null) {
* times = null;
* } else if (origTimes == net.kyori.adventure.title.Title.DEFAULT_TIMES) {
* times = com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title.DEFAULT_TIMES;
* } else {
* times = com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title$Times.times(
* origTimes.fadeIn(), origTimes.stay(), origTimes.fadeOut());
* }
*
* // Reverse the other elements
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component titleText =
* adapter.reverseComponent(title.title());
* com.bergerkiller.bukkit.common.dep.net.kyori.adventure.text.Component subTitleText =
* adapter.reverseComponent(title.subtitle());
*
* // Build
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.Title.title(titleText, subTitleText, times);
* }
*/
@Template.Generated("%REVERSE_TITLE%")
public abstract net.kyori.adventure.title.Title reverseTitle(PaperAdventureAdapter adapter, Object title);
public final net.kyori.adventure.title.Title reverseTitle(Object title) {
return reverseTitle(this, title);
}
/*
* <ADAPT_TITLE_PART>
* public static net.kyori.adventure.title.TitlePart<?> adaptMessageType(com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart<?> titlePart) {
* if (titlePart == null) {
* return null;
* } else if (titlePart == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.TITLE) {
* return net.kyori.adventure.title.TitlePart.TITLE;
* } else if (titlePart == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.SUBTITLE) {
* return net.kyori.adventure.title.TitlePart.SUBTITLE;
* } else if (titlePart == com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.TIMES) {
* return net.kyori.adventure.title.TitlePart.TIMES;
* } else {
* throw new IllegalArgumentException("Unknown title part: " + titlePart);
* }
* }
*/
@Template.Generated("%ADAPT_TITLE_PART%")
public abstract Object adaptTitlePart(net.kyori.adventure.title.TitlePart<?> titlePart);
/*
* <REVERSE_TITLE_PART>
* public static com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart<?> reverseMessageType(net.kyori.adventure.title.TitlePart<?> titlePart) {
* if (titlePart == null) {
* return null;
* } else if (titlePart == net.kyori.adventure.title.TitlePart.TITLE) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.TITLE;
* } else if (titlePart == net.kyori.adventure.title.TitlePart.SUBTITLE) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.SUBTITLE;
* } else if (titlePart == net.kyori.adventure.title.TitlePart.TIMES) {
* return com.bergerkiller.bukkit.common.dep.net.kyori.adventure.title.TitlePart.TIMES;
* } else {
* throw new IllegalArgumentException("Unknown title part: " + titlePart);
* }
* }
*/
@Template.Generated("%REVERSE_TITLE_PART%")
public abstract <T> net.kyori.adventure.title.TitlePart<T> reverseTitlePart(Object titlePart);
public final net.kyori.adventure.audience.Audience reverseAudience(Object audience) {
return adaptAudience.reverseAudience(audience);
}
public final net.kyori.adventure.platform.AudienceProvider reverseAudienceProvider(Object audienceProvider) {
return adaptAudienceProvider.reverseAudienceProvider(audienceProvider);
}
/**
* Registers a Type Adapter that performs no adapting at all, passing it through.
*
* @param type Type
*/
private void registerPassThroughAdapter(
final Class<?> type
) {
registerTypeAdapter(new TypeAdapter() {
@Override
public Class<?> getType() {
return type;
}
@Override
public Class<?> getAdaptedType() {
return type;
}
@Override
public int applyParameter(ExtendedClassWriter<?> cw, MethodVisitor mv, int register) {
return MPLType.visitVarILoad(mv, register, type);
}
@Override
public void applyReturn(ExtendedClassWriter<?> cw, MethodVisitor mv) {
}
});
}
private void registerStandardAdapter(
final Class<?> type, /* For example: net.kyori.adventure.title.TitlePart.class */
final String adaptedTypeName, /* For example: "net.kyori.adventure.title.TitlePart" */
final String adaptMethodName, /* For example: "adaptTitlePart" */
final String reverseMethodName /* For example: "reverseTitlePart" */
) {
final Class<?> adaptedType = getAdventureClass(adaptedTypeName);
registerTypeAdapter(new TypeAdapter() {
@Override
public Class<?> getType() {
return type;
}
@Override
public Class<?> getAdaptedType() {
return adaptedType;
}
@Override
public int applyParameter(ExtendedClassWriter<?> cw, MethodVisitor mv, int register) {
// Access 'adapter' field of the adapter class we generate
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, cw.getInternalName(), "adapter", MPLType.getDescriptor(PaperAdventureAdapter.class));
// Load the parameter from the stack, and adapt it
int nextRegister = MPLType.visitVarILoad(mv, register, type);
mv.visitMethodInsn(INVOKEVIRTUAL, MPLType.getInternalName(PaperAdventureAdapter.class),
adaptMethodName, "(" + MPLType.getDescriptor(type) + ")Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, MPLType.getInternalName(adaptedType));
return nextRegister;
}
@Override
public void applyReturn(ExtendedClassWriter<?> cw, MethodVisitor mv) {
mv.visitMethodInsn(INVOKEVIRTUAL, MPLType.getInternalName(PaperAdventureAdapter.class), reverseMethodName,
"(Ljava/lang/Object;)" + MPLType.getDescriptor(type), false);
}
});
}
private void registerTypeAdapter(TypeAdapter typeAdapter) {
typeAdapters.put(typeAdapter.getType(), typeAdapter);
}
/**
* Looks up the (foreign) Adventure library Class and then initializes a loader for other
* types in the Adventure library.
*
* @return Loader
*/
private static Function<String, Class<?>> getAdventureClassLoader() {
final String prefix;
final ClassLoader classLoader;
try {
String audienceName = String.join(".", "net", "kyori", "adventure", "audience", "Audience");
Class<?> audienceType = Class.forName(audienceName);
prefix = audienceType.getName().endsWith(audienceName)
? audienceType.getName().substring(0, audienceType.getName().length() - audienceName.length())
: "";
classLoader = audienceType.getClassLoader();
} catch (ClassNotFoundException ex) {
throw new UnsupportedOperationException("Native Adventure library could not be found on the server");
}
return name -> {
try {
return Class.forName(prefix + name, false, classLoader);
} catch (ClassNotFoundException ex) {
throw new UnsupportedOperationException("Adventure library type not found: " + name);
}
};
}
public interface SignedMessageFactory<T> {
T create(
final java.time.Instant timestamp,
final long salt,
final String message,
final Object /* net.kyori.adventure.chat.SignedMessage$Signature */ signature,
final Object /* net.kyori.adventure.text.Component */ unsignedContent,
final Object /* net.kyori.adventure.identity.Identity */ identity
);
}
public interface AudienceAdapter {
net.kyori.adventure.audience.Audience reverseAudience(
final Object /* net.kyori.adventure.audience.Audience */ audience
);
}
public interface AudienceProviderAdapter {
net.kyori.adventure.platform.AudienceProvider reverseAudienceProvider(
final Object /* net.kyori.adventure.platform.AudienceProvider */ audienceProvider
);
}
/**
* A single type adapter responsible for converting from a public API Type to an adapted
* (foreign) Adventure type. Handles generating the required ASM for converting
* parameter types before calling the foreign method and converting the method result
* back into API types.
*/
public interface TypeAdapter {
/**
* Gets the API type (known to BKCommonLib at compile time)
*
* @return API Type
*/
Class<?> getType();
/**
* Gets the adapted type (not known to BKCommonLib at compile time)
*
* @return Adapted Type
*/
Class<?> getAdaptedType();
/**
* Adapts the type from API type to Adapted Type for a method parameter
*
* @param cw ExtendedClassWriter
* @param mv MethodVisitor
* @param register Parameter register position
* @return Parameter register position for the next parameter (register + size)
*/
int applyParameter(ExtendedClassWriter<?> cw, MethodVisitor mv, int register);
/**
* Adapts the type from Adapted Type to API type for a return value
*
* @param cw ExtendedClassWriter
* @param mv MethodVisitor
*/
void applyReturn(ExtendedClassWriter<?> cw, MethodVisitor mv);
}
}
@@ -0,0 +1,135 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
import com.bergerkiller.mountiplex.reflection.util.ExtendedClassWriter;
import com.bergerkiller.mountiplex.reflection.util.FastConstructor;
import com.bergerkiller.mountiplex.reflection.util.asm.MPLType;
import net.kyori.adventure.audience.Audience;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
/**
* Because we shade in the Adventure library at a path different than Paper, we cannot
* make use of Paper's own Audience implementation. This class generates an adapter
* class that calls into Paper's Audience methods from (remapped) our own.<br>
* <br>
* To avoid making this a nightmare to maintain, it generates most of the adapter code.
*/
public class PaperAudienceAdapterFactoryOld {
private final FastConstructor<Audience> audienceAdapterCtor = new FastConstructor<>();
public PaperAudienceAdapterFactoryOld() throws Throwable {
}
/*
* <li>net.kyori.adventure.key.Key</li>
* <li>net.kyori.adventure.text.ComponentLike</li> Don't implement, use default implementation
* <li>net.kyori.adventure.text.Component</li> Implement, with GSON String as intermediate
* <li>net.kyori.adventure.audience.MessageType</li> Implement, just CHAT or SYSTEM
* <li>net.kyori.adventure.identity.Identified</li> Don't implement, use default implementation
* <li>net.kyori.adventure.identity.Identity</li> Implement, just has UUID field
* <li>net.kyori.adventure.chat.ChatType.Bound</li> Implement, and ChatType too
* <li>net.kyori.adventure.chat.SignedMessage</li> Implement, has simple types (+Component)
* <li>net.kyori.adventure.chat.SignedMessage.Signature</li> Implement, is just byte[]
* <li>net.kyori.adventure.title.Title</li> Implement, has Times (Duration) and some components
* <li>net.kyori.adventure.title.TitlePart</li> Implements, it's like an enum
*/
private static Class<? extends Audience> createAudienceAdapterClass(Class<?> paperAudienceType) {
ExtendedClassWriter<? extends Audience> cw = ExtendedClassWriter.builder(Audience.class)
.setFlags(ClassWriter.COMPUTE_MAXS)
.setAccess(ACC_FINAL)
.setExactName("com.bergerkiller.bukkit.common.cloud.audiences.PaperAudienceAdapterFactoryOld$AudienceAdapter")
.build();
FieldVisitor fieldVisitor;
MethodVisitor methodVisitor;
// Add field storing the Paper audience implementation type
{
fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, "aud",
MPLType.getDescriptor(paperAudienceType), null, null);
fieldVisitor.visitEnd();
}
// Generate a constructor that assigns this Paper audience field
{
methodVisitor = cw.visitMethod(ACC_PUBLIC, "<init>", "(" + MPLType.getDescriptor(paperAudienceType) + ")V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), "aud", MPLType.getDescriptor(paperAudienceType));
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
// Go by all methods that exist in the Audience class, and implement them by calling
// the same method on the 'aud' field. If the method is not available, and the method
// is abstract (no default implementation), implements it to throw an UnsupportedOperationException
{
methodVisitor = cw.visitMethod(ACC_PUBLIC, "doThing", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
methodVisitor.visitInsn(DUP);
methodVisitor.visitLdcInsn("Not Supported");
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V", false);
methodVisitor.visitInsn(ATHROW);
methodVisitor.visitMaxs(3, 1);
methodVisitor.visitEnd();
}
cw.visitEnd();
//return cw.toByteArray();
return null;
}
/**
* Type Adapter. These implementations exist:
* <ul>
* <li>net.kyori.adventure.key.Key</li>
* <li>net.kyori.adventure.text.ComponentLike</li>
* <li>net.kyori.adventure.text.Component</li>
* <li>net.kyori.adventure.audience.MessageType</li>
* <li>net.kyori.adventure.identity.Identified</li>
* <li>net.kyori.adventure.identity.Identity</li>
* <li>net.kyori.adventure.chat.ChatType.Bound</li>
* <li>net.kyori.adventure.chat.SignedMessage</li>
* <li>net.kyori.adventure.chat.SignedMessage.Signature</li>
* <li>net.kyori.adventure.title.Title</li>
* <li>net.kyori.adventure.title.TitlePart</li>
* </ul>
*
* These are not implemented and these functionalities will not work as a result:
* <ul>
* <li>net.kyori.adventure.bossbar.BossBar</li>
* <li>net.kyori.adventure.sound.Sound</li>
* <li>net.kyori.adventure.sound.SoundStop</li>
* <li>net.kyori.adventure.sound.Sound.Emitter</li>
* <li>net.kyori.adventure.inventory.Book.Builder</li>
* <li>net.kyori.adventure.inventory.Book</li>
* <li>net.kyori.adventure.resource.ResourcePackInfoLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequestLike</li>
* <li>net.kyori.adventure.resource.ResourcePackRequest</li>
* <li>net.kyori.adventure.dialog.DialogLike</li>
* </ul>
*
* @param <A> Type we know
* @param <B> Unknown type
*/
public interface TypeAdapter<A, B> {
/** Adapts the type we know into the unknown type */
B adapt(A value);
/** Restores the unknown type back into a type we know (reverse) */
A restore(B value);
}
}
@@ -0,0 +1,131 @@
package com.bergerkiller.bukkit.common.cloud.audiences;
import com.bergerkiller.mountiplex.reflection.util.ExtendedClassWriter;
import com.bergerkiller.mountiplex.reflection.util.FastConstructor;
import com.bergerkiller.mountiplex.reflection.util.UniqueHash;
import com.bergerkiller.mountiplex.reflection.util.asm.MPLType;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.objectweb.asm.Opcodes.*;
/**
* Generates implementation types for net.kyori.adventure.chat.SignedMessage.
*/
final class SignedMessageFactoryImpl<T> implements PaperAdventureAdapter.SignedMessageFactory<T> {
private static final UniqueHash generatedClassCtr = new UniqueHash();
private final FastConstructor<T> ctor;
public SignedMessageFactoryImpl(Class<?> signatureClass) {
this.ctor = new FastConstructor<>(generateConstructor(signatureClass));
}
@Override
public T create(
final java.time.Instant timestamp,
final long salt,
final String message,
final Object /* net.kyori.adventure.chat.SignedMessage$Signature */ signature,
final Object /* net.kyori.adventure.text.Component */ unsignedContent,
final Object /* net.kyori.adventure.identity.Identity */ identity
) {
return ctor.newInstanceVA(timestamp, salt, message, signature, unsignedContent, identity);
}
private static <T> Constructor<? extends T> generateConstructor(Class<?> signatureClass) {
ExtendedClassWriter<? extends T> cw = ExtendedClassWriter.builder(signatureClass)
.setFlags(ClassWriter.COMPUTE_MAXS)
.setAccess(ACC_FINAL)
.setExactName(SignedMessageFactoryImpl.class.getName() + "$SignedMessageImpl" + generatedClassCtr.nextHex())
.build();
MethodVisitor methodVisitor;
// We need these types
Class<?> signedMessageSignatureType = findAdventureType(signatureClass, "chat.SignedMessage$Signature");
Class<?> componentType = findAdventureType(signatureClass, "text.Component");
Class<?> identityType = findAdventureType(signatureClass, "identity.Identity");
// Declare all fields in our type that will also be set as constructor parameters
List<FieldInfo> fields = Arrays.asList(
new FieldInfo("timestamp", "timestamp", java.time.Instant.class),
new FieldInfo("salt", "salt", long.class),
new FieldInfo("message", "message", String.class),
new FieldInfo("signature", "signature", signedMessageSignatureType),
new FieldInfo("unsignedContent", "unsignedContent", componentType),
new FieldInfo("identity", "identity", identityType)
);
// Add fields as private final fields
for (FieldInfo field : fields) {
FieldVisitor fieldVisitor = cw.visitField(ACC_PRIVATE | ACC_FINAL, field.name,
MPLType.getDescriptor(field.type), null, null);
fieldVisitor.visitEnd();
}
// Generate a constructor that assigns all these fields
{
String ctorDesc = "(" + fields.stream().map(f -> MPLType.getDescriptor(f.type)).collect(Collectors.joining()) + ")V";
methodVisitor = cw.visitMethod(ACC_PUBLIC, "<init>", ctorDesc, null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
int register = 1;
for (FieldInfo field : fields) {
methodVisitor.visitVarInsn(ALOAD, 0);
register = MPLType.visitVarILoad(methodVisitor, register, field.type);
methodVisitor.visitFieldInsn(PUTFIELD, cw.getInternalName(), field.name, MPLType.getDescriptor(field.type));
}
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
// Implement all the getter methods that return our fields
for (FieldInfo field : fields) {
methodVisitor = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, field.getterName,
"()" + MPLType.getDescriptor(field.type), null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, cw.getInternalName(), field.name, MPLType.getDescriptor(field.type));
methodVisitor.visitInsn(MPLType.getOpcode(field.type, IRETURN));
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
// Generate the class and retrieve its constructor
return cw.generateConstructor(fields.stream().map(f -> f.type).toArray(Class<?>[]::new));
}
private static Class<?> findAdventureType(Class<?> signedMessageType, String adventureTypeName) {
// Extract package from signedMessageType (net.kyori.adventure.chat.SignedMessage -> net.kyori.adventure)
String prefix = signedMessageType.getName().substring(0, signedMessageType.getName().length() - 18);
String fullTypeName = prefix + adventureTypeName;
try {
return Class.forName(fullTypeName, false, signedMessageType.getClassLoader());
} catch (ClassNotFoundException ex) {
throw new UnsupportedOperationException("Adventure library type not found: " + fullTypeName);
}
}
private static class FieldInfo {
public final String name;
public final Class<?> type;
public final String getterName;
public FieldInfo(String name, String getterName, Class<?> type) {
this.name = name;
this.getterName = getterName;
this.type = type;
}
}
}
@@ -58,6 +58,8 @@ import com.bergerkiller.mountiplex.MountiplexUtil;
import com.bergerkiller.mountiplex.reflection.SafeField;
import com.bergerkiller.mountiplex.reflection.util.asm.ASMUtil;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
@@ -74,6 +76,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.incendo.cloud.minecraft.extras.AudienceProvider;
import java.io.File;
import java.lang.ref.SoftReference;
@@ -676,6 +679,18 @@ public class CommonPlugin extends PluginBase {
return;
}
new Task(this) {
@Override
public void run() {
try (BukkitAudiences aud = BukkitAudiences.create(this.getPlugin())) {
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendMessage("OLD STYLE HELLO");
aud.player(player).sendMessage(Component.text("Hello!"));
}
}
}
}.start(20, 20);
// Welcome message
final List<String> welcomeMessages = Arrays.asList(
"This library is written with stability in mind.",