diff --git a/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java b/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java
index bd79cea1..86db7345 100644
--- a/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java
+++ b/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java
@@ -32,7 +32,7 @@ import java.util.Optional;
/**
* A Minecraft json text component. Use {@link DiscordSRVApi#componentFactory()} to get an instance.
*
- * 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.
* Legacy is not supported .
*/
@@ -66,11 +66,26 @@ public interface MinecraftComponent {
* Creates an Adventure adapter for convenience.
*
* @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
+ * @see #adventureAdapter(Class, Class)
*/
@NotNull
- Adapter adventureAdapter(@NotNull Class> gsonSerializerClass);
+ default Adapter 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
+ Adapter adventureAdapter(@NotNull Class> gsonSerializerClass, Class componentClass);
/**
* 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}
*/
@NotNull
- Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter);
+ Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter);
/**
* Creates an Adventure adapter for the unrelocated adventure.
@@ -88,8 +103,8 @@ public interface MinecraftComponent {
*/
@NotNull
@ApiStatus.NonExtendable
- default Optional unrelocatedAdapter() {
- MinecraftComponentAdapter adapter = MinecraftComponentAdapter.UNRELOCATED;
+ default Optional> unrelocatedAdapter() {
+ MinecraftComponentAdapter adapter = MinecraftComponentAdapter.UNRELOCATED;
if (adapter == null) {
return Optional.empty();
}
@@ -99,21 +114,21 @@ public interface MinecraftComponent {
/**
* An Adventure adapter, converts from/to given adventure components from/to json.
*/
- interface Adapter {
+ interface 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
*/
@NotNull
- Object getComponent();
+ Component getComponent();
/**
* Sets the component to the component that can be serialized by the gson serializer for this class.
* @param adventureComponent the component
* @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);
}
}
diff --git a/api/src/main/java/com/discordsrv/api/component/MinecraftComponentAdapter.java b/api/src/main/java/com/discordsrv/api/component/MinecraftComponentAdapter.java
index e28af54a..a646e619 100644
--- a/api/src/main/java/com/discordsrv/api/component/MinecraftComponentAdapter.java
+++ b/api/src/main/java/com/discordsrv/api/component/MinecraftComponentAdapter.java
@@ -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)}.
* @see MinecraftComponent#adventureAdapter(MinecraftComponentAdapter)
*/
-public class MinecraftComponentAdapter {
+public class MinecraftComponentAdapter {
- public static final MinecraftComponentAdapter UNRELOCATED;
+ public static final MinecraftComponentAdapter UNRELOCATED;
static {
- MinecraftComponentAdapter unrelocated = null;
+ MinecraftComponentAdapter unrelocated = null;
try {
unrelocated = MinecraftComponentAdapter.create(
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
* @return a new {@link MinecraftComponentAdapter} with the provided GsonComponentSerializer
* @throws IllegalArgumentException if the provided argument is not a GsonComponentSerialize class
*/
- public static MinecraftComponentAdapter create(Class> gsonSerializerClass) {
- return new MinecraftComponentAdapter(gsonSerializerClass);
+ public static MinecraftComponentAdapter create(Class> 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 MinecraftComponentAdapter create(Class> gsonSerializerClass, Class componentClass) {
+ return new MinecraftComponentAdapter<>(gsonSerializerClass, componentClass);
}
private final Class> gsonSerializerClass;
@@ -60,31 +72,48 @@ public class MinecraftComponentAdapter {
private final Method deserialize;
private final Method serialize;
- private MinecraftComponentAdapter(Class> gsonSerializerClass) {
+ private MinecraftComponentAdapter(Class> gsonSerializerClass, Class providedComponentClass) {
try {
this.gsonSerializerClass = gsonSerializerClass;
this.instance = gsonSerializerClass.getDeclaredMethod("gson").invoke(null);
this.deserialize = gsonSerializerClass.getMethod("deserialize", Object.class);
Class> componentClass = deserialize.getReturnType();
+ checkComponentClass(providedComponentClass, componentClass);
this.serialize = gsonSerializerClass.getMethod("serialize", componentClass);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException 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;
}
- public Object instance() {
+ public Object serializerInstance() {
return instance;
}
- public Method deserialize() {
+ public Method deserializeMethod() {
return deserialize;
}
- public Method serialize() {
+ public Method serializeMethod() {
return serialize;
}
}
diff --git a/buildscript/relocations.gradle b/buildscript/relocations.gradle
index 48f977d0..ccacec80 100644
--- a/buildscript/relocations.gradle
+++ b/buildscript/relocations.gradle
@@ -31,8 +31,11 @@ shadowJar {
// HikariCP
'com.zaxxer.hikari',
- // Adventure, EnhancedLegacyText, MCDiscordReserializer
- 'net.kyori',
+ // Adventure (API isn't relocated always)
+ 'net.kyori.adventure.platform',
+ 'net.kyori.adventure.text.serializer',
+
+ // EnhancedLegacyText, MCDiscordReserializer
'dev.vankka.enhancedlegacytext',
'dev.vankka.mcdiscordreserializer',
'dev.vankka.simpleast',
diff --git a/bukkit/build.gradle b/bukkit/build.gradle
index 9df4dd1c..0b943e23 100644
--- a/bukkit/build.gradle
+++ b/bukkit/build.gradle
@@ -48,5 +48,6 @@ dependencies {
shadowJar {
archiveFileName = 'bukkit.jarinjar'
- // Relocations in buildscript/relocations.gradle
+ relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
+ // More relocations in buildscript/relocations.gradle
}
diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java b/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java
index 16a15736..486fa10d 100644
--- a/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java
+++ b/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java
@@ -70,12 +70,10 @@ public final class PaperComponentUtil {
}
MinecraftComponent component = discordSRV.componentFactory().empty();
- MinecraftComponent.Adapter adapter = component.unrelocatedAdapter().orElse(null);
- if (adapter == null) {
- throw new IllegalStateException("Unrelocated adventure unavailable");
- }
+ component.unrelocatedAdapter()
+ .orElseThrow(() -> new IllegalStateException("Unrelocated adventure unavailable"))
+ .setComponent(unrelocated);
- adapter.setComponent(unrelocated);
return component;
}
}
diff --git a/bungee/build.gradle b/bungee/build.gradle
index e1c96e92..c03bb061 100644
--- a/bungee/build.gradle
+++ b/bungee/build.gradle
@@ -36,5 +36,6 @@ dependencies {
shadowJar {
archiveFileName = 'bungee.jarinjar'
- // Relocations in buildscript/relocations.gradle
+ relocate 'net.kyori', 'com.discordsrv.dependencies.net.kyori'
+ // More relocations in buildscript/relocations.gradle
}
diff --git a/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java b/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java
index 3a6c6674..9683fc2d 100644
--- a/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java
+++ b/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java
@@ -44,6 +44,15 @@ public class MinecraftComponentImpl implements MinecraftComponent {
setComponent(component);
}
+ public Component getComponent() {
+ return component;
+ }
+
+ public void setComponent(Component component) {
+ this.component = component;
+ this.json = GsonComponentSerializer.gson().serialize(component);
+ }
+
@Override
public @NotNull String asJson() {
return json;
@@ -66,43 +75,37 @@ public class MinecraftComponentImpl implements MinecraftComponent {
return PlainTextComponentSerializer.plainText().serialize(component);
}
- public Component getComponent() {
- return component;
- }
-
- public void setComponent(Component component) {
- this.component = component;
- this.json = GsonComponentSerializer.gson().serialize(component);
+ @Override
+ public MinecraftComponent.@NotNull Adapter adventureAdapter(
+ @NotNull Class> gsonSerializerClass, @NotNull Class componentClass
+ ) {
+ return new Adapter<>(gsonSerializerClass, componentClass);
}
@Override
- public @NotNull MinecraftComponent.Adapter adventureAdapter(@NotNull Class> gsonSerializerClass) {
- return new Adapter(gsonSerializerClass);
+ public MinecraftComponent.@NotNull Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter) {
+ return new Adapter<>(adapter);
}
- @Override
- public @NotNull MinecraftComponent.Adapter adventureAdapter(@NotNull MinecraftComponentAdapter adapter) {
- return new Adapter(adapter);
- }
+ @SuppressWarnings("unchecked")
+ public class Adapter implements MinecraftComponent.Adapter {
- public class Adapter implements MinecraftComponent.Adapter {
+ private final MinecraftComponentAdapter adapter;
- private final MinecraftComponentAdapter adapter;
-
- private Adapter(Class> gsonSerializerClass) {
- this(MinecraftComponentAdapter.create(gsonSerializerClass));
+ private Adapter(Class> gsonSerializerClass, Class componentClass) {
+ this(MinecraftComponentAdapter.create(gsonSerializerClass, componentClass));
}
- private Adapter(MinecraftComponentAdapter adapter) {
+ private Adapter(MinecraftComponentAdapter adapter) {
this.adapter = adapter;
}
@Override
- public @NotNull Object getComponent() {
+ public @NotNull T getComponent() {
try {
- return adapter.deserialize()
+ return (T) adapter.deserializeMethod()
.invoke(
- adapter.instance(),
+ adapter.serializerInstance(),
json
);
} catch (IllegalAccessException | InvocationTargetException e) {
@@ -113,13 +116,15 @@ public class MinecraftComponentImpl implements MinecraftComponent {
@Override
public void setComponent(@NotNull Object adventureComponent) {
try {
- json = (String) adapter.serialize()
- .invoke(
- adapter.instance(),
- adventureComponent
- );
+ setJson(
+ (String) adapter.serializeMethod()
+ .invoke(
+ adapter.serializerInstance(),
+ adventureComponent
+ )
+ );
} 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) {
throw new RuntimeException("Failed to convert from adventure component", e);
}
diff --git a/common/src/main/java/com/discordsrv/common/component/util/ComponentUtil.java b/common/src/main/java/com/discordsrv/common/component/util/ComponentUtil.java
index 525cc310..a5ebc3cb 100644
--- a/common/src/main/java/com/discordsrv/common/component/util/ComponentUtil.java
+++ b/common/src/main/java/com/discordsrv/common/component/util/ComponentUtil.java
@@ -34,7 +34,11 @@ import java.util.Collection;
*/
public final class ComponentUtil {
- public static final MinecraftComponentAdapter ADAPTER = MinecraftComponentAdapter.create(GsonComponentSerializer.class);
+ private static MinecraftComponentAdapter ADAPTER;
+
+ private static MinecraftComponentAdapter getAdapter() {
+ return ADAPTER != null ? ADAPTER : (ADAPTER = MinecraftComponentAdapter.create(GsonComponentSerializer.class, Component.class));
+ }
private ComponentUtil() {}
@@ -43,7 +47,7 @@ public final class ComponentUtil {
}
public static boolean isEmpty(MinecraftComponent component) {
- return isEmpty(fromAPI(component));
+ return component.asPlainString().isEmpty();
}
public static MinecraftComponent toAPI(Component component) {
@@ -54,12 +58,16 @@ public final class ComponentUtil {
if (component instanceof MinecraftComponentImpl) {
return ((MinecraftComponentImpl) component).getComponent();
} else {
- return (Component) component.adventureAdapter(ADAPTER).getComponent();
+ return component.adventureAdapter(getAdapter()).getComponent();
}
}
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) {
diff --git a/sponge/loader/src/main/java/com/discordsrv/sponge/loader/DiscordSRVSpongeLoader.java b/sponge/loader/src/main/java/com/discordsrv/sponge/loader/DiscordSRVSpongeLoader.java
index fe55c0dd..ed49c1aa 100644
--- a/sponge/loader/src/main/java/com/discordsrv/sponge/loader/DiscordSRVSpongeLoader.java
+++ b/sponge/loader/src/main/java/com/discordsrv/sponge/loader/DiscordSRVSpongeLoader.java
@@ -24,6 +24,7 @@ import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
import dev.vankka.mcdependencydownload.loader.ILoader;
import dev.vankka.mcdependencydownload.loader.exception.LoadingException;
import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.Game;
import org.spongepowered.api.config.ConfigDir;
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.builtin.jvm.Plugin;
+import java.io.IOException;
import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.file.Path;
+import java.util.Objects;
import java.util.Optional;
@Plugin("discordsrv")
@@ -46,6 +48,7 @@ public class DiscordSRVSpongeLoader implements ILoader {
private final PluginContainer pluginContainer;
private final Game game;
private final Path dataDirectory;
+ private final JarInJarClassLoader classLoader;
private ISpongeBootstrap bootstrap;
@Inject
@@ -60,10 +63,11 @@ public class DiscordSRVSpongeLoader implements ILoader {
logger.error("| DiscordSRV does not run on clients |");
logger.error("| DiscordSRV can only be installed on servers |");
logger.error("+---------------------------------------------+");
+ this.classLoader = null;
return;
}
- initialize();
+ this.classLoader = initialize();
}
private Optional bootstrap() {
@@ -71,14 +75,12 @@ public class DiscordSRVSpongeLoader implements ILoader {
}
@Override
- public String getBootstrapClassName() {
+ public @NotNull String getBootstrapClassName() {
return "com.discordsrv.sponge.DiscordSRVSpongeBootstrap";
}
@Override
- public void initiateBootstrap(Class> bootstrapClass, JarInJarClassLoader classLoader)
- throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
-
+ public void initiateBootstrap(Class> bootstrapClass, @NotNull JarInJarClassLoader classLoader) throws ReflectiveOperationException {
Constructor> constructor = bootstrapClass.getConstructor(PluginContainer.class, Game.class, JarInJarClassLoader.class, Path.class);
bootstrap = (ISpongeBootstrap) constructor.newInstance(pluginContainer, game, classLoader, dataDirectory);
}
@@ -89,18 +91,18 @@ public class DiscordSRVSpongeLoader implements ILoader {
}
@Override
- public String getName() {
+ public @NotNull String getName() {
return "DiscordSRV";
}
@Override
- public ClassLoader getParentClassLoader() {
+ public @NotNull ClassLoader getParentClassLoader() {
return getClass().getClassLoader();
}
@Override
- public URL getJarInJarResource() {
- return getParentClassLoader().getResource("sponge.jarinjar");
+ public @NotNull URL getJarInJarResource() {
+ return Objects.requireNonNull(getParentClassLoader().getResource("sponge.jarinjar"));
}
@Listener
@@ -121,5 +123,10 @@ public class DiscordSRVSpongeLoader implements ILoader {
@Listener
public void onStoppingEngine(StoppingEngineEvent> event) {
bootstrap().ifPresent(ISpongeBootstrap::onStopping);
+ try {
+ classLoader.close();
+ } catch (IOException e) {
+ pluginContainer.logger().error("Failed to close JarInJarClassLoader", e);
+ }
}
}