From 93f66cb974b03d48594683de6daa992a9978ad36 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Fri, 29 Nov 2024 19:06:05 +0100 Subject: [PATCH] Reimplement showInvisible in modern Signed-off-by: Pablo Herrera --- .../modern/impl/ModernPlayerUtils.java | 48 ++++++++---- .../modern/packets/PacketManipulations.java | 73 +++++++++++++++---- 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java index 0bad988d9b..06113659c9 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java @@ -7,6 +7,7 @@ import java.util.UUID; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; import org.bukkit.Location; @@ -17,6 +18,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.Vector; +import tc.oc.pgm.platform.modern.packets.PacketManipulations; import tc.oc.pgm.platform.modern.util.Skins; import tc.oc.pgm.util.block.RayBlockIntersection; import tc.oc.pgm.util.bukkit.BukkitUtils; @@ -27,6 +29,9 @@ @Supports(value = PAPER, minVersion = "1.20.6") public class ModernPlayerUtils implements PlayerUtils { + private static final FixedMetadataValue TRUE = + new FixedMetadataValue(BukkitUtils.getPlugin(), true); + @Override public boolean teleportRelative( Player player, @@ -70,8 +75,22 @@ public double getAbsorption(LivingEntity entity) { } @Override - public void showInvisibles(Player player, boolean showInvisibles) { - // TODO: PLATFORM 1.20 does not support allowing seeing invisible players + public void showInvisibles(Player player, boolean showInvisible) { + if (updateMetadata(player, showInvisible, PacketManipulations.SHOW_INVISIBLE_KEY)) { + // Refresh all seen entities' metadata + var nmsPlayer = ((CraftPlayer) player).getHandle(); + ServerLevel world = (ServerLevel) nmsPlayer.level(); + var entityMap = world.getChunkSource().chunkMap.entityMap; + + for (var entity : world.players()) { + if (entity.isInvisible() && player.canSee(entity.getBukkitEntity())) { + var trackedEntity = entityMap.get(entity.getId()); + if (trackedEntity != null && trackedEntity.seenBy.contains(nmsPlayer.connection)) { + entity.refreshEntityData(nmsPlayer); + } + } + } + } } @Override @@ -113,20 +132,13 @@ public int getPing(Player player) { return player.getPing(); } - public static final String HIDE_PARTICLES = "hideParticles"; - private static final FixedMetadataValue TRUE = - new FixedMetadataValue(BukkitUtils.getPlugin(), true); - @Override public void setPotionParticles(Player player, boolean enabled) { - if (player.hasMetadata(HIDE_PARTICLES) != enabled) return; - - if (enabled) player.removeMetadata(HIDE_PARTICLES, BukkitUtils.getPlugin()); - else player.setMetadata(HIDE_PARTICLES, TRUE); - - var nmsPlayer = ((CraftPlayer) player).getHandle(); - if (!nmsPlayer.getActiveEffects().isEmpty()) { - nmsPlayer.effectsDirty = true; + if (updateMetadata(player, !enabled, PacketManipulations.HIDE_PARTICLES_KEY)) { + var nmsPlayer = ((CraftPlayer) player).getHandle(); + if (!nmsPlayer.getActiveEffects().isEmpty()) { + nmsPlayer.effectsDirty = true; + } } } @@ -142,4 +154,12 @@ public RayBlockIntersection getTargetedBlock(Player player) { return null; } } + + private boolean updateMetadata(Player player, boolean set, String key) { + if (player.hasMetadata(key)) return false; + + if (set) player.setMetadata(key, TRUE); + else player.removeMetadata(key, BukkitUtils.getPlugin()); + return true; + } } diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java index 4c9d80cadc..43bf15cb4b 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java @@ -3,12 +3,14 @@ import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketEvent; +import java.util.ArrayList; import java.util.List; import java.util.Map; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -16,13 +18,21 @@ import tc.oc.pgm.platform.modern.util.PlayerTracker; import tc.oc.pgm.util.reflect.ReflectionUtils; -public class PacketManipulations { +public class PacketManipulations implements PacketSender { + public static final String HIDE_PARTICLES_KEY = "hideParticles"; + public static final String SHOW_INVISIBLE_KEY = "showInvisible"; + + protected static final EntityDataAccessor DATA_SHARED_FLAGS_ID = + ReflectionUtils.readStaticField( + Entity.class, EntityDataAccessor.class, "DATA_SHARED_FLAGS_ID"); private static final EntityDataAccessor DATA_HEALTH_ID = LivingEntity.DATA_HEALTH_ID; private static final EntityDataAccessor> DATA_EFFECT_PARTICLES = ReflectionUtils.readStaticField( LivingEntity.class, EntityDataAccessor.class, "DATA_EFFECT_PARTICLES"); + private static final int INVISIBILITY = 0x20; + private final PlayerTracker tracker; public PacketManipulations(Plugin plugin, PlayerTracker tracker) { @@ -55,34 +65,65 @@ private void handleCombatKill(PacketEvent event) { } private void handleEntityMetadata(PacketEvent event) { - var playerId = event.getPlayer().getEntityId(); - var packet = event.getPacket(); - int entityId = packet.getIntegers().read(0); - ClientboundSetEntityDataPacket nmsPacket = (ClientboundSetEntityDataPacket) packet.getHandle(); + ClientboundSetEntityDataPacket nmsPacket = + (ClientboundSetEntityDataPacket) event.getPacket().getHandle(); + int entityId = nmsPacket.id(); var items = nmsPacket.packedItems(); Player pl = tracker.get(entityId); + // We're only interested in modifying players if (pl == null) return; - boolean isSelf = playerId == entityId; + // Replace the packet without invisibility if needed + if (pl.isInvisible() && event.getPlayer().hasMetadata(SHOW_INVISIBLE_KEY)) { + for (int i = 0; i < items.size(); i++) { + var item = items.get(i); + if (item.id() == DATA_SHARED_FLAGS_ID.id()) { + byte val = (byte) item.value(); + if ((val & INVISIBILITY) != 0) { + // Because multiple players could receive this packet, we can't mutate it. + // Make a clone to send instead, and cancel the original + var newItems = new ArrayList<>(items); + newItems.set(i, SynchedEntityData.DataValue.create(DATA_SHARED_FLAGS_ID, (byte) + (val & ~INVISIBILITY))); + + send(new ClientboundSetEntityDataPacket(entityId, newItems), event.getPlayer()); + event.setCancelled(true); + return; + } + } + } + } + + boolean isSelf = event.getPlayer().getEntityId() == entityId; boolean isDead = pl.hasMetadata("isDead"); boolean checkHealth = isSelf || isDead; - boolean hideParticles = pl.hasMetadata("hideParticles"); + boolean hideParticles = pl.hasMetadata(HIDE_PARTICLES_KEY); - if (!isSelf && !isDead && !hideParticles) return; + // Nothing to check, move along + if (!checkHealth && !hideParticles) return; for (int i = 0; i < items.size(); i++) { - var packedItem = items.get(i); - if (checkHealth && packedItem.id() == DATA_HEALTH_ID.id()) { - float val = (float) packedItem.value(); - if (isSelf && val > 0) continue; - items.set( - i, SynchedEntityData.DataValue.create(DATA_HEALTH_ID, isSelf ? Math.max(val, 1f) : 0f)); + var item = items.get(i); + + if (checkHealth && item.id() == DATA_HEALTH_ID.id()) { + float val = (float) item.value(); + if (!isSelf || val <= 0) { + val = isSelf ? Math.max(val, 1f) : 0f; + items.set(i, SynchedEntityData.DataValue.create(DATA_HEALTH_ID, val)); + } + checkHealth = false; } - if (hideParticles && packedItem.id() == DATA_EFFECT_PARTICLES.id()) { - items.set(i, SynchedEntityData.DataValue.create(DATA_EFFECT_PARTICLES, List.of())); + if (hideParticles && item.id() == DATA_EFFECT_PARTICLES.id()) { + List val = (List) item.value(); + if (!val.isEmpty()) { + items.set(i, SynchedEntityData.DataValue.create(DATA_EFFECT_PARTICLES, List.of())); + } + hideParticles = false; } + + if (!checkHealth && !hideParticles) return; } } }