mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-22 11:55:54 +01:00
Make MinecraftComponent immutable
This commit is contained in:
parent
295bc1c6bf
commit
6f8b5bd023
@ -5,6 +5,9 @@ dependencies {
|
||||
// Annotations
|
||||
compileOnlyApi(libs.jetbrains.annotations)
|
||||
|
||||
// Unrelocate
|
||||
compileOnly(project(':common:common-unrelocate'))
|
||||
|
||||
// JDA
|
||||
api(libs.jda) {
|
||||
// We don't use audio
|
||||
|
@ -43,7 +43,7 @@ import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The DiscordSRV API.
|
||||
*
|
||||
* <p>
|
||||
* Use your platform's service provider or {@link #get()} / {@link #optional()} to get the instance.
|
||||
*/
|
||||
@SuppressWarnings("unused") // API
|
||||
@ -51,12 +51,17 @@ public interface DiscordSRVApi {
|
||||
|
||||
/**
|
||||
* Gets the instance of {@link DiscordSRVApi}.
|
||||
* @return the DiscordSRV api, or {@code null} if not available
|
||||
* @return the DiscordSRV api
|
||||
* @see #isAvailable()
|
||||
* @throws IllegalStateException if DiscordSRV has not been initialized yet
|
||||
*/
|
||||
@Nullable
|
||||
@NotNull
|
||||
static DiscordSRVApi get() {
|
||||
return InstanceHolder.API;
|
||||
DiscordSRVApi api = InstanceHolder.API;
|
||||
if (api == null) {
|
||||
throw new IllegalStateException("DiscordSRV has not been initialized yet");
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@
|
||||
package com.discordsrv.api.component;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.unrelocate.net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -31,7 +32,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* A Minecraft json text component. Use {@link DiscordSRVApi#componentFactory()} to get an instance.<br/>
|
||||
* <br/>
|
||||
* This is designed to work with Adventure, see {@link #adventureAdapter(Class, Class)} and {@link #adventureAdapter(MinecraftComponentAdapter)}
|
||||
* This is designed to work with Adventure, see {@link #asAdventure(Class, Class)} and {@link #asAdventure(MinecraftComponentAdapter)}
|
||||
* but is compatible with anything able to handle Minecraft's json format.
|
||||
* Legacy is <b>not supported</b>.
|
||||
*/
|
||||
@ -39,6 +40,34 @@ import org.jetbrains.annotations.Nullable;
|
||||
@ApiStatus.NonExtendable
|
||||
public interface MinecraftComponent {
|
||||
|
||||
/**
|
||||
* Creates a {@link MinecraftComponent} from the provided JSON.
|
||||
*
|
||||
* @param json the json
|
||||
* @return a new {@link MinecraftComponent}
|
||||
*/
|
||||
@NotNull
|
||||
static MinecraftComponent fromJson(@NotNull String json) {
|
||||
return DiscordSRVApi.get()
|
||||
.componentFactory()
|
||||
.fromJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link MinecraftComponent} from the given unrelocated Adventure Component.
|
||||
*
|
||||
* @param unrelocatedAdventureComponent the <b>unrelocated</b> adventure Component
|
||||
* @return a new {@link MinecraftComponent}
|
||||
*/
|
||||
@NotNull
|
||||
static MinecraftComponent fromAdventure(@NotNull Component unrelocatedAdventureComponent) {
|
||||
MinecraftComponentAdapter<Component> adapter = MinecraftComponentAdapter.unrelocated();
|
||||
if (adapter == null) {
|
||||
throw new IllegalStateException("Unrelocated Adventure GSON serializer not available");
|
||||
}
|
||||
return adapter.toDiscordSRV(unrelocatedAdventureComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this component as json.
|
||||
* @return json of this component
|
||||
@ -46,13 +75,6 @@ public interface MinecraftComponent {
|
||||
@NotNull
|
||||
String asJson();
|
||||
|
||||
/**
|
||||
* Sets this component from json.
|
||||
* @param json valid Minecraft message component json
|
||||
* @throws IllegalArgumentException if the provided json is not valid
|
||||
*/
|
||||
void setJson(@NotNull String json);
|
||||
|
||||
/**
|
||||
* Gets this message as a plain {@link String} losing any coloring, click and hover components it may have had.
|
||||
* Use for reference only, <b>this is not a substitute for legacy</b>.
|
||||
@ -62,72 +84,59 @@ public interface MinecraftComponent {
|
||||
String asPlainString();
|
||||
|
||||
/**
|
||||
* Creates an Adventure adapter for convenience.
|
||||
* Converts this {@link MinecraftComponent} to an Adventure Component of the provided GSON serializer.
|
||||
* Prefer using {@link #asAdventure(MinecraftComponentAdapter)}.
|
||||
*
|
||||
* @param gsonSerializerClass the gson serializer class
|
||||
* @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)
|
||||
* @see #asAdventure(Class, Class)
|
||||
*/
|
||||
@NotNull
|
||||
default Adapter<Object> adventureAdapter(@NotNull Class<?> gsonSerializerClass) {
|
||||
return adventureAdapter(gsonSerializerClass, null);
|
||||
default Object asAdventure(@NotNull Class<?> gsonSerializerClass) {
|
||||
return asAdventure(gsonSerializerClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Adventure adapter for convenience.
|
||||
* Converts this {@link MinecraftComponent} to an Adventure Component of the provided GSON serializer.
|
||||
* Prefer using {@link #asAdventure(MinecraftComponentAdapter)}.
|
||||
*
|
||||
* @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
|
||||
* @return the component from the GSON serializers output
|
||||
* @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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
<T> Adapter<T> adventureAdapter(@NotNull Class<?> gsonSerializerClass, Class<T> componentClass);
|
||||
default <T> T asAdventure(@NotNull Class<?> gsonSerializerClass, Class<T> componentClass) {
|
||||
return (T) MinecraftComponentAdapter.create(gsonSerializerClass).toAdventure(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Adventure adapter from a {@link MinecraftComponentAdapter} for convenience.
|
||||
*
|
||||
* @param adapter the pre-made {@link MinecraftComponentAdapter}
|
||||
* @return a {@link Adapter} for this component using the given {@link MinecraftComponentAdapter}
|
||||
* @return a new Adventure Component
|
||||
*/
|
||||
@NotNull
|
||||
<T> Adapter<T> adventureAdapter(@NotNull MinecraftComponentAdapter<T> adapter);
|
||||
default <T> T asAdventure(@NotNull MinecraftComponentAdapter<T> adapter) {
|
||||
return adapter.toAdventure(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Adventure adapter for the unrelocated adventure.
|
||||
*
|
||||
* @return a {@link Adapter} for this component using the unrelocated adventure, {@code null} if not available
|
||||
* @return the <b>unrelocated</b> Adventure Component, {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
default Adapter<Object> unrelocatedAdapter() {
|
||||
MinecraftComponentAdapter<Object> adapter = MinecraftComponentAdapter.UNRELOCATED;
|
||||
default Component asAdventure() {
|
||||
MinecraftComponentAdapter<Component> adapter = MinecraftComponentAdapter.unrelocated();
|
||||
if (adapter == null) {
|
||||
return null;
|
||||
}
|
||||
return adventureAdapter(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* An Adventure adapter, converts from/to given adventure components from/to json.
|
||||
*/
|
||||
interface Adapter<Component> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
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 Component adventureComponent);
|
||||
return asAdventure(adapter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,97 +23,66 @@
|
||||
|
||||
package com.discordsrv.api.component;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A persistent Adventure adapter for {@link MinecraftComponent}s, this is more efficient than using {@link MinecraftComponent#adventureAdapter(Class)}.
|
||||
* @see MinecraftComponent#adventureAdapter(MinecraftComponentAdapter)
|
||||
* A helper class to make {@link MinecraftComponent}s using Adventure.
|
||||
* @param <Component> the Adventure Component type, relocated or unrelocated
|
||||
*/
|
||||
public class MinecraftComponentAdapter<Component> {
|
||||
|
||||
public static final MinecraftComponentAdapter<Object> UNRELOCATED;
|
||||
|
||||
static {
|
||||
MinecraftComponentAdapter<Object> unrelocated = null;
|
||||
try {
|
||||
unrelocated = MinecraftComponentAdapter.create(
|
||||
Class.forName("net.ky".concat("ori.adventure.text.serializer.gson.GsonComponentSerializer"))
|
||||
);
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
UNRELOCATED = unrelocated;
|
||||
}
|
||||
public interface MinecraftComponentAdapter<Component> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link MinecraftComponentAdapter} that can be used with {@link MinecraftComponent}s.
|
||||
* Create a new {@link MinecraftComponentAdapter} for the given GSONComponentSerializer class.
|
||||
*
|
||||
* @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
|
||||
* @param gsonSerializerClass the serializer class
|
||||
* @return a new {@link MinecraftComponentAdapter}
|
||||
* @param <Component> the type of Adventure Component the serializer handles
|
||||
*/
|
||||
public static MinecraftComponentAdapter<Object> create(Class<?> gsonSerializerClass) {
|
||||
return new MinecraftComponentAdapter<>(gsonSerializerClass, null);
|
||||
@NotNull
|
||||
static <Component> MinecraftComponentAdapter<Component> create(Class<?> gsonSerializerClass) {
|
||||
return create(gsonSerializerClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MinecraftComponentAdapter} that can be used with {@link MinecraftComponent}s.
|
||||
* Create a new {@link MinecraftComponentAdapter} for the given GSONComponentSerializer class.
|
||||
*
|
||||
* @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
|
||||
* @param gsonSerializerClass the serializer class
|
||||
* @param componentClass the {@code Component} class that's returned by the given gson component serializer
|
||||
* @return a new {@link MinecraftComponentAdapter}
|
||||
* @param <T> the type of Adventure Component the serializer returns
|
||||
* @throws IllegalArgumentException if the provided componentClass does not match the gsonSerializerClasses Component
|
||||
*/
|
||||
public static <Component> MinecraftComponentAdapter<Component> create(Class<?> gsonSerializerClass, Class<Component> componentClass) {
|
||||
return new MinecraftComponentAdapter<>(gsonSerializerClass, componentClass);
|
||||
@NotNull
|
||||
static <T> MinecraftComponentAdapter<T> create(Class<?> gsonSerializerClass, Class<T> componentClass) {
|
||||
return DiscordSRVApi.get()
|
||||
.componentFactory()
|
||||
.makeAdapter(gsonSerializerClass, componentClass);
|
||||
}
|
||||
|
||||
private final Class<?> gsonSerializerClass;
|
||||
private final Object instance;
|
||||
private final Method deserialize;
|
||||
private final Method serialize;
|
||||
|
||||
private MinecraftComponentAdapter(Class<?> gsonSerializerClass, Class<Component> 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);
|
||||
}
|
||||
/**
|
||||
* Create a new {@link MinecraftComponentAdapter} for unrelocated Adventure.
|
||||
*
|
||||
* @return the shared instance of {@link MinecraftComponentAdapter} unrelocated Adventure, or {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
static MinecraftComponentAdapter<com.discordsrv.unrelocate.net.kyori.adventure.text.Component> unrelocated() {
|
||||
return (MinecraftComponentAdapter<com.discordsrv.unrelocate.net.kyori.adventure.text.Component>) (Object) MinecraftComponentAdapterUnrelocated.INSTANCE;
|
||||
}
|
||||
|
||||
private static void checkComponentClass(Class<?> provided, Class<?> actual) {
|
||||
if (provided == null) {
|
||||
// Ignore null
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Converts a {@link MinecraftComponent} to an Adventure Component.
|
||||
* @param component the {@link MinecraftComponent}
|
||||
* @return a new Adventure Component
|
||||
*/
|
||||
Component toAdventure(MinecraftComponent component);
|
||||
|
||||
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 serializerInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Method deserializeMethod() {
|
||||
return deserialize;
|
||||
}
|
||||
|
||||
public Method serializeMethod() {
|
||||
return serialize;
|
||||
}
|
||||
/**
|
||||
* Converts an Adventure Component into a {@link MinecraftComponent}.
|
||||
* @param component the Adventure Component
|
||||
* @return a new {@link MinecraftComponent}
|
||||
*/
|
||||
MinecraftComponent toDiscordSRV(Component component);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ package com.discordsrv.api.component;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A factory for creating {@link MinecraftComponent}s.
|
||||
@ -33,12 +34,21 @@ import org.jetbrains.annotations.NotNull;
|
||||
public interface MinecraftComponentFactory {
|
||||
|
||||
/**
|
||||
* Creates an empty {@link MinecraftComponent}.
|
||||
* Creates an {@link MinecraftComponent} from JSON.
|
||||
*
|
||||
* @param json the Minecraft component json
|
||||
* @return a new {@link MinecraftComponent}
|
||||
*/
|
||||
@NotNull
|
||||
MinecraftComponent empty();
|
||||
MinecraftComponent fromJson(@NotNull String json);
|
||||
|
||||
/**
|
||||
* Create an {@link MinecraftComponentAdapter} for the given GSONComponentSerializer class.
|
||||
* @param gsonSerializerClass the serializer class
|
||||
* @return a new {@link MinecraftComponentAdapter}
|
||||
*/
|
||||
@NotNull
|
||||
<T> MinecraftComponentAdapter<T> makeAdapter(Class<?> gsonSerializerClass, @Nullable Class<T> componentClass);
|
||||
|
||||
/**
|
||||
* Creates a EnhancedLegacyText {@link GameTextBuilder} based on the given input.
|
||||
|
@ -355,9 +355,6 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
@Override
|
||||
public @NotNull Formatter applyPlaceholderService() {
|
||||
DiscordSRVApi api = DiscordSRVApi.get();
|
||||
if (api == null) {
|
||||
throw new IllegalStateException("DiscordSRVApi not available");
|
||||
}
|
||||
this.replacements.put(
|
||||
PlaceholderService.PATTERN,
|
||||
wrapFunction(matcher -> api.placeholderService().getResultAsCharSequence(matcher, context))
|
||||
@ -384,9 +381,6 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
@Override
|
||||
public @NotNull SendableDiscordMessage build() {
|
||||
DiscordSRVApi api = DiscordSRVApi.get();
|
||||
if (api == null) {
|
||||
throw new IllegalStateException("DiscordSRVApi not available");
|
||||
}
|
||||
|
||||
Function<String, String> placeholders = input -> {
|
||||
if (input == null) {
|
||||
|
@ -67,9 +67,6 @@ public interface Module {
|
||||
@NotNull
|
||||
default Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
DiscordSRVApi api = DiscordSRVApi.get();
|
||||
if (api == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Collection<? extends EventListener> listeners = api.eventBus().getListeners(this);
|
||||
EnumSet<DiscordGatewayIntent> intents = EnumSet.noneOf(DiscordGatewayIntent.class);
|
||||
|
@ -66,7 +66,7 @@ public class PaperComponentHandle<T> {
|
||||
unrelocated = handle.invoke(target);
|
||||
} catch (Throwable ignored) {}
|
||||
if (unrelocated != null) {
|
||||
return ComponentUtil.fromUnrelocated((com.discordsrv.unrelocate.net.kyori.adventure.text.Component) unrelocated);
|
||||
return MinecraftComponent.fromAdventure((com.discordsrv.unrelocate.net.kyori.adventure.text.Component) unrelocated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
package com.discordsrv.bukkit.console.executor;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.unrelocate.net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -43,7 +42,6 @@ public class PaperCommandFeedbackExecutor implements Consumer<Component> {
|
||||
|
||||
@Override
|
||||
public void accept(Component component) {
|
||||
MinecraftComponent minecraftComponent = ComponentUtil.fromUnrelocated(component);
|
||||
componentConsumer.accept(minecraftComponent);
|
||||
componentConsumer.accept(MinecraftComponent.fromAdventure(component));
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ public final class PaperPlayer {
|
||||
|
||||
public static void kick(Player player, Component reason) {
|
||||
try {
|
||||
KICK_COMPONENT_HANDLE.invokeExact(player, ComponentUtil.toUnrelocated(ComponentUtil.toAPI(reason)));
|
||||
KICK_COMPONENT_HANDLE.invokeExact(player, ComponentUtil.toAPI(reason).asAdventure());
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to kick player", e);
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ public class BukkitPlayer extends BukkitCommandSender implements IPlayer {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PAPER_SEND_MESSAGE_HANDLE.invoke(player, ComponentUtil.toUnrelocated(component));
|
||||
PAPER_SEND_MESSAGE_HANDLE.invoke(player, component.asAdventure());
|
||||
} catch (Throwable ignored) {
|
||||
super.sendMessage(ComponentUtil.fromAPI(component));
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package com.discordsrv.common.component;
|
||||
|
||||
import com.discordsrv.api.component.GameTextBuilder;
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.component.MinecraftComponentAdapter;
|
||||
import com.discordsrv.api.component.MinecraftComponentFactory;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.renderer.DiscordSRVMinecraftRenderer;
|
||||
@ -40,6 +41,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.kyori.ansi.ColorLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -129,8 +131,13 @@ public class ComponentFactory implements MinecraftComponentFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MinecraftComponent empty() {
|
||||
return MinecraftComponentImpl.empty();
|
||||
public @NotNull MinecraftComponent fromJson(@NotNull String json) {
|
||||
return new MinecraftComponentImpl(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @NotNull MinecraftComponentAdapter<T> makeAdapter(Class<?> gsonSerializerClass, @Nullable Class<T> componentClass) {
|
||||
return new MinecraftComponentAdapterImpl<>(gsonSerializerClass, componentClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of DiscordSRV, licensed under the GPLv3 License
|
||||
* Copyright (c) 2016-2024 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.component;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.component.MinecraftComponentAdapter;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MinecraftComponentAdapterImpl<T> implements MinecraftComponentAdapter<T> {
|
||||
|
||||
private final Class<?> gsonSerializerClass;
|
||||
private final Object instance;
|
||||
private final Method deserialize;
|
||||
private final Method serialize;
|
||||
|
||||
public MinecraftComponentAdapterImpl(Class<?> gsonSerializerClass, Class<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R execute(Method method, Object input) {
|
||||
try {
|
||||
return (R) method.invoke(instance, input);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Failed to invoke " + gsonSerializerClass.getName() + "." + method.getName(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T toAdventure(MinecraftComponent component) {
|
||||
String json = component.asJson();
|
||||
return execute(deserialize, json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftComponent toDiscordSRV(T o) {
|
||||
String json = execute(serialize, o);
|
||||
return MinecraftComponent.fromJson(json);
|
||||
}
|
||||
}
|
@ -19,116 +19,38 @@
|
||||
package com.discordsrv.common.component;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.component.MinecraftComponentAdapter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class MinecraftComponentImpl implements MinecraftComponent {
|
||||
|
||||
private String json;
|
||||
private Component component;
|
||||
|
||||
public static MinecraftComponentImpl empty() {
|
||||
return new MinecraftComponentImpl("{text:\"\"}");
|
||||
}
|
||||
private final String json;
|
||||
private final Component component;
|
||||
|
||||
public MinecraftComponentImpl(String json) {
|
||||
setJson(json);
|
||||
this(GsonComponentSerializer.gson().deserialize(json));
|
||||
}
|
||||
|
||||
public MinecraftComponentImpl(@NotNull Component component) {
|
||||
setComponent(component);
|
||||
this.component = component;
|
||||
this.json = GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
public Component getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(@NotNull Component component) {
|
||||
this.component = component;
|
||||
this.json = GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String asJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJson(@NotNull String json) throws IllegalArgumentException {
|
||||
Component component;
|
||||
try {
|
||||
component = GsonComponentSerializer.gson().deserialize(json);
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalArgumentException("Provided json is not valid: " + json, t);
|
||||
}
|
||||
this.component = component;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String asPlainString() {
|
||||
return PlainTextComponentSerializer.plainText().serialize(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MinecraftComponent.@NotNull Adapter<T> adventureAdapter(
|
||||
@NotNull Class<?> gsonSerializerClass, @NotNull Class<T> componentClass
|
||||
) {
|
||||
return new Adapter<>(gsonSerializerClass, componentClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MinecraftComponent.@NotNull Adapter<T> adventureAdapter(@NotNull MinecraftComponentAdapter<T> adapter) {
|
||||
return new Adapter<>(adapter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Adapter<T> implements MinecraftComponent.Adapter<T> {
|
||||
|
||||
private final MinecraftComponentAdapter<T> adapter;
|
||||
|
||||
private Adapter(Class<?> gsonSerializerClass, Class<T> componentClass) {
|
||||
this(MinecraftComponentAdapter.create(gsonSerializerClass, componentClass));
|
||||
}
|
||||
|
||||
private Adapter(MinecraftComponentAdapter<T> adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull T getComponent() {
|
||||
try {
|
||||
return (T) adapter.deserializeMethod()
|
||||
.invoke(
|
||||
adapter.serializerInstance(),
|
||||
json
|
||||
);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("Failed to convert to adventure component", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComponent(@NotNull Object adventureComponent) {
|
||||
try {
|
||||
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.serializerClass().getName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to convert from adventure component", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,30 +22,26 @@ import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.component.MinecraftComponentAdapter;
|
||||
import com.discordsrv.common.component.MinecraftComponentImpl;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An util class for {@link Component}s and {@link MinecraftComponent}s.
|
||||
* A util class for {@link Component}s and {@link MinecraftComponent}s.
|
||||
*/
|
||||
public final class ComponentUtil {
|
||||
|
||||
private ComponentUtil() {}
|
||||
|
||||
private static MinecraftComponentAdapter<Component> ADAPTER;
|
||||
|
||||
@NotNull
|
||||
private static MinecraftComponentAdapter<Component> getAdapter() {
|
||||
private static MinecraftComponentAdapter<Component> adapter() {
|
||||
return ADAPTER != null ? ADAPTER : (ADAPTER = MinecraftComponentAdapter.create(GsonComponentSerializer.class, Component.class));
|
||||
}
|
||||
|
||||
private ComponentUtil() {}
|
||||
|
||||
public static boolean isEmpty(@NotNull Component component) {
|
||||
return PlainTextComponentSerializer.plainText().serialize(component).isEmpty();
|
||||
}
|
||||
@ -78,48 +74,7 @@ public final class ComponentUtil {
|
||||
if (component instanceof MinecraftComponentImpl) {
|
||||
return ((MinecraftComponentImpl) component).getComponent();
|
||||
} else {
|
||||
return component.adventureAdapter(getAdapter()).getComponent();
|
||||
return component.asAdventure(adapter());
|
||||
}
|
||||
}
|
||||
|
||||
public static void set(MinecraftComponent minecraftComponent, Component component) {
|
||||
if (component instanceof MinecraftComponentImpl) {
|
||||
((MinecraftComponentImpl) component).setComponent(component);
|
||||
} else {
|
||||
minecraftComponent.adventureAdapter(getAdapter()).setComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
public static MinecraftComponent fromUnrelocated(@NotNull com.discordsrv.unrelocate.net.kyori.adventure.text.Component unrelocatedAdventure) {
|
||||
MinecraftComponentImpl component = MinecraftComponentImpl.empty();
|
||||
MinecraftComponent.Adapter<Object> adapter = component.unrelocatedAdapter();
|
||||
if (adapter == null) {
|
||||
throw new IllegalStateException("Could not get unrelocated adventure gson serializer");
|
||||
}
|
||||
adapter.setComponent(unrelocatedAdventure);
|
||||
return component;
|
||||
}
|
||||
|
||||
public static com.discordsrv.unrelocate.net.kyori.adventure.text.Component toUnrelocated(MinecraftComponent component) {
|
||||
MinecraftComponent.Adapter<Object> adapter = component.unrelocatedAdapter();
|
||||
if (adapter == null) {
|
||||
throw new IllegalStateException("Could not get unrelocated adventure gson serializer");
|
||||
}
|
||||
return (com.discordsrv.unrelocate.net.kyori.adventure.text.Component) adapter.getComponent();
|
||||
}
|
||||
|
||||
public static Component join(@NotNull Component delimiter, @NotNull Collection<? extends ComponentLike> components) {
|
||||
return join(delimiter, components.toArray(new ComponentLike[0]));
|
||||
}
|
||||
|
||||
public static Component join(@NotNull Component delimiter, @NotNull ComponentLike[] components) {
|
||||
TextComponent.Builder builder = Component.text();
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
builder.append(components[i]);
|
||||
if (i < components.length - 1) {
|
||||
builder.append(delimiter);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,7 @@ public class MinecraftMessage {
|
||||
}
|
||||
|
||||
public GameTextBuilder textBuilder() {
|
||||
DiscordSRVApi discordSRV = DiscordSRVApi.get();
|
||||
if (discordSRV == null) {
|
||||
throw new IllegalStateException("DiscordSRVApi == null");
|
||||
}
|
||||
return discordSRV.componentFactory().textBuilder(rawFormat);
|
||||
return DiscordSRVApi.get().componentFactory().textBuilder(rawFormat);
|
||||
}
|
||||
|
||||
public MinecraftComponent make() {
|
||||
|
@ -27,10 +27,10 @@ import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -159,7 +159,7 @@ public class DiscordGuildMemberImpl implements DiscordGuildMember {
|
||||
components.add(Component.text(role.getName()).color(TextColor.color(role.getColor().rgb())));
|
||||
}
|
||||
|
||||
return ComponentUtil.join(Component.text(suffix), components);
|
||||
return Component.join(JoinConfiguration.separator(Component.text(suffix)), components);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,6 +42,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.WebhookClient;
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@ -307,7 +308,7 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
|
||||
));
|
||||
}
|
||||
|
||||
return ComponentUtil.join(Component.text(suffix), components);
|
||||
return Component.join(JoinConfiguration.separator(Component.text(suffix)), components);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user