Merge main

This commit is contained in:
Vankka 2022-03-01 17:40:17 +02:00
commit f3021d1472
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
9 changed files with 138 additions and 71 deletions

View File

@ -32,7 +32,7 @@ import java.util.Optional;
/** /**
* A Minecraft json text component. Use {@link DiscordSRVApi#componentFactory()} to get an instance.<br/> * A Minecraft json text component. Use {@link DiscordSRVApi#componentFactory()} to get an instance.<br/>
* <br/> * <br/>
* This is designed to work with Adventure, see {@link #adventureAdapter(Class)} & {@link #adventureAdapter(MinecraftComponentAdapter)} * This is designed to work with Adventure, see {@link #adventureAdapter(Class, Class)} & {@link #adventureAdapter(MinecraftComponentAdapter)}
* but is compatible with anything able to handle Minecraft's json format. * but is compatible with anything able to handle Minecraft's json format.
* Legacy is <b>not supported</b>. * Legacy is <b>not supported</b>.
*/ */
@ -66,11 +66,26 @@ public interface MinecraftComponent {
* Creates an Adventure adapter for convenience. * Creates an Adventure adapter for convenience.
* *
* @param gsonSerializerClass the gson serializer class * @param gsonSerializerClass the gson serializer class
* @return a adapter that will convert to/from relocated or unrelocated adventure classes to/from json * @return an adapter that will convert to/from relocated or unrelocated adventure classes to/from json
* @throws IllegalArgumentException if the provided class is not an Adventure GsonComponentSerializer * @throws IllegalArgumentException if the provided class is not an Adventure GsonComponentSerializer
* @see #adventureAdapter(Class, Class)
*/ */
@NotNull @NotNull
Adapter adventureAdapter(@NotNull Class<?> gsonSerializerClass); default Adapter<Object> adventureAdapter(@NotNull Class<?> gsonSerializerClass) {
return adventureAdapter(gsonSerializerClass, null);
}
/**
* Creates an Adventure adapter for convenience.
*
* @param gsonSerializerClass the {@code GsonComponentSerializer} class
* @param componentClass the {@code Component} class that's returned by the given gson component serializer
* @return an adapter that will convert to/from relocated or unrelocated adventure classes to/from json
* @throws IllegalArgumentException if the provided class is not an Adventure {@code GsonComponentSerializer}
* or if the provided {@code Component} class isn't the one returned by the serializer
*/
@NotNull
<T> Adapter<T> adventureAdapter(@NotNull Class<?> gsonSerializerClass, Class<T> componentClass);
/** /**
* Creates an Adventure adapter from a {@link MinecraftComponentAdapter} for convenience. * Creates an Adventure adapter from a {@link MinecraftComponentAdapter} for convenience.
@ -79,7 +94,7 @@ public interface MinecraftComponent {
* @return a {@link Adapter} for this component using the given {@link MinecraftComponentAdapter} * @return a {@link Adapter} for this component using the given {@link MinecraftComponentAdapter}
*/ */
@NotNull @NotNull
Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter); <T> Adapter<T> adventureAdapter(@NotNull MinecraftComponentAdapter<T> adapter);
/** /**
* Creates an Adventure adapter for the unrelocated adventure. * Creates an Adventure adapter for the unrelocated adventure.
@ -88,8 +103,8 @@ public interface MinecraftComponent {
*/ */
@NotNull @NotNull
@ApiStatus.NonExtendable @ApiStatus.NonExtendable
default Optional<Adapter> unrelocatedAdapter() { default Optional<Adapter<Object>> unrelocatedAdapter() {
MinecraftComponentAdapter adapter = MinecraftComponentAdapter.UNRELOCATED; MinecraftComponentAdapter<Object> adapter = MinecraftComponentAdapter.UNRELOCATED;
if (adapter == null) { if (adapter == null) {
return Optional.empty(); return Optional.empty();
} }
@ -99,21 +114,21 @@ public interface MinecraftComponent {
/** /**
* An Adventure adapter, converts from/to given adventure components from/to json. * An Adventure adapter, converts from/to given adventure components from/to json.
*/ */
interface Adapter { interface Adapter<Component> {
/** /**
* Returns the Adventure Component returned by the gson serializer of this adapter. * Returns the Adventure Component returned by the gson serializer of this adapter.
* @return the {@code net.kyori.adventure.text.Component} (or relocated), cast this to your end class * @return the {@code net.kyori.adventure.text.Component} (or relocated), cast this to your end class
*/ */
@NotNull @NotNull
Object getComponent(); Component getComponent();
/** /**
* Sets the component to the component that can be serialized by the gson serializer for this class. * Sets the component to the component that can be serialized by the gson serializer for this class.
* @param adventureComponent the component * @param adventureComponent the component
* @throws IllegalArgumentException if the provided component cannot be processed by the gson serializer of this adapter * @throws IllegalArgumentException if the provided component cannot be processed by the gson serializer of this adapter
*/ */
void setComponent(@NotNull Object adventureComponent); void setComponent(@NotNull Component adventureComponent);
} }
} }

View File

@ -30,12 +30,12 @@ import java.lang.reflect.Method;
* A persistent Adventure adapter for {@link MinecraftComponent}s, this is more efficient than using {@link MinecraftComponent#adventureAdapter(Class)}. * A persistent Adventure adapter for {@link MinecraftComponent}s, this is more efficient than using {@link MinecraftComponent#adventureAdapter(Class)}.
* @see MinecraftComponent#adventureAdapter(MinecraftComponentAdapter) * @see MinecraftComponent#adventureAdapter(MinecraftComponentAdapter)
*/ */
public class MinecraftComponentAdapter { public class MinecraftComponentAdapter<Component> {
public static final MinecraftComponentAdapter UNRELOCATED; public static final MinecraftComponentAdapter<Object> UNRELOCATED;
static { static {
MinecraftComponentAdapter unrelocated = null; MinecraftComponentAdapter<Object> unrelocated = null;
try { try {
unrelocated = MinecraftComponentAdapter.create( unrelocated = MinecraftComponentAdapter.create(
Class.forName("net.ky".concat("ori.adventure.text.serializer.gson.GsonComponentSerializer")) Class.forName("net.ky".concat("ori.adventure.text.serializer.gson.GsonComponentSerializer"))
@ -45,14 +45,26 @@ public class MinecraftComponentAdapter {
} }
/** /**
* Creates a {@link MinecraftComponentAdapter} that can be used with {@link MinecraftComponent}s. * Creates a new {@link MinecraftComponentAdapter} that can be used with {@link MinecraftComponent}s.
* *
* @param gsonSerializerClass a GsonComponentSerializer class * @param gsonSerializerClass a GsonComponentSerializer class
* @return a new {@link MinecraftComponentAdapter} with the provided GsonComponentSerializer * @return a new {@link MinecraftComponentAdapter} with the provided GsonComponentSerializer
* @throws IllegalArgumentException if the provided argument is not a GsonComponentSerialize class * @throws IllegalArgumentException if the provided argument is not a GsonComponentSerialize class
*/ */
public static MinecraftComponentAdapter create(Class<?> gsonSerializerClass) { public static MinecraftComponentAdapter<Object> create(Class<?> gsonSerializerClass) {
return new MinecraftComponentAdapter(gsonSerializerClass); return new MinecraftComponentAdapter<>(gsonSerializerClass, null);
}
/**
* Creates a new {@link MinecraftComponentAdapter} that can be used with {@link MinecraftComponent}s.
*
* @param gsonSerializerClass a GsonComponentSerializer class
* @param componentClass the Component class returned by the GsonComponentSerializer
* @return a new {@link MinecraftComponentAdapter} with the provided GsonComponentSerializer
* @throws IllegalArgumentException if the provided argument is not a GsonComponentSerialize class
*/
public static <Component> MinecraftComponentAdapter<Component> create(Class<?> gsonSerializerClass, Class<Component> componentClass) {
return new MinecraftComponentAdapter<>(gsonSerializerClass, componentClass);
} }
private final Class<?> gsonSerializerClass; private final Class<?> gsonSerializerClass;
@ -60,31 +72,48 @@ public class MinecraftComponentAdapter {
private final Method deserialize; private final Method deserialize;
private final Method serialize; private final Method serialize;
private MinecraftComponentAdapter(Class<?> gsonSerializerClass) { private MinecraftComponentAdapter(Class<?> gsonSerializerClass, Class<Component> providedComponentClass) {
try { try {
this.gsonSerializerClass = gsonSerializerClass; this.gsonSerializerClass = gsonSerializerClass;
this.instance = gsonSerializerClass.getDeclaredMethod("gson").invoke(null); this.instance = gsonSerializerClass.getDeclaredMethod("gson").invoke(null);
this.deserialize = gsonSerializerClass.getMethod("deserialize", Object.class); this.deserialize = gsonSerializerClass.getMethod("deserialize", Object.class);
Class<?> componentClass = deserialize.getReturnType(); Class<?> componentClass = deserialize.getReturnType();
checkComponentClass(providedComponentClass, componentClass);
this.serialize = gsonSerializerClass.getMethod("serialize", componentClass); this.serialize = gsonSerializerClass.getMethod("serialize", componentClass);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
throw new IllegalArgumentException("The provided class is not a GsonComponentSerializer", e); throw new IllegalArgumentException("The provided class is not a GsonComponentSerializer", e);
} }
} }
public Class<?> gsonSerializerClass() { private static void checkComponentClass(Class<?> provided, Class<?> actual) {
if (provided == null) {
// Ignore null
return;
}
String providedName = provided.getName();
String actualName = actual.getName();
if (!providedName.equals(actualName)) {
throw new IllegalArgumentException(
"The provided Component class (" + providedName
+ ") does not match the one returned by the serializer: " + actualName
);
}
}
public Class<?> serializerClass() {
return gsonSerializerClass; return gsonSerializerClass;
} }
public Object instance() { public Object serializerInstance() {
return instance; return instance;
} }
public Method deserialize() { public Method deserializeMethod() {
return deserialize; return deserialize;
} }
public Method serialize() { public Method serializeMethod() {
return serialize; return serialize;
} }
} }

View File

@ -31,8 +31,11 @@ shadowJar {
// HikariCP // HikariCP
'com.zaxxer.hikari', 'com.zaxxer.hikari',
// Adventure, EnhancedLegacyText, MCDiscordReserializer // Adventure (API isn't relocated always)
'net.kyori', 'net.kyori.adventure.platform',
'net.kyori.adventure.text.serializer',
// EnhancedLegacyText, MCDiscordReserializer
'dev.vankka.enhancedlegacytext', 'dev.vankka.enhancedlegacytext',
'dev.vankka.mcdiscordreserializer', 'dev.vankka.mcdiscordreserializer',
'dev.vankka.simpleast', 'dev.vankka.simpleast',

View File

@ -48,5 +48,6 @@ dependencies {
shadowJar { shadowJar {
archiveFileName = 'bukkit.jarinjar' archiveFileName = 'bukkit.jarinjar'
// Relocations in buildscript/relocations.gradle relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
// More relocations in buildscript/relocations.gradle
} }

View File

@ -70,12 +70,10 @@ public final class PaperComponentUtil {
} }
MinecraftComponent component = discordSRV.componentFactory().empty(); MinecraftComponent component = discordSRV.componentFactory().empty();
MinecraftComponent.Adapter adapter = component.unrelocatedAdapter().orElse(null); component.unrelocatedAdapter()
if (adapter == null) { .orElseThrow(() -> new IllegalStateException("Unrelocated adventure unavailable"))
throw new IllegalStateException("Unrelocated adventure unavailable"); .setComponent(unrelocated);
}
adapter.setComponent(unrelocated);
return component; return component;
} }
} }

View File

@ -36,5 +36,6 @@ dependencies {
shadowJar { shadowJar {
archiveFileName = 'bungee.jarinjar' archiveFileName = 'bungee.jarinjar'
// Relocations in buildscript/relocations.gradle relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
// More relocations in buildscript/relocations.gradle
} }

View File

@ -44,6 +44,15 @@ public class MinecraftComponentImpl implements MinecraftComponent {
setComponent(component); setComponent(component);
} }
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
this.json = GsonComponentSerializer.gson().serialize(component);
}
@Override @Override
public @NotNull String asJson() { public @NotNull String asJson() {
return json; return json;
@ -66,43 +75,37 @@ public class MinecraftComponentImpl implements MinecraftComponent {
return PlainTextComponentSerializer.plainText().serialize(component); return PlainTextComponentSerializer.plainText().serialize(component);
} }
public Component getComponent() { @Override
return component; public <T> MinecraftComponent.@NotNull Adapter<T> adventureAdapter(
} @NotNull Class<?> gsonSerializerClass, @NotNull Class<T> componentClass
) {
public void setComponent(Component component) { return new Adapter<>(gsonSerializerClass, componentClass);
this.component = component;
this.json = GsonComponentSerializer.gson().serialize(component);
} }
@Override @Override
public @NotNull MinecraftComponent.Adapter adventureAdapter(@NotNull Class<?> gsonSerializerClass) { public <T> MinecraftComponent.@NotNull Adapter<T> adventureAdapter(@NotNull MinecraftComponentAdapter<T> adapter) {
return new Adapter(gsonSerializerClass); return new Adapter<>(adapter);
} }
@Override @SuppressWarnings("unchecked")
public @NotNull MinecraftComponent.Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter) { public class Adapter<T> implements MinecraftComponent.Adapter<T> {
return new Adapter(adapter);
private final MinecraftComponentAdapter<T> adapter;
private Adapter(Class<?> gsonSerializerClass, Class<T> componentClass) {
this(MinecraftComponentAdapter.create(gsonSerializerClass, componentClass));
} }
public class Adapter implements MinecraftComponent.Adapter { private Adapter(MinecraftComponentAdapter<T> adapter) {
private final MinecraftComponentAdapter adapter;
private Adapter(Class<?> gsonSerializerClass) {
this(MinecraftComponentAdapter.create(gsonSerializerClass));
}
private Adapter(MinecraftComponentAdapter adapter) {
this.adapter = adapter; this.adapter = adapter;
} }
@Override @Override
public @NotNull Object getComponent() { public @NotNull T getComponent() {
try { try {
return adapter.deserialize() return (T) adapter.deserializeMethod()
.invoke( .invoke(
adapter.instance(), adapter.serializerInstance(),
json json
); );
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
@ -113,13 +116,15 @@ public class MinecraftComponentImpl implements MinecraftComponent {
@Override @Override
public void setComponent(@NotNull Object adventureComponent) { public void setComponent(@NotNull Object adventureComponent) {
try { try {
json = (String) adapter.serialize() setJson(
(String) adapter.serializeMethod()
.invoke( .invoke(
adapter.instance(), adapter.serializerInstance(),
adventureComponent adventureComponent
)
); );
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw new IllegalArgumentException("The provided class is not a Component for the GsonComponentSerializer " + adapter.gsonSerializerClass().getName(), e); throw new IllegalArgumentException("The provided class is not a Component for the GsonComponentSerializer " + adapter.serializerClass().getName(), e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new RuntimeException("Failed to convert from adventure component", e); throw new RuntimeException("Failed to convert from adventure component", e);
} }

View File

@ -34,7 +34,11 @@ import java.util.Collection;
*/ */
public final class ComponentUtil { public final class ComponentUtil {
public static final MinecraftComponentAdapter ADAPTER = MinecraftComponentAdapter.create(GsonComponentSerializer.class); private static MinecraftComponentAdapter<Component> ADAPTER;
private static MinecraftComponentAdapter<Component> getAdapter() {
return ADAPTER != null ? ADAPTER : (ADAPTER = MinecraftComponentAdapter.create(GsonComponentSerializer.class, Component.class));
}
private ComponentUtil() {} private ComponentUtil() {}
@ -43,7 +47,7 @@ public final class ComponentUtil {
} }
public static boolean isEmpty(MinecraftComponent component) { public static boolean isEmpty(MinecraftComponent component) {
return isEmpty(fromAPI(component)); return component.asPlainString().isEmpty();
} }
public static MinecraftComponent toAPI(Component component) { public static MinecraftComponent toAPI(Component component) {
@ -54,12 +58,16 @@ public final class ComponentUtil {
if (component instanceof MinecraftComponentImpl) { if (component instanceof MinecraftComponentImpl) {
return ((MinecraftComponentImpl) component).getComponent(); return ((MinecraftComponentImpl) component).getComponent();
} else { } else {
return (Component) component.adventureAdapter(ADAPTER).getComponent(); return component.adventureAdapter(getAdapter()).getComponent();
} }
} }
public static void set(MinecraftComponent minecraftComponent, Component component) { public static void set(MinecraftComponent minecraftComponent, Component component) {
minecraftComponent.adventureAdapter(ADAPTER).setComponent(component); if (component instanceof MinecraftComponentImpl) {
((MinecraftComponentImpl) component).setComponent(component);
} else {
minecraftComponent.adventureAdapter(getAdapter()).setComponent(component);
}
} }
public static Component fromUnrelocated(Object unrelocatedAdventure) { public static Component fromUnrelocated(Object unrelocatedAdventure) {

View File

@ -24,6 +24,7 @@ import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
import dev.vankka.mcdependencydownload.loader.ILoader; import dev.vankka.mcdependencydownload.loader.ILoader;
import dev.vankka.mcdependencydownload.loader.exception.LoadingException; import dev.vankka.mcdependencydownload.loader.exception.LoadingException;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.Game; import org.spongepowered.api.Game;
import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Listener;
@ -34,10 +35,11 @@ import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.builtin.jvm.Plugin; import org.spongepowered.plugin.builtin.jvm.Plugin;
import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@Plugin("discordsrv") @Plugin("discordsrv")
@ -46,6 +48,7 @@ public class DiscordSRVSpongeLoader implements ILoader {
private final PluginContainer pluginContainer; private final PluginContainer pluginContainer;
private final Game game; private final Game game;
private final Path dataDirectory; private final Path dataDirectory;
private final JarInJarClassLoader classLoader;
private ISpongeBootstrap bootstrap; private ISpongeBootstrap bootstrap;
@Inject @Inject
@ -60,10 +63,11 @@ public class DiscordSRVSpongeLoader implements ILoader {
logger.error("| DiscordSRV does not run on clients |"); logger.error("| DiscordSRV does not run on clients |");
logger.error("| DiscordSRV can only be installed on servers |"); logger.error("| DiscordSRV can only be installed on servers |");
logger.error("+---------------------------------------------+"); logger.error("+---------------------------------------------+");
this.classLoader = null;
return; return;
} }
initialize(); this.classLoader = initialize();
} }
private Optional<ISpongeBootstrap> bootstrap() { private Optional<ISpongeBootstrap> bootstrap() {
@ -71,14 +75,12 @@ public class DiscordSRVSpongeLoader implements ILoader {
} }
@Override @Override
public String getBootstrapClassName() { public @NotNull String getBootstrapClassName() {
return "com.discordsrv.sponge.DiscordSRVSpongeBootstrap"; return "com.discordsrv.sponge.DiscordSRVSpongeBootstrap";
} }
@Override @Override
public void initiateBootstrap(Class<?> bootstrapClass, JarInJarClassLoader classLoader) public void initiateBootstrap(Class<?> bootstrapClass, @NotNull JarInJarClassLoader classLoader) throws ReflectiveOperationException {
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Constructor<?> constructor = bootstrapClass.getConstructor(PluginContainer.class, Game.class, JarInJarClassLoader.class, Path.class); Constructor<?> constructor = bootstrapClass.getConstructor(PluginContainer.class, Game.class, JarInJarClassLoader.class, Path.class);
bootstrap = (ISpongeBootstrap) constructor.newInstance(pluginContainer, game, classLoader, dataDirectory); bootstrap = (ISpongeBootstrap) constructor.newInstance(pluginContainer, game, classLoader, dataDirectory);
} }
@ -89,18 +91,18 @@ public class DiscordSRVSpongeLoader implements ILoader {
} }
@Override @Override
public String getName() { public @NotNull String getName() {
return "DiscordSRV"; return "DiscordSRV";
} }
@Override @Override
public ClassLoader getParentClassLoader() { public @NotNull ClassLoader getParentClassLoader() {
return getClass().getClassLoader(); return getClass().getClassLoader();
} }
@Override @Override
public URL getJarInJarResource() { public @NotNull URL getJarInJarResource() {
return getParentClassLoader().getResource("sponge.jarinjar"); return Objects.requireNonNull(getParentClassLoader().getResource("sponge.jarinjar"));
} }
@Listener @Listener
@ -121,5 +123,10 @@ public class DiscordSRVSpongeLoader implements ILoader {
@Listener @Listener
public void onStoppingEngine(StoppingEngineEvent<?> event) { public void onStoppingEngine(StoppingEngineEvent<?> event) {
bootstrap().ifPresent(ISpongeBootstrap::onStopping); bootstrap().ifPresent(ISpongeBootstrap::onStopping);
try {
classLoader.close();
} catch (IOException e) {
pluginContainer.logger().error("Failed to close JarInJarClassLoader", e);
}
} }
} }