From 92639d8c2c3ef6d92048515e7311baf1d457077e Mon Sep 17 00:00:00 2001 From: Cerus Date: Thu, 25 Jul 2024 03:40:51 +0200 Subject: [PATCH] feat: 1.21 --- README.md | 8 +- bukkit-16_R3/pom.xml | 2 +- bukkit-17_R1/pom.xml | 2 +- bukkit-18_R1/pom.xml | 2 +- bukkit-18_R2/pom.xml | 2 +- bukkit-19_R1/pom.xml | 2 +- bukkit-19_R2/pom.xml | 2 +- bukkit-19_R3/pom.xml | 2 +- bukkit-20_R1/pom.xml | 2 +- bukkit-20_R2/pom.xml | 2 +- bukkit-20_R3/pom.xml | 2 +- bukkit-20_R4/pom.xml | 2 +- bukkit-21_R1/pom.xml | 35 ++++ .../cerus/maps/version/PacketHandler21R1.java | 110 +++++++++++ .../maps/version/VersionAdapter21R1.java | 185 ++++++++++++++++++ common/pom.xml | 2 +- plugin/pom.xml | 10 +- .../maps/version/VersionAdapterFactory.java | 3 +- pom.xml | 3 +- 19 files changed, 358 insertions(+), 20 deletions(-) create mode 100644 bukkit-21_R1/pom.xml create mode 100644 bukkit-21_R1/src/main/java/dev/cerus/maps/version/PacketHandler21R1.java create mode 100644 bukkit-21_R1/src/main/java/dev/cerus/maps/version/VersionAdapter21R1.java diff --git a/README.md b/README.md index eedff87..980f08c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ of packet-maps.

• Advanced engine features like [alpha compositing](https://en.wikipedia.org/wiki/Alpha_compositing) ([Image](https://cerus.dev/img/maps_alpha_composition.png))\ • Efficient click handling\ -• Supports 1.16.5 - 1.20.6 +• Supports 1.16.5 - 1.21 **What is the point of the plugin module?**\ See [FAQ](#FAQ) @@ -50,7 +50,7 @@ See [FAQ](#FAQ) dev.cerus.maps common - 3.8.5 + 3.8.6 provided @@ -59,7 +59,7 @@ See [FAQ](#FAQ) dev.cerus.maps plugin - 3.8.5 + 3.8.6 provided @@ -136,7 +136,7 @@ public class MyPlugin extends JavaPlugin { ### Building -Requirements: Java 21, Git, Maven, CraftBukkit 1.16.5, 1.17.1, 1.18.1, 1.18.2, 1.19.1, 1.19.3, 1.19.4, 1.20, 1.20.2, 1.20.4 and 1.20.6 installed in local +Requirements: Java 21, Git, Maven, CraftBukkit 1.16.5, 1.17.1, 1.18.1, 1.18.2, 1.19.1, 1.19.3, 1.19.4, 1.20, 1.20.2, 1.20.4, 1.20.6 and 1.21 installed in local Maven repo Simply clone the repository, navigate into the directory and run `mvn clean package`. The plugin will be in `plugin/target` and the api diff --git a/bukkit-16_R3/pom.xml b/bukkit-16_R3/pom.xml index 6c784dc..d35aed5 100644 --- a/bukkit-16_R3/pom.xml +++ b/bukkit-16_R3/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-17_R1/pom.xml b/bukkit-17_R1/pom.xml index 709ed33..ca743d4 100644 --- a/bukkit-17_R1/pom.xml +++ b/bukkit-17_R1/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-18_R1/pom.xml b/bukkit-18_R1/pom.xml index 37f71ee..5564537 100644 --- a/bukkit-18_R1/pom.xml +++ b/bukkit-18_R1/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-18_R2/pom.xml b/bukkit-18_R2/pom.xml index e49afe5..6533501 100644 --- a/bukkit-18_R2/pom.xml +++ b/bukkit-18_R2/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-19_R1/pom.xml b/bukkit-19_R1/pom.xml index 31ebbad..2940ede 100644 --- a/bukkit-19_R1/pom.xml +++ b/bukkit-19_R1/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-19_R2/pom.xml b/bukkit-19_R2/pom.xml index 0d76319..7eb5698 100644 --- a/bukkit-19_R2/pom.xml +++ b/bukkit-19_R2/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/bukkit-19_R3/pom.xml b/bukkit-19_R3/pom.xml index 8ae70be..e71d81d 100644 --- a/bukkit-19_R3/pom.xml +++ b/bukkit-19_R3/pom.xml @@ -6,7 +6,7 @@ dev.cerus.maps parent - 3.8.5 + 3.8.6 bukkit-19_R3 diff --git a/bukkit-20_R1/pom.xml b/bukkit-20_R1/pom.xml index fe7a44d..6caa7b2 100644 --- a/bukkit-20_R1/pom.xml +++ b/bukkit-20_R1/pom.xml @@ -6,7 +6,7 @@ dev.cerus.maps parent - 3.8.5 + 3.8.6 bukkit-20_R1 diff --git a/bukkit-20_R2/pom.xml b/bukkit-20_R2/pom.xml index 99b8677..14a9362 100644 --- a/bukkit-20_R2/pom.xml +++ b/bukkit-20_R2/pom.xml @@ -6,7 +6,7 @@ dev.cerus.maps parent - 3.8.5 + 3.8.6 bukkit-20_R2 diff --git a/bukkit-20_R3/pom.xml b/bukkit-20_R3/pom.xml index 33d401d..73f4761 100644 --- a/bukkit-20_R3/pom.xml +++ b/bukkit-20_R3/pom.xml @@ -6,7 +6,7 @@ dev.cerus.maps parent - 3.8.5 + 3.8.6 bukkit-20_R3 diff --git a/bukkit-20_R4/pom.xml b/bukkit-20_R4/pom.xml index 87c73a6..301a68b 100644 --- a/bukkit-20_R4/pom.xml +++ b/bukkit-20_R4/pom.xml @@ -6,7 +6,7 @@ dev.cerus.maps parent - 3.8.5 + 3.8.6 bukkit-20_R4 diff --git a/bukkit-21_R1/pom.xml b/bukkit-21_R1/pom.xml new file mode 100644 index 0000000..0d002ff --- /dev/null +++ b/bukkit-21_R1/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + dev.cerus.maps + parent + 3.8.6 + + + bukkit-21_R1 + + + 16 + 16 + UTF-8 + + + + + dev.cerus.maps + common + ${parent.version} + provided + + + org.bukkit + craftbukkit + 1.21-R0.1-SNAPSHOT + provided + + + + diff --git a/bukkit-21_R1/src/main/java/dev/cerus/maps/version/PacketHandler21R1.java b/bukkit-21_R1/src/main/java/dev/cerus/maps/version/PacketHandler21R1.java new file mode 100644 index 0000000..febc37c --- /dev/null +++ b/bukkit-21_R1/src/main/java/dev/cerus/maps/version/PacketHandler21R1.java @@ -0,0 +1,110 @@ +package dev.cerus.maps.version; + +import dev.cerus.maps.api.version.PacketListener; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import java.lang.reflect.Field; +import java.util.Arrays; +import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; +import net.minecraft.network.protocol.game.PacketPlayInBlockDig; +import net.minecraft.network.protocol.game.PacketPlayInBlockPlace; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import net.minecraft.network.protocol.game.PacketPlayInUseItem; +import net.minecraft.world.EnumHand; +import net.minecraft.world.phys.MovingObjectPositionBlock; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +public class PacketHandler21R1 extends ChannelDuplexHandler { + + private static final Field actionField; + private static final Object attackAction; + + static { + try { + actionField = PacketPlayInUseEntity.class.getDeclaredField("c"); + actionField.setAccessible(true); + final Field attackActionField = PacketPlayInUseEntity.class.getDeclaredField("e"); + attackActionField.setAccessible(true); + attackAction = attackActionField.get(null); + } catch (final NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private final Player player; + private final PacketListener listener; + private final JavaPlugin plugin; + + public PacketHandler21R1(final Player player, final PacketListener listener, final JavaPlugin plugin) { + this.player = player; + this.listener = listener; + this.plugin = plugin; + } + + private static Object getAction(final PacketPlayInUseEntity packet) { + try { + return actionField.get(packet); + } catch (final IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static EnumHand getHand(final PacketPlayInUseEntity packet) { + try { + final Object action = getAction(packet); + final Field handField = Arrays.stream(action.getClass().getDeclaredFields()) + .filter(field -> field.getType() == EnumHand.class) + .findAny().orElse(null); + if (handField == null) { + return null; + } + handField.setAccessible(true); + return (EnumHand) handField.get(action); + } catch (final IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + if ((((msg instanceof final PacketPlayInUseItem useItem && useItem.b() != EnumHand.b) || msg instanceof PacketPlayInBlockPlace) && this.listener.handlePlayerRightClick(this.player)) + || (msg instanceof final PacketPlayInUseEntity useEntity && getHand(useEntity) != EnumHand.b && (getAction(useEntity) == attackAction ? this.listener.handlePlayerLeftClick(this.player) : this.listener.handlePlayerRightClick(this.player))) + || (msg instanceof PacketPlayInBlockDig && this.listener.handlePlayerLeftClick(this.player))) { + if (msg instanceof final PacketPlayInBlockDig dig) { + // To prevent de-syncs we need to tell the client that the block has not changed + final Location location = new Location( + this.player.getWorld(), + dig.b().u(), + dig.b().v(), + dig.b().w() + ); + // If we don't acknowledge the client's block change it won't accept further block change packets + ((CraftPlayer) this.player).getHandle().c.a(new ClientboundBlockChangedAckPacket(dig.g())); + Bukkit.getScheduler().runTask(this.plugin, () -> this.player.sendBlockChange(location, location.getBlock().getBlockData())); + } + if (msg instanceof final PacketPlayInUseItem useItem) { + // To prevent de-syncs we need to tell the client that the block has not changed + final MovingObjectPositionBlock pos = useItem.e(); + if (pos != null && pos.a() != null) { + final Location location = new Location( + this.player.getWorld(), + pos.a().u(), + pos.a().v(), + pos.a().w() + ).getBlock().getRelative(CraftBlock.notchToBlockFace(pos.b())).getLocation(); + // If we don't acknowledge the client's block change it won't accept further block change packets + ((CraftPlayer) this.player).getHandle().c.a(new ClientboundBlockChangedAckPacket(useItem.f())); + Bukkit.getScheduler().runTask(this.plugin, () -> this.player.sendBlockChange(location, location.getBlock().getBlockData())); + } + } + return; + } + super.channelRead(ctx, msg); + } + +} diff --git a/bukkit-21_R1/src/main/java/dev/cerus/maps/version/VersionAdapter21R1.java b/bukkit-21_R1/src/main/java/dev/cerus/maps/version/VersionAdapter21R1.java new file mode 100644 index 0000000..c528a74 --- /dev/null +++ b/bukkit-21_R1/src/main/java/dev/cerus/maps/version/VersionAdapter21R1.java @@ -0,0 +1,185 @@ +package dev.cerus.maps.version; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import dev.cerus.maps.api.ClientsideMap; +import dev.cerus.maps.api.Frame; +import dev.cerus.maps.api.version.PacketListener; +import dev.cerus.maps.api.version.VersionAdapter; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Level; +import java.util.stream.Collectors; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy; +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.server.network.PlayerConnection; +import net.minecraft.server.network.ServerCommonPacketListenerImpl; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.level.saveddata.maps.MapIcon; +import net.minecraft.world.level.saveddata.maps.MapId; +import net.minecraft.world.level.saveddata.maps.WorldMap; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.plugin.java.JavaPlugin; + +public class VersionAdapter21R1 implements VersionAdapter { + + private Field netManField; + + @Override + public void spawnBarrierParticle(final Player player, final Location loc) { + player.spawnParticle(Particle.BLOCK_MARKER, loc, 1, Material.BARRIER.createBlockData()); + } + + @Override + public Object makeMapPacket(final boolean ignoreBounds, final ClientsideMap map) { + final int x = ignoreBounds ? 0 : map.getX(); + final int y = ignoreBounds ? 0 : map.getY(); + final int w = ignoreBounds ? 128 : Math.max(1, map.getWidth()); + final int h = ignoreBounds ? 128 : Math.max(1, map.getHeight()); + + final byte[] data; + if (ignoreBounds) { + data = map.getData(); + } else { + data = new byte[w * h]; + for (int xx = 0; xx < w; ++xx) { + for (int yy = 0; yy < h; ++yy) { + data[xx + yy * w] = map.getData()[x + xx + (y + yy) * 128]; + } + } + } + + return new PacketPlayOutMap( + new MapId(map.getId()), + (byte) 0, + true, + map.getMarkers().stream() + .map(cursor -> new MapIcon( + BuiltInRegistries.at.c(cursor.getType()).get(), + cursor.getCompressedX(), + cursor.getCompressedY(), + cursor.getDirection(), + !cursor.hasCaption() ? Optional.empty() : parse(cursor.getCaptionString()) + )) + .collect(Collectors.toList()), + new WorldMap.b( + x, + y, + w, + h, + data + ) + ); + } + + private Optional parse(String s) { + if (s == null) { + return Optional.empty(); + } + JsonElement element = JsonParser.parseString(s); + if (element == null) { + return Optional.empty(); + } + return ComponentSerialization.a.parse(JsonOps.INSTANCE, element).result(); + } + + @Override + public Object makeFramePacket(final int frameId, final boolean visible, final ClientsideMap map) { + final org.bukkit.inventory.ItemStack mapItem = new org.bukkit.inventory.ItemStack(Material.FILLED_MAP, 1); + final MapMeta mapMeta = (MapMeta) mapItem.getItemMeta(); + mapMeta.setMapId(map.getId()); + mapItem.setItemMeta(mapMeta); + + final List> dwItems = Arrays.asList( + new DataWatcher.c<>(8, DataWatcherRegistry.h, CraftItemStack.asNMSCopy(mapItem)), + new DataWatcher.c<>(0, DataWatcherRegistry.a, (byte) (visible ? 0 : 0x20)) + ); + return new PacketPlayOutEntityMetadata(frameId, dwItems); + } + + @Override + public Object makeFrameSpawnPacket(final Frame frame) { + return new PacketPlayOutSpawnEntity( + frame.getEntityId(), + UUID.randomUUID(), + frame.getPosX(), + frame.getPosY(), + frame.getPosZ(), + frame.getFacing() == BlockFace.DOWN ? 90 : frame.getFacing() == BlockFace.UP ? -90 : 0, + switch (frame.getFacing()) { + case NORTH -> -180; + case EAST -> -90; + case WEST -> 90; + default -> 0; + }, + frame.isGlowing() ? EntityTypes.V : EntityTypes.ai, + switch (frame.getFacing()) { + case UP -> 1; + case NORTH -> 2; + case SOUTH -> 3; + case WEST -> 4; + case EAST -> 5; + default -> 0; + }, + new Vec3D(0, 0, 0), + switch (frame.getFacing()) { + case NORTH -> -180; + case EAST -> -90; + case WEST -> 90; + default -> 0; + } + ); + } + + @Override + public Object makeFrameDespawnPacket(final Frame frame) { + return new PacketPlayOutEntityDestroy(frame.getEntityId()); + } + + @Override + public void sendPacket(final Player player, final Object packet) { + ((CraftPlayer) player).getHandle().c.b((Packet) packet); + } + + @Override + public void inject(final Player player, final PacketListener listener, final JavaPlugin plugin) { + final NetworkManager networkManager; + try { + networkManager = this.getNetworkManager(((CraftPlayer) player).getHandle().c); + } catch (final IllegalAccessException | NoSuchFieldException e) { + plugin.getLogger().log(Level.WARNING, "Failed to inject packet handler into player %s".formatted(player.getName()), e); + return; + } + networkManager.n.pipeline().addBefore("packet_handler", "maps_listener", new PacketHandler21R1(player, listener, plugin)); + } + + private NetworkManager getNetworkManager(final PlayerConnection b) throws IllegalAccessException, NoSuchFieldException { + if (this.netManField == null) { + this.netManField = ServerCommonPacketListenerImpl.class.getDeclaredField("e"); + this.netManField.setAccessible(true); + } + return (NetworkManager) this.netManField.get(b); + } + +} diff --git a/common/pom.xml b/common/pom.xml index 5cdfb86..dea754b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 diff --git a/plugin/pom.xml b/plugin/pom.xml index 476cb95..d81e1d2 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -5,7 +5,7 @@ parent dev.cerus.maps - 3.8.5 + 3.8.6 4.0.0 @@ -46,7 +46,7 @@ co.aikar acf-bukkit - 0.5.0-SNAPSHOT + 0.5.1-SNAPSHOT compile @@ -122,6 +122,12 @@ ${parent.version} compile + + dev.cerus.maps + bukkit-21_R1 + ${parent.version} + compile + diff --git a/plugin/src/main/java/dev/cerus/maps/version/VersionAdapterFactory.java b/plugin/src/main/java/dev/cerus/maps/version/VersionAdapterFactory.java index 9180c1a..3f396de 100644 --- a/plugin/src/main/java/dev/cerus/maps/version/VersionAdapterFactory.java +++ b/plugin/src/main/java/dev/cerus/maps/version/VersionAdapterFactory.java @@ -6,7 +6,7 @@ public class VersionAdapterFactory { public static final String MIN_VER = "1.16.5"; - public static final String MAX_VER = "1.20.6"; + public static final String MAX_VER = "1.21"; public VersionAdapter makeAdapter() { String version = Bukkit.getVersion(); @@ -24,6 +24,7 @@ public VersionAdapter makeAdapter() { case "1.20.2" -> new VersionAdapter20R2(); case "1.20.3", "1.20.4" -> new VersionAdapter20R3(); case "1.20.5", "1.20.6" -> new VersionAdapter20R4(); + case "1.21" -> new VersionAdapter21R1(); default -> null; }; } diff --git a/pom.xml b/pom.xml index a3b2211..4f8cbe3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.cerus.maps parent pom - 3.8.5 + 3.8.6 common bukkit-16_R3 @@ -21,6 +21,7 @@ bukkit-20_R2 bukkit-20_R3 bukkit-20_R4 + bukkit-21_R1 plugin