From d710683c9e35df6bfc01d37981b4e2760a5b05a1 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 13 Nov 2024 23:31:38 +0200 Subject: [PATCH] Fix viewer engine bug --- .gitignore | 2 + .../me/tofaa/entitylib/meta/MultiMeta.java | 21 +++++ .../me/tofaa/entitylib/ve/ViewerEngine.java | 6 ++ .../entitylib/ve/ViewerEngineListener.java | 93 ++++++++++++++----- build.gradle.kts | 2 +- discord-bot/build.gradle.kts | 16 ++++ .../tofaa/entitylib/discord/DiscordBot.java | 86 +++++++++++++++++ .../entitylib/discord/commands/Command.java | 47 ++++++++++ .../discord/commands/impl/PingCommand.java | 21 +++++ .../commands/impl/SendMessageCommand.java | 52 +++++++++++ .../discord/listeners/CommandListener.java | 28 ++++++ discord-bot/src/main/resources/logback.xml | 12 +++ settings.gradle.kts | 1 + 13 files changed, 361 insertions(+), 26 deletions(-) create mode 100644 api/src/main/java/me/tofaa/entitylib/meta/MultiMeta.java create mode 100644 discord-bot/build.gradle.kts create mode 100644 discord-bot/src/main/java/me/tofaa/entitylib/discord/DiscordBot.java create mode 100644 discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/Command.java create mode 100644 discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/PingCommand.java create mode 100644 discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/SendMessageCommand.java create mode 100644 discord-bot/src/main/java/me/tofaa/entitylib/discord/listeners/CommandListener.java create mode 100644 discord-bot/src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore index 61c3d56..50eb430 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ test-plugin/run !**/src/main/**/build/ !**/src/test/**/build/ +discord-bot/.env + /website ### IntelliJ IDEA ### diff --git a/api/src/main/java/me/tofaa/entitylib/meta/MultiMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/MultiMeta.java new file mode 100644 index 0000000..e2bc476 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/meta/MultiMeta.java @@ -0,0 +1,21 @@ +package me.tofaa.entitylib.meta; + +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import me.tofaa.entitylib.utils.ConcurrentWeakIdentityHashMap; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +final class MultiMeta { + + private final WeakReference parent; + private final Map> replacements; + + MultiMeta(EntityMeta parent) { + this.parent = new WeakReference<>(parent); + this.replacements = new ConcurrentWeakIdentityHashMap<>(); + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java index fb2a059..93ee2e2 100644 --- a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java +++ b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java @@ -1,6 +1,7 @@ package me.tofaa.entitylib.ve; +import com.github.retrooper.packetevents.protocol.player.User; import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.wrapper.WrapperEntity; import org.jetbrains.annotations.NotNull; @@ -49,6 +50,11 @@ public class ViewerEngine { tracked.clear(); } + public boolean canSpawnFor(User user, WrapperEntity entity) { + if (entity.getViewerRules().stream().anyMatch(rule -> rule.shouldSee(user))) return true; + return globalRules.stream().anyMatch(rule -> rule.shouldSee(user)); + } + public @UnmodifiableView Collection getTracked() { return Collections.unmodifiableCollection(tracked); } diff --git a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java index 42de5ac..ac4d89a 100644 --- a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java +++ b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java @@ -4,10 +4,14 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract; import com.github.retrooper.packetevents.event.PacketSendEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUnloadChunk; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import com.github.retrooper.packetevents.wrapper.play.server.*; +import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.utils.Check; +import me.tofaa.entitylib.wrapper.WrapperEntity; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; final class ViewerEngineListener extends PacketListenerAbstract { @@ -18,43 +22,82 @@ final class ViewerEngineListener extends PacketListenerAbstract { } @Override - public void onPacketSend(PacketSendEvent event) { - PacketTypeCommon type = event.getPacketType(); - if (type == PacketType.Play.Server.UNLOAD_CHUNK) { - PacketSendEvent copy = event.clone(); - engine.getExecutor().execute(() -> { + public void onPacketSend(PacketSendEvent e) { + PacketTypeCommon type = e.getPacketType(); + if (type == PacketType.Play.Server.SPAWN_PLAYER) { + exec(e, (event) -> { + WrapperPlayServerSpawnPlayer packet = new WrapperPlayServerSpawnPlayer(event); + genericSpawnCheck(packet.getEntityId(), event.getUser()); + }); + } + else if (type == PacketType.Play.Server.SPAWN_WEATHER_ENTITY) { + exec(e, (event) -> { + WrapperPlayServerSpawnWeatherEntity packet = new WrapperPlayServerSpawnWeatherEntity(event); + genericSpawnCheck(packet.getEntityId(), event.getUser()); + }); + } + else if (type == PacketType.Play.Server.SPAWN_EXPERIENCE_ORB) { + exec(e, (event) -> { + WrapperPlayServerSpawnExperienceOrb packet = new WrapperPlayServerSpawnExperienceOrb(event); + genericSpawnCheck(packet.getEntityId(), event.getUser()); + }); + } + else if (type == PacketType.Play.Server.SPAWN_LIVING_ENTITY) { + exec(e, (event) -> { + WrapperPlayServerSpawnLivingEntity packet = new WrapperPlayServerSpawnLivingEntity(event); + genericSpawnCheck(packet.getEntityId(), event.getUser()); + }); + } + else if (type == PacketType.Play.Server.SPAWN_ENTITY) { + exec(e, (event) -> { + WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event); + genericSpawnCheck(packet.getEntityId(), event.getUser()); + }); + } + else if (type == PacketType.Play.Server.UNLOAD_CHUNK) { + exec(e, (event) -> { WrapperPlayServerUnloadChunk packet = new WrapperPlayServerUnloadChunk(event); int chunkX = packet.getChunkX(); int chunkZ = packet.getChunkZ(); engine.getTracked0().forEach(entity -> { if (!Check.inChunk(entity.getLocation(), chunkX, chunkZ)) return; - entity.removeViewer(event.getUser()); + entity.removeViewer(e.getUser()); }); - copy.cleanUp(); }); } - if (type == PacketType.Play.Server.CHUNK_DATA) { - PacketSendEvent copy = event.clone(); - engine.getExecutor().execute(() -> { - WrapperPlayServerUnloadChunk packet = new WrapperPlayServerUnloadChunk(event); - int chunkX = packet.getChunkX(); - int chunkZ = packet.getChunkZ(); + else if (type == PacketType.Play.Server.CHUNK_DATA) { + exec(e, (event) -> { + WrapperPlayServerChunkData packet = new WrapperPlayServerChunkData(event); + int chunkX = packet.getColumn().getX(); + int chunkZ = packet.getColumn().getZ(); engine.getTracked0().forEach(entity -> { if (!Check.inChunk(entity.getLocation(), chunkX, chunkZ)) return; if (entity.hasViewer(event.getUser())) return; - AtomicBoolean pass = new AtomicBoolean(false); - entity.getViewerRules().forEach(rule -> { - pass.set(rule.shouldSee(event.getUser())); - }); - engine.getViewerRules().forEach(rule -> { - pass.set(rule.shouldSee(event.getUser())); - }); - if (!pass.get()) return; - entity.addViewer(event.getUser()); + if (engine.canSpawnFor(event.getUser(), entity)) { + entity.addViewer(event.getUser()); + } }); - copy.cleanUp(); }); } } + private void genericSpawnCheck(int entityId, User user) { + WrapperEntity entity = EntityLib.getApi().getEntity(entityId); + if (entity == null) return; + if (engine.canSpawnFor(user, entity)) { + entity.addViewer(user); + } + } + + private > void exec( + PacketSendEvent event, + Consumer runnable + ) { + PacketSendEvent copy = event.clone(); + engine.getExecutor().execute(() -> { + runnable.accept(copy); + copy.cleanUp(); + }); + } + } diff --git a/build.gradle.kts b/build.gradle.kts index 51a3dd2..41bb2f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ import java.io.ByteArrayOutputStream -val fullVersion = "2.4.10" +val fullVersion = "2.4.11" val snapshot = true group = "me.tofaa.entitylib" diff --git a/discord-bot/build.gradle.kts b/discord-bot/build.gradle.kts new file mode 100644 index 0000000..c9cc01e --- /dev/null +++ b/discord-bot/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + entitylib.`shadow-conventions` + entitylib.`library-conventions` +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(21) +} + +dependencies { + implementation("net.dv8tion:JDA:5.2.0") + implementation("ch.qos.logback:logback-classic:1.4.12") + + implementation("com.google.auto.service:auto-service:1.1.1") + annotationProcessor("com.google.auto.service:auto-service:1.1.1") +} \ No newline at end of file diff --git a/discord-bot/src/main/java/me/tofaa/entitylib/discord/DiscordBot.java b/discord-bot/src/main/java/me/tofaa/entitylib/discord/DiscordBot.java new file mode 100644 index 0000000..8998b16 --- /dev/null +++ b/discord-bot/src/main/java/me/tofaa/entitylib/discord/DiscordBot.java @@ -0,0 +1,86 @@ +package me.tofaa.entitylib.discord; + +import me.tofaa.entitylib.discord.commands.Command; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.hooks.EventListener; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.requests.GatewayIntent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public final class DiscordBot { + + public static Logger LOGGER = LoggerFactory.getLogger(DiscordBot.class); + + private static DiscordBot instance; + public static DiscordBot instance() { + return instance; + } + + private final JDA discord; + private final Set commands; + + DiscordBot(String[] args) { + instance = this; + if (args.length == 0) throw new RuntimeException("No token specified, use --token=token args"); + String token = args[0].replaceFirst("--token=", ""); + discord = JDABuilder.create(token, GatewayIntent.getIntents(GatewayIntent.ALL_INTENTS)).build(); + + this.commands = ConcurrentHashMap.newKeySet(); + } + + + private void loadCommands() { + ServiceLoader commands = ServiceLoader.load(Command.class); + List data = new ArrayList<>(); + for (Command command : commands) { + SlashCommandData cmd = Commands.slash(command.name(), command.description()); + if (command.hasPermission()) { + cmd.setDefaultPermissions(DefaultMemberPermissions.enabledFor(command.permission())); + } + command.options().forEach(option -> { + cmd.addOption(option.getType(), option.getName(), option.getDescription(), option.isRequired(), option.isAutoComplete()); + }); + data.add(cmd); + LOGGER.info("Loaded command {}", command.name()); + this.commands.add(command); + } + discord.updateCommands().addCommands(data).queue(); + } + + private void loadEvents() { + ServiceLoader loader = ServiceLoader.load(EventListener.class); + for (EventListener listener : loader) { + discord.getEventManager().register(listener); + LOGGER.info("Loaded event listener {}", listener.getClass().getSimpleName()); + } + } + + private void start() throws InterruptedException { + discord.awaitReady(); + loadCommands(); + loadEvents(); + } + + + public Set commands() { + return commands; + } + + public JDA discord() { + return discord; + } + + public static void main(String[] args) throws InterruptedException { + DiscordBot bot = new DiscordBot(args); + bot.start(); + } + +} diff --git a/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/Command.java b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/Command.java new file mode 100644 index 0000000..c686ff0 --- /dev/null +++ b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/Command.java @@ -0,0 +1,47 @@ +package me.tofaa.entitylib.discord.commands; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.Interaction; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public abstract class Command { + + private final String name; + private final String description; + private final Collection options; + private final Permission permission; + + public Command(String name, String description, Collection options, @Nullable Permission permission) { + this.name = name; + this.description = description; + this.options = options; + this.permission = permission; + } + + public Permission permission() { + return permission; + } + + public boolean hasPermission() { + return permission != null; + } + + public String name() { + return name; + } + + public String description() { + return description; + } + + public Collection options() { + return options; + } + + public abstract void handle(SlashCommandInteraction interaction); + +} diff --git a/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/PingCommand.java b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/PingCommand.java new file mode 100644 index 0000000..ed36642 --- /dev/null +++ b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/PingCommand.java @@ -0,0 +1,21 @@ +package me.tofaa.entitylib.discord.commands.impl; + +import com.google.auto.service.AutoService; +import me.tofaa.entitylib.discord.commands.Command; +import net.dv8tion.jda.api.interactions.Interaction; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; + +import java.util.Collections; + +@AutoService(Command.class) +public class PingCommand extends Command { + + public PingCommand() { + super("ping", "Ping command", Collections.emptyList(), null); + } + + @Override + public void handle(SlashCommandInteraction interaction) { + interaction.reply("Pong").queue(); + } +} diff --git a/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/SendMessageCommand.java b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/SendMessageCommand.java new file mode 100644 index 0000000..dad6cb1 --- /dev/null +++ b/discord-bot/src/main/java/me/tofaa/entitylib/discord/commands/impl/SendMessageCommand.java @@ -0,0 +1,52 @@ +package me.tofaa.entitylib.discord.commands.impl; + +import com.google.auto.service.AutoService; +import me.tofaa.entitylib.discord.commands.Command; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; + +import java.util.Arrays; +import java.util.List; + +@AutoService(Command.class) +public class SendMessageCommand extends Command { + + public SendMessageCommand() { + super( + "sendmessage", + "Send a message in a channel", + Arrays.asList( + new OptionData(OptionType.STRING, "message", "The message", true), + new OptionData(OptionType.STRING, "ping", "The type of ping to do", true) + .addChoice("Everyone", "everyone") + .addChoice("EntityLib", "elib") + ), + Permission.ADMINISTRATOR + ); + } + + @Override + public void handle(SlashCommandInteraction interaction) { + String msg = interaction.getOption("message").getAsString(); + MessageCreateBuilder builder =new MessageCreateBuilder(); + builder.setContent(msg); + + String pingType = interaction.getOption("ping").getAsString().toLowerCase(); + switch (pingType) { + case "elib": { + builder =builder.mentionRoles(1304607456650985502L); + break; + } + case "everyone": { + builder =builder.mentionRoles(1197504277787201616L); + break; + } + default: {} + } + interaction.getChannel().sendMessage(builder.build()).queue();; + interaction.reply("Sent").setEphemeral(true).queue(); + } +} diff --git a/discord-bot/src/main/java/me/tofaa/entitylib/discord/listeners/CommandListener.java b/discord-bot/src/main/java/me/tofaa/entitylib/discord/listeners/CommandListener.java new file mode 100644 index 0000000..9cb5765 --- /dev/null +++ b/discord-bot/src/main/java/me/tofaa/entitylib/discord/listeners/CommandListener.java @@ -0,0 +1,28 @@ +package me.tofaa.entitylib.discord.listeners; + +import com.google.auto.service.AutoService; +import me.tofaa.entitylib.discord.DiscordBot; +import me.tofaa.entitylib.discord.commands.Command; +import net.dv8tion.jda.api.events.GenericEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.hooks.EventListener; +import org.jetbrains.annotations.NotNull; + +@AutoService(EventListener.class) +public class CommandListener implements EventListener { + + @Override + public void onEvent(@NotNull GenericEvent genericEvent) { + if (!(genericEvent instanceof SlashCommandInteractionEvent)) { + return; + } + SlashCommandInteractionEvent event = (SlashCommandInteractionEvent) genericEvent; + Command command = DiscordBot.instance().commands().stream() + .filter(f -> f.name().equalsIgnoreCase(event.getName())) + .findAny().orElse(null); + if (command == null) { + return; + } + command.handle(event.getInteraction()); + } +} diff --git a/discord-bot/src/main/resources/logback.xml b/discord-bot/src/main/resources/logback.xml new file mode 100644 index 0000000..4cb45a6 --- /dev/null +++ b/discord-bot/src/main/resources/logback.xml @@ -0,0 +1,12 @@ + + + + + %d{HH:mm:ss.SSS} %boldCyan(%-34.-34thread) %boldGreen(%-15.-15logger{0}) %highlight(%-6level) %msg%n + + + + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 27a36d3..bf53135 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,6 +35,7 @@ include(":platforms:velocity") include(":platforms:standalone") if (!System.getenv("JITPACK").toBoolean()) { + include("discord-bot") include(":code-gen") include(":test-plugin") include(":block-bench-addon")