Fix viewer engine bug

This commit is contained in:
= 2024-11-13 23:31:38 +02:00
parent 519dd91426
commit d710683c9e
13 changed files with 361 additions and 26 deletions

2
.gitignore vendored
View file

@ -8,6 +8,8 @@ test-plugin/run
!**/src/main/**/build/
!**/src/test/**/build/
discord-bot/.env
/website
### IntelliJ IDEA ###

View file

@ -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<EntityMeta> parent;
private final Map<UUID, List<EntityData>> replacements;
MultiMeta(EntityMeta parent) {
this.parent = new WeakReference<>(parent);
this.replacements = new ConcurrentWeakIdentityHashMap<>();
}
}

View file

@ -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<WrapperEntity> getTracked() {
return Collections.unmodifiableCollection(tracked);
}

View file

@ -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 <T extends PacketWrapper<T>> void exec(
PacketSendEvent event,
Consumer<PacketSendEvent> runnable
) {
PacketSendEvent copy = event.clone();
engine.getExecutor().execute(() -> {
runnable.accept(copy);
copy.cleanUp();
});
}
}

View file

@ -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"

View file

@ -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")
}

View file

@ -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<Command> 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<Command> commands = ServiceLoader.load(Command.class);
List<CommandData> 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<EventListener> 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<Command> commands() {
return commands;
}
public JDA discord() {
return discord;
}
public static void main(String[] args) throws InterruptedException {
DiscordBot bot = new DiscordBot(args);
bot.start();
}
}

View file

@ -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<OptionData> options;
private final Permission permission;
public Command(String name, String description, Collection<OptionData> 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<OptionData> options() {
return options;
}
public abstract void handle(SlashCommandInteraction interaction);
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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());
}
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %boldCyan(%-34.-34thread) %boldGreen(%-15.-15logger{0}) %highlight(%-6level) %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View file

@ -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")