Skip to content

Commit

Permalink
Reimplement showInvisible in modern (#1450)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Herrera <[email protected]>
  • Loading branch information
Pablete1234 authored Nov 29, 2024
1 parent fac084a commit ccef0fd
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
}

Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,36 @@
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;
import tc.oc.pgm.platform.modern.util.Packets;
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<Byte> DATA_SHARED_FLAGS_ID =
ReflectionUtils.readStaticField(
Entity.class, EntityDataAccessor.class, "DATA_SHARED_FLAGS_ID");
private static final EntityDataAccessor<Float> DATA_HEALTH_ID = LivingEntity.DATA_HEALTH_ID;
private static final EntityDataAccessor<List<ParticleOptions>> 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) {
Expand Down Expand Up @@ -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;
}
}
}

0 comments on commit ccef0fd

Please sign in to comment.