Skip to content

Commit

Permalink
Fix: SignGUI in MC 1.21.x
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzimmermann committed Oct 6, 2024
1 parent a8cc377 commit bd3e3db
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 33 deletions.
99 changes: 69 additions & 30 deletions src/main/java/de/codingair/codingapi/player/gui/sign/SignGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import de.codingair.codingapi.tools.items.XMaterial;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
Expand All @@ -25,23 +24,29 @@
import java.util.UUID;

public abstract class SignGUI {
private static final Class<?> packetClass;
private static final Class<?> updatePacket;
private static final Class<?> playInUpdateSignClass;
private static final Class<?> playOutTileEntityData;
private static final Class<?> baseBlockPosition;
private static final IReflection.FieldAccessor<?> pos;
private static final Class<?> playOutBlockChangeClass;
private static final IReflection.FieldAccessor<?> playOutBlockChangeClass$BlockPosition;
private static final IReflection.FieldAccessor<?> playOutTileEntityData$BlockPosition;
private static final IReflection.FieldAccessor<Integer> getX;
private static final IReflection.FieldAccessor<Integer> getY;
private static final IReflection.FieldAccessor<Integer> getZ;
private static final IReflection.FieldAccessor<?> packetLines;

private static final IReflection.MethodAccessor setWorld;

static {
packetClass = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayInUpdateSign");
playInUpdateSignClass = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayInUpdateSign");
playOutBlockChangeClass = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayOutBlockChange");

if (Version.atLeast(9))
updatePacket = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayOutTileEntityData");
else updatePacket = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayOutUpdateSign");
playOutTileEntityData = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayOutTileEntityData");
else playOutTileEntityData = IReflection.getClass(IReflection.ServerPacket.PACKETS, "PacketPlayOutUpdateSign");

pos = IReflection.getField(updatePacket, PacketUtils.BlockPositionClass, 0);
playOutBlockChangeClass$BlockPosition = IReflection.getField(playOutBlockChangeClass, PacketUtils.BlockPositionClass, 0);
playOutTileEntityData$BlockPosition = IReflection.getField(playOutTileEntityData, PacketUtils.BlockPositionClass, 0);
baseBlockPosition = IReflection.getClass(IReflection.ServerPacket.CORE, Version.choose("Vec3i", "BaseBlockPosition"));

getX = IReflection.getNonStaticField(baseBlockPosition, int.class, 0);
Expand All @@ -51,6 +56,10 @@ public abstract class SignGUI {
if (Version.atLeast(9))
packetLines = IReflection.getField(PacketUtils.PacketPlayInUpdateSignClass, String[].class, 0);
else packetLines = IReflection.getField(PacketUtils.PacketPlayInUpdateSignClass, "b");

if (Version.atLeast(20.6)) {
setWorld = IReflection.getMethod(PacketUtils.TileEntityClass, new Class[]{PacketUtils.WorldClass});
} else setWorld = null;
}

private final Player player;
Expand All @@ -61,6 +70,8 @@ public abstract class SignGUI {
//used for instant opening -> open directly after sending the sign update packet
private Location signLocation = null;
private Runnable waiting = null;
private boolean ignoreFutureBlockChanges = true;
private boolean ignoreBlockChanges = false;

@NmsLoader
private SignGUI() {
Expand Down Expand Up @@ -133,7 +144,7 @@ private void injectPacketReader() {
new PacketReader(this.player, "SignEditor", this.plugin) {
@Override
public boolean readPacket(Object packet) {
if (packet.getClass().equals(packetClass)) {
if (packet.getClass().equals(playInUpdateSignClass)) {
String[] lines;

if (Version.atLeast(9)) {
Expand Down Expand Up @@ -165,7 +176,7 @@ public boolean readPacket(Object packet) {

Bukkit.getScheduler().runTask(plugin, () -> {
onSignChangeEvent(lines);
revertTempSignBlock();
close();
});
return true;
}
Expand All @@ -175,46 +186,68 @@ public boolean readPacket(Object packet) {

@Override
public boolean writePacket(Object packet) {
if (packet.getClass().equals(updatePacket)) {
Object position = pos.get(packet);

int x = getX.get(position);
int y = getY.get(position);
int z = getZ.get(position);

if (waiting != null && signLocation != null &&
signLocation.getBlockX() == x &&
signLocation.getBlockY() == y &&
signLocation.getBlockZ() == z) {
Bukkit.getScheduler().runTask(plugin, waiting);
waiting = null;
if (signLocation == null) return false;

Object position;
if (packet.getClass().equals(playOutBlockChangeClass))
position = playOutBlockChangeClass$BlockPosition.get(packet);
else if (packet.getClass().equals(playOutTileEntityData))
position = playOutTileEntityData$BlockPosition.get(packet);
else return false;

int x = getX.get(position);
int y = getY.get(position);
int z = getZ.get(position);

if (signLocation.getBlockX() != x || signLocation.getBlockY() != y || signLocation.getBlockZ() != z)
return false;

if (packet.getClass().equals(playOutBlockChangeClass)) {
// ignore changes during editing
if (ignoreBlockChanges) return true;

if (ignoreFutureBlockChanges) {
// The first block change needs to be passed through to allow us to set the sign
ignoreFutureBlockChanges = false;
ignoreBlockChanges = true;
}
}

if (waiting != null && packet.getClass().equals(playOutTileEntityData)) {
Bukkit.getScheduler().runTask(plugin, waiting);
waiting = null;
}

return false;
}
}.inject();
}

private @NotNull Location getTemporarySignLocation() {
// max distance is 7
return player.getLocation().clone().subtract(0, 7, 0);
return player.getLocation().getBlock().getLocation().subtract(0, 7, 0);
}

private void prepareTemporarySign(@NotNull XMaterial material, @NotNull Location tempSign) {
Object updatePacket = null;

ignoreFutureBlockChanges = true;
PacketUtils.sendBlockChange(player, tempSign, material);
if (this.lines != null) sendLinesChange(tempSign, material);

if (this.lines != null) updatePacket = createUpdatePacket(tempSign, material);
if (updatePacket != null) PacketUtils.sendPacket(player, updatePacket);
}

private void revertTempSignBlock() {
// do we have a temporary sign?
if (sign != null) return;

Block b = signLocation.getBlock();
PacketUtils.sendBlockChange(player, signLocation, b);
ignoreBlockChanges = false;
PacketUtils.sendBlockChange(player, signLocation, signLocation.getBlock());
}

private void sendLinesChange(@NotNull Location tempSign, @NotNull XMaterial material) {
@NotNull
private Object createUpdatePacket(@NotNull Location tempSign, @NotNull XMaterial material) {
IReflection.ConstructorAccessor con;
Class<?> iChatBaseComponentArrayClass = Array.newInstance(PacketUtils.IChatBaseComponentClass, 0).getClass();

Expand All @@ -241,6 +274,11 @@ private void sendLinesChange(@NotNull Location tempSign, @NotNull XMaterial mate
setBlockPos.set(tileEntity, blockPos);
}

if (Version.atLeast(20.6)) {
// Fix: Since 20.6, the PacketPlayOutTileEntityData requires the world to be set.
setWorld.invoke(tileEntity, PacketUtils.getWorldServer(tempSign.getWorld()));
}

// write line contents
if (Version.atLeast(20)) {
Class<?> signTextClass = IReflection.getClass(IReflection.ServerPacket.BLOCK_ENTITY, "SignText");
Expand All @@ -250,7 +288,7 @@ private void sendLinesChange(@NotNull Location tempSign, @NotNull XMaterial mate
throw new NullPointerException("Cannot prepare temporary sign: Could not find SignText constructor.");

Object blackColor = enumColorClass.getEnumConstants()[enumColorClass.getEnumConstants().length - 1];
Object signText = con.newInstance(lines, lines, blackColor, false);
Object signText = con.newInstance(lines, lines, blackColor, false /*glow*/);

IReflection.MethodAccessor applyText = IReflection.getMethod(tileEntitySignClass, boolean.class, new Class[]{signTextClass, boolean.class});

Expand Down Expand Up @@ -285,7 +323,7 @@ private void sendLinesChange(@NotNull Location tempSign, @NotNull XMaterial mate
packet = createUpdatePacket.invoke(tileEntity);
}

PacketUtils.sendPacket(this.player, packet);
return packet;
}

private void prepareSignEditing(@Nullable Sign sign) {
Expand Down Expand Up @@ -336,6 +374,7 @@ public void close(@Nullable Call call) {

if (packetReader != null) packetReader.unInject();
AsyncCatcher.runSync(plugin, () -> {
revertTempSignBlock();
player.closeInventory();
if (call != null) call.proceed();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,19 @@ public static Class<?> getClass(String packet, String className, boolean acceptN
return null;
}
} else {
return IReflection.getClass(packet, className);
return IReflection.getClass(packet, className);
}
}

public static void sendBlockChange(@NotNull Player player, @NotNull Location location, @NotNull XMaterial data) {
Object iBlockData = PacketUtils.getIBlockData(data);
sendBlockChange(player, location, iBlockData);
if (Version.atLeast(20.6)) {
Material material = data.parseMaterial();
if (material == null) throw new NullPointerException("Material cannot be null! (XMaterial=" + data + ")");
player.sendBlockChange(location, material.createBlockData());
} else {
Object iBlockData = PacketUtils.getIBlockData(data);
sendBlockChange(player, location, iBlockData);
}
}

public static void sendBlockChange(@NotNull Player player, @NotNull Location location, @NotNull Block data) {
Expand Down

0 comments on commit bd3e3db

Please sign in to comment.