From ee554d66b4f5330cee6a6fec50cc74f747dd7223 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 26 Oct 2024 22:51:15 +0300 Subject: [PATCH] Viewer Rules --- .../java/me/tofaa/entitylib/utils/Check.java | 7 ++ .../me/tofaa/entitylib/ve/ViewerEngine.java | 86 +++++++++++++++++++ .../entitylib/ve/ViewerEngineListener.java | 60 +++++++++++++ .../me/tofaa/entitylib/ve/ViewerRule.java | 11 +++ .../entitylib/wrapper/WrapperEntity.java | 38 ++++++++ 5 files changed, 202 insertions(+) create mode 100644 api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java create mode 100644 api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java create mode 100644 api/src/main/java/me/tofaa/entitylib/ve/ViewerRule.java diff --git a/api/src/main/java/me/tofaa/entitylib/utils/Check.java b/api/src/main/java/me/tofaa/entitylib/utils/Check.java index 478ed49..8dcf013 100644 --- a/api/src/main/java/me/tofaa/entitylib/utils/Check.java +++ b/api/src/main/java/me/tofaa/entitylib/utils/Check.java @@ -1,5 +1,6 @@ package me.tofaa.entitylib.utils; +import com.github.retrooper.packetevents.protocol.world.Location; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,6 +16,12 @@ public final class Check { private Check() {} + public static boolean inChunk(Location location, int cx, int cz) { + // Assumes each chunk is 16x + int lx = ((int) Math.floor(location.getX())) >> 4; + int lz = ((int) Math.floor(location.getZ())) >> 4; + return cx == lx && cz == lz; + } public static void arrayLength(List lines, int index, T e) { if (index >= lines.size()) { diff --git a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java new file mode 100644 index 0000000..fb2a059 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngine.java @@ -0,0 +1,86 @@ +package me.tofaa.entitylib.ve; + + +import me.tofaa.entitylib.EntityLib; +import me.tofaa.entitylib.wrapper.WrapperEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class ViewerEngine { + + private final List globalRules; + private final Set tracked; + private final ViewerEngineListener listener; + private Executor executor; + + public ViewerEngine() { + this.globalRules = new CopyOnWriteArrayList<>(); + this.tracked = Collections.newSetFromMap(new WeakHashMap<>()); + this.executor = Executors.newSingleThreadExecutor(); + this.listener = new ViewerEngineListener(this); + } + + public void enable() { + EntityLib.getApi().getPacketEvents().getEventManager().registerListener(listener); + } + public void disable() { + EntityLib.getApi().getPacketEvents().getEventManager().unregisterListener(listener); + } + + public Executor getExecutor() { + return executor; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + + public void track(@NotNull WrapperEntity entity) { + tracked.add(entity); + } + + public void clearTracked() { + tracked.clear(); + } + + public @UnmodifiableView Collection getTracked() { + return Collections.unmodifiableCollection(tracked); + } + + Set getTracked0() { + return tracked; + } + + public void addViewerRule(@NotNull ViewerRule rule) { + this.globalRules.add(rule); + } + + public void removeViewerRule(@NotNull ViewerRule rule) { + this.globalRules.remove(rule); + } + + public void removeViewerRule(int index) { + this.globalRules.remove(index); + } + + public void clearViewerRules() { + this.globalRules.clear(); + } + + public @UnmodifiableView Collection getViewerRules() { + return Collections.unmodifiableCollection(globalRules); + } + + public @Nullable ViewerRule getViewerRule(int index) { + if (this.globalRules.size() >= index - 1) return null; + if (index < 0) return null; + return globalRules.get(index); + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java new file mode 100644 index 0000000..42de5ac --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/ve/ViewerEngineListener.java @@ -0,0 +1,60 @@ +package me.tofaa.entitylib.ve; + +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 me.tofaa.entitylib.utils.Check; + +import java.util.concurrent.atomic.AtomicBoolean; + +final class ViewerEngineListener extends PacketListenerAbstract { + + private final ViewerEngine engine; + + ViewerEngineListener(ViewerEngine engine) { + this.engine = engine; + } + + @Override + public void onPacketSend(PacketSendEvent event) { + PacketTypeCommon type = event.getPacketType(); + if (type == PacketType.Play.Server.UNLOAD_CHUNK) { + PacketSendEvent copy = event.clone(); + engine.getExecutor().execute(() -> { + 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()); + }); + 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(); + 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()); + }); + copy.cleanUp(); + }); + } + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/ve/ViewerRule.java b/api/src/main/java/me/tofaa/entitylib/ve/ViewerRule.java new file mode 100644 index 0000000..61431e6 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/ve/ViewerRule.java @@ -0,0 +1,11 @@ +package me.tofaa.entitylib.ve; + + +import com.github.retrooper.packetevents.protocol.player.User; + +@FunctionalInterface +public interface ViewerRule { + + boolean shouldSee(User user); + +} diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index e40db7b..206f594 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -12,12 +12,15 @@ import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.meta.types.ObjectData; import me.tofaa.entitylib.tick.Tickable; +import me.tofaa.entitylib.ve.ViewerRule; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; public class WrapperEntity implements Tickable, TrackedEntity { @@ -36,6 +39,7 @@ public class WrapperEntity implements Tickable, TrackedEntity { private int riding = -1; private final Set passengers; private EntityContainer parent; + private final List viewerRules; public WrapperEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) { this.entityId = entityId; @@ -46,6 +50,7 @@ public class WrapperEntity implements Tickable, TrackedEntity { this.viewers = ConcurrentHashMap.newKeySet(); this.passengers = ConcurrentHashMap.newKeySet(); this.location = new Location(0, 0, 0, 0, 0); + this.viewerRules = new CopyOnWriteArrayList<>(); } public WrapperEntity(int entityId, EntityType entityType) { @@ -353,6 +358,31 @@ public class WrapperEntity implements Tickable, TrackedEntity { return new WrapperPlayServerSetPassengers(entityId, passengers.stream().mapToInt(i -> i).toArray()); } + public @UnmodifiableView Collection getViewerRules() { + return Collections.unmodifiableCollection(viewerRules); + } + + public void addViewerRule(@NotNull ViewerRule rule) { + this.viewerRules.add(rule); + } + + public void removeViewerRule(@NotNull ViewerRule rule) { + this.viewerRules.remove(rule); + } + + public void removeViewerRule(int index) { + this.viewerRules.remove(index); + } + + public void clearViewerRules() { + this.viewerRules.clear(); + } + + public @Nullable ViewerRule getViewerRule(int index) { + if (this.viewerRules.size() >= index - 1) return null; + if (index < 0) return null; + return viewerRules.get(index); + } private WrapperPlayServerEntityVelocity getVelocityPacket() { Vector3d velocity = this.velocity.multiply(8000.0f / 20.0f); @@ -578,6 +608,14 @@ public class WrapperEntity implements Tickable, TrackedEntity { return Collections.unmodifiableSet(viewers); } + public boolean hasViewer(UUID uuid) { + return viewers.contains(uuid); + } + + public boolean hasViewer(User user) { + return hasViewer(user.getUUID()); + } + public Location getLocation() { return location; }