More work on Discord -> Minecraft messages

This commit is contained in:
Vankka 2021-10-18 02:35:53 +03:00
parent 419391ddbb
commit 8accdbbdcd
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
28 changed files with 277 additions and 56 deletions

View File

@ -28,6 +28,7 @@ import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.api.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.api.entity.DiscordUser;
import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
@ -76,4 +77,12 @@ public interface DiscordAPI {
*/
@NotNull
Optional<DiscordUser> getUserById(long id);
/**
* Gets a Discord role by id, the provided entity can be cached and will not update if it changes on Discord.
* @param id the id for the Discord role
* @return the Discord role
*/
@NotNull
Optional<DiscordRole> getRoleById(long id);
}

View File

@ -23,7 +23,7 @@
package com.discordsrv.api.discord.api.entity;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import org.jetbrains.annotations.NotNull;
/**

View File

@ -24,7 +24,7 @@
package com.discordsrv.api.discord.api.entity.guild;
import com.discordsrv.api.discord.api.entity.Snowflake;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import java.util.Optional;

View File

@ -25,7 +25,7 @@ package com.discordsrv.api.discord.api.entity.guild;
import com.discordsrv.api.color.Color;
import com.discordsrv.api.discord.api.entity.DiscordUser;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import org.jetbrains.annotations.NotNull;
import java.util.List;

View File

@ -25,7 +25,7 @@ package com.discordsrv.api.discord.api.entity.guild;
import com.discordsrv.api.color.Color;
import com.discordsrv.api.discord.api.entity.Snowflake;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import org.jetbrains.annotations.NotNull;
/**
@ -59,6 +59,7 @@ public interface DiscordRole extends Snowflake {
* @return the color of this role, or {@link #DEFAULT_COLOR} if there is no color set
* @see #hasColor()
*/
@NotNull
@Placeholder("role_color")
Color getColor();

View File

@ -21,7 +21,9 @@
* SOFTWARE.
*/
package com.discordsrv.api.placeholder;
package com.discordsrv.api.placeholder.annotation;
import com.discordsrv.api.placeholder.PlaceholderService;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -0,0 +1,34 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.placeholder.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface PlaceholderRemainder {
}

View File

@ -23,7 +23,7 @@
package com.discordsrv.api.player;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;

View File

@ -18,7 +18,8 @@ shadowJar {
'org.apache.commons',
'org.spongepowered.configurate',
'org.yaml.snakeyaml',
'net.kyori'
'net.kyori',
'dev.vankka.enhancedlegacytext',
].each {
relocate it, 'com.discordsrv.dependencies.' + it
}

View File

@ -246,7 +246,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
discordConnectionManager.connect().join();
// Placeholder result stringifiers
placeholderService().addResultStringifier(new ComponentResultStringifier());
placeholderService().addResultStringifier(new ComponentResultStringifier(this));
// Register PlayerProvider listeners
playerProvider().subscribe();

View File

@ -22,14 +22,26 @@ import com.discordsrv.api.component.EnhancedTextBuilder;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.component.MinecraftComponentFactory;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.renderer.DiscordSRVMinecraftRenderer;
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializerOptions;
import dev.vankka.mcdiscordreserializer.minecraft.MinecraftSerializer;
import dev.vankka.mcdiscordreserializer.minecraft.MinecraftSerializerOptions;
import org.jetbrains.annotations.NotNull;
public class ComponentFactory implements MinecraftComponentFactory {
private final DiscordSRV discordSRV;
private final MinecraftSerializer minecraftSerializer;
private final DiscordSerializer discordSerializer;
public ComponentFactory(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.minecraftSerializer = new MinecraftSerializer(
MinecraftSerializerOptions.defaults().addRenderer(new DiscordSRVMinecraftRenderer(discordSRV)),
MinecraftSerializerOptions.escapeDefaults()
);
this.discordSerializer = new DiscordSerializer(DiscordSerializerOptions.defaults());
}
@Override
@ -41,4 +53,12 @@ public class ComponentFactory implements MinecraftComponentFactory {
public EnhancedTextBuilder enhancedBuilder(String content) {
return new EnhancedTextBuilderImpl(discordSRV, content);
}
public MinecraftSerializer minecraftSerializer() {
return minecraftSerializer;
}
public DiscordSerializer discordSerializer() {
return discordSerializer;
}
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2021 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.renderer;
import com.discordsrv.api.discord.api.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
import com.discordsrv.common.DiscordSRV;
import dev.vankka.mcdiscordreserializer.renderer.implementation.DefaultMinecraftRenderer;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.AbstractChannel;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
private static final ThreadLocal<Long> GUILD_CONTEXT = new ThreadLocal<>();
private final DiscordSRV discordSRV;
public DiscordSRVMinecraftRenderer(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
public static void inGuildContext(long guildId, Runnable runnable) {
GUILD_CONTEXT.set(guildId);
runnable.run();
GUILD_CONTEXT.set(0L);
}
@Override
public @Nullable Component appendChannelMention(@NonNull Component component, @NonNull String id) {
return component.append(Component.text(
discordSRV.jda()
.map(jda -> jda.getGuildChannelById(id))
.map(AbstractChannel::getName)
.map(name -> "#" + name)
.orElse("<#" + id + ">")
));
}
@Override
public @Nullable Component appendUserMention(@NonNull Component component, @NonNull String id) {
long guildId = GUILD_CONTEXT.get();
Optional<DiscordGuild> guild = guildId > 0
? discordSRV.discordAPI().getGuildById(guildId)
: Optional.empty();
long userId = MiscUtil.parseLong(id);
return component.append(Component.text(
guild.flatMap(g -> g.getMemberById(userId))
.map(member -> "@" + member.getEffectiveName())
.orElseGet(() -> discordSRV.discordAPI()
.getUserById(userId)
.map(user -> "@" + user.getUsername())
.orElse("<@" + id + ">"))
));
}
@Override
public @Nullable Component appendRoleMention(@NonNull Component component, @NonNull String id) {
return component.append(Component.text(
discordSRV.discordAPI()
.getRoleById(MiscUtil.parseLong(id))
.map(DiscordRole::getName)
.map(name -> "@" + name)
.orElse("<@" + id + ">")
));
}
}

View File

@ -25,7 +25,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
/**
* A util class for {@link Component}s and {@link MinecraftComponent}s.
* An util class for {@link Component}s and {@link MinecraftComponent}s.
*/
public final class ComponentUtil {

View File

@ -38,7 +38,7 @@ public @interface Untranslated {
enum Type {
/**
* The option's value and it's comment will be undocumented.
* The option's value, and it's comment will be undocumented.
*/
FULL(true, true),

View File

@ -23,5 +23,5 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class DiscordToMinecraftChatConfig {
public String format = "%user_name%: %message%";
public String format = "[&#5865F2Discord&r] [hover:show_text:Tag: %user_tag%&r\nRoles: %user_roles_, %]%user_color%%user_effective_name%&r » %message%";
}

View File

@ -26,6 +26,7 @@ import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel;
import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.api.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
import com.discordsrv.api.discord.api.exception.NotReadyException;
import com.discordsrv.api.discord.api.exception.UnknownChannelException;
import com.discordsrv.common.DiscordSRV;
@ -34,6 +35,7 @@ import com.discordsrv.common.config.main.channels.ChannelConfig;
import com.discordsrv.common.discord.api.channel.DiscordDMChannelImpl;
import com.discordsrv.common.discord.api.channel.DiscordTextChannelImpl;
import com.discordsrv.common.discord.api.guild.DiscordGuildImpl;
import com.discordsrv.common.discord.api.guild.DiscordRoleImpl;
import com.discordsrv.common.discord.api.user.DiscordUserImpl;
import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
@ -116,6 +118,13 @@ public class DiscordAPIImpl implements DiscordAPI {
.map(DiscordUserImpl::new);
}
@Override
public @NotNull Optional<DiscordRole> getRoleById(long id) {
return discordSRV.jda()
.map(jda -> jda.getRoleById(id))
.map(DiscordRoleImpl::new);
}
private class WebhookCacheLoader implements AsyncCacheLoader<Long, WebhookClient> {
@Override

View File

@ -22,11 +22,15 @@ import com.discordsrv.api.color.Color;
import com.discordsrv.api.discord.api.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.api.entity.guild.DiscordGuildMember;
import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.discord.api.user.DiscordUserImpl;
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.TextComponent;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@ -74,12 +78,12 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu
}
@Placeholder(value = "user_highest_role", relookup = "role")
public DiscordRole highestRole() {
public DiscordRole _highestRole() {
return !roles.isEmpty() ? roles.get(0) : null;
}
@Placeholder(value = "user_hoisted_role", relookup = "role")
public DiscordRole hoistedRole() {
public DiscordRole _hoistedRole() {
for (DiscordRole role : roles) {
if (role.isHoisted()) {
return role;
@ -87,4 +91,27 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu
}
return null;
}
@Placeholder(value = "user_roles")
public Component _allRoles(@PlaceholderRemainder String suffix) {
if (suffix.startsWith("_")) {
suffix = suffix.substring(1);
} else {
return null;
}
List<Component> components = new ArrayList<>();
for (DiscordRole role : getRoles()) {
components.add(Component.text(role.getName()).color(TextColor.color(role.getColor().rgb())));
}
TextComponent.Builder builder = Component.text();
for (int i = 0; i < components.size(); i++) {
builder.append(components.get(i));
if (i < components.size() - 1) {
builder.append(Component.text(suffix));
}
}
return builder.build();
}
}

View File

@ -48,7 +48,7 @@ public class DiscordRoleImpl implements DiscordRole {
}
@Override
public Color getColor() {
public @NotNull Color getColor() {
return color;
}

View File

@ -50,6 +50,7 @@ import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.exceptions.RateLimitedException;
import net.dv8tion.jda.api.requests.*;
import net.dv8tion.jda.api.utils.AllowedMentions;
import net.dv8tion.jda.api.utils.ChunkingFilter;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import net.dv8tion.jda.internal.entities.ReceivedMessage;
import net.dv8tion.jda.internal.hooks.EventManagerProxy;
@ -224,6 +225,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
@SuppressWarnings("BusyWait")
private void connectInternal() {
discordSRV.discordConnectionDetails().requestGatewayIntent(GatewayIntent.GUILD_MESSAGES); // TODO: figure out how DiscordSRV required intents are going to work
discordSRV.discordConnectionDetails().requestGatewayIntent(GatewayIntent.GUILD_MEMBERS); // TODO: figure out how DiscordSRV required intents are going to work
detailsAccepted = false;
ConnectionConfig.Bot botConfig = discordSRV.connectionConfig().bot;
@ -235,6 +237,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
JDABuilder jdaBuilder = JDABuilder.createLight(botConfig.token, intents);
jdaBuilder.enableCache(connectionDetails.getCacheFlags());
jdaBuilder.setMemberCachePolicy(membersIntent ? MemberCachePolicy.ALL : MemberCachePolicy.OWNER);
jdaBuilder.setChunkingFilter(membersIntent ? ChunkingFilter.ALL : ChunkingFilter.NONE);
jdaBuilder.setEventManager(new EventManagerProxy(new JDAEventManager(discordSRV), discordSRV.scheduler().forkExecutor()));

View File

@ -26,10 +26,10 @@ import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.discord.DiscordMessageReceivedEvent;
import com.discordsrv.api.event.events.message.receive.discord.DiscordMessageProcessingEvent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.renderer.DiscordSRVMinecraftRenderer;
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.discordtominecraft.DiscordToMinecraftChatConfig;
import com.discordsrv.common.function.OrDefault;
import dev.vankka.mcdiscordreserializer.minecraft.MinecraftSerializer;
import net.kyori.adventure.text.Component;
import org.apache.commons.lang3.tuple.Pair;
@ -69,20 +69,22 @@ public class DiscordChatListener extends AbstractListener {
OrDefault<? extends BaseChannelConfig> channelConfig = channelPair.map(Pair::getValue);
OrDefault<DiscordToMinecraftChatConfig> chatConfig = channelConfig.map(cfg -> cfg.discordToMinecraft);
String format = chatConfig.get(cfg -> cfg.format);
String format = chatConfig.get(cfg -> cfg.format.replace("\\n", "\n"));
if (format == null) {
return;
}
ReceivedDiscordMessage discordMessage = event.getDiscordMessage();
Component message = MinecraftSerializer.INSTANCE.serialize(event.getMessageContent());
DiscordSRVMinecraftRenderer.inGuildContext(channel.getGuild().getId(), () -> {
Component message = discordSRV.componentFactory().minecraftSerializer().serialize(event.getMessageContent());
EnhancedTextBuilder componentBuilder = discordSRV.componentFactory()
.enhancedBuilder(format)
.addContext(discordMessage, discordMessage.getAuthor())
.addReplacement("%message%", message);
discordMessage.getMember().ifPresent(componentBuilder::addContext);
EnhancedTextBuilder componentBuilder = discordSRV.componentFactory()
.enhancedBuilder(format)
.addContext(discordMessage, discordMessage.getAuthor())
.addReplacement("%message%", message);
discordMessage.getMember().ifPresent(componentBuilder::addContext);
gameChannel.sendMessage(componentBuilder.build());
gameChannel.sendMessage(componentBuilder.build());
});
}
}

View File

@ -23,8 +23,8 @@ import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.receive.game.ChatMessageProcessingEvent;
import com.discordsrv.api.event.events.message.forward.game.ChatMessageForwardedEvent;
import com.discordsrv.api.event.events.message.receive.game.ChatMessageProcessingEvent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
@ -32,7 +32,6 @@ import com.discordsrv.common.config.main.channels.ChannelConfig;
import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig;
import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageClusterImpl;
import com.discordsrv.common.function.OrDefault;
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
import net.kyori.adventure.text.Component;
import java.util.ArrayList;
@ -62,7 +61,7 @@ public class GameChatListener extends AbstractListener {
}
Component message = ComponentUtil.fromAPI(event.message());
String serializedMessage = DiscordSerializer.INSTANCE.serialize(message);
String serializedMessage = discordSRV.componentFactory().discordSerializer().serialize(message);
SendableDiscordMessage discordMessage = builder.toFormatter()
.addContext(event.getPlayer())

View File

@ -20,14 +20,20 @@ package com.discordsrv.common.placeholder;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.placeholder.PlaceholderResultStringifier;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.jetbrains.annotations.NotNull;
public class ComponentResultStringifier implements PlaceholderResultStringifier {
private final DiscordSRV discordSRV;
public ComponentResultStringifier(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
@Override
public String convertPlaceholderResult(@NotNull Object result) {
if (result instanceof MinecraftComponent) {
@ -36,11 +42,9 @@ public class ComponentResultStringifier implements PlaceholderResultStringifier
if (result instanceof Component) {
Component component = (Component) result;
if (PLAIN_COMPONENT_CONTEXT.get()) {
return PlainTextComponentSerializer.plainText()
.serialize(component);
return PlainTextComponentSerializer.plainText().serialize(component);
} else {
return DiscordSerializer.INSTANCE
.serialize(component);
return discordSRV.componentFactory().discordSerializer().serialize(component);
}
}
return null;

View File

@ -19,7 +19,7 @@
package com.discordsrv.common.placeholder;
import com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
import com.discordsrv.api.placeholder.PlaceholderResultStringifier;
import com.discordsrv.api.placeholder.PlaceholderService;

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.placeholder.provider;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
import com.discordsrv.common.placeholder.provider.util.PlaceholderMethodUtil;
import org.jetbrains.annotations.NotNull;
@ -71,13 +71,15 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
}
}
String remainder = placeholder.replace(annotationPlaceholder, "");
Object result;
try {
if (field != null) {
result = field.get(instance);
} else {
assert method != null;
result = PlaceholderMethodUtil.lookup(method, instance, context);
result = PlaceholderMethodUtil.lookup(method, instance, context, remainder);
}
} catch (Throwable e) {
e.printStackTrace();
@ -92,7 +94,7 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
Set<Object> newContext = new HashSet<>(context);
newContext.add(result);
String newPlaceholder = placeholder.replace(annotationPlaceholder, reLookup);
String newPlaceholder = reLookup + remainder;
return PlaceholderLookupResult.newLookup(newPlaceholder, newContext);
}

View File

@ -19,12 +19,13 @@
package com.discordsrv.common.placeholder.provider;
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* A placeholder provider used internally by DiscordSRV for {@link com.discordsrv.api.placeholder.Placeholder}.
* A placeholder provider used internally by DiscordSRV for {@link Placeholder}.
* API users should use the {@link com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent} instead.
*/
public interface PlaceholderProvider {

View File

@ -18,39 +18,59 @@
package com.discordsrv.common.placeholder.provider.util;
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Set;
import java.util.function.BiConsumer;
public final class PlaceholderMethodUtil {
private PlaceholderMethodUtil() {}
public static Object lookup(Method method, Object instance, Set<Object> context)
public static Object lookup(Method method, Object instance, Set<Object> context, String remainder)
throws InvocationTargetException, IllegalAccessException {
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] parameters = new Object[parameterTypes.length];
Parameter[] parameters = method.getParameters();
Object[] parameterValues = new Object[parameters.length];
for (Object o : context) {
Class<?> objectType = o.getClass();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
if (parameterType == null) {
continue;
}
if (parameterType.isAssignableFrom(objectType)) {
parameters[i] = o;
parameterTypes[i] = null;
apply(parameters, (parameter, i) -> {
PlaceholderRemainder annotation = parameter.getAnnotation(PlaceholderRemainder.class);
if (annotation != null) {
parameters[i] = null;
if (parameter.getType().isAssignableFrom(String.class)) {
parameterValues[i] = remainder;
} else {
parameterValues[i] = null;
}
}
});
for (Object o : context) {
Class<?> objectType = o.getClass();
apply(parameters, (parameter, i) -> {
if (parameter.getType().isAssignableFrom(objectType)) {
parameters[i] = null;
parameterValues[i] = o;
}
});
}
for (Class<?> parameterType : parameterTypes) {
if (parameterType != null) {
for (Object parameter : parameters) {
if (parameter != null) {
return null;
}
}
return method.invoke(instance, parameters);
return method.invoke(instance, parameterValues);
}
private static void apply(Parameter[] parameters, BiConsumer<Parameter, Integer> parameterProcessor) {
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (parameter == null) {
continue;
}
parameterProcessor.accept(parameter, i);
}
}
}

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.player;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import net.kyori.adventure.identity.Identified;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.player;
import com.discordsrv.api.placeholder.Placeholder;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.common.command.game.sender.ICommandSender;
import net.kyori.adventure.text.Component;