diff --git a/build.gradle.kts b/build.gradle.kts index a98ff0c..b638aa5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,6 +22,7 @@ repositories { maven("https://jitpack.io") maven("https://maven.enginehub.org/repo/") maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.viaversion.com") } dependencies { @@ -38,6 +39,9 @@ dependencies { implementation(platform("com.intellectualsites.bom:bom-newest:1.37")) compileOnly("com.intellectualsites.plotsquared:plotsquared-core") compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } + + // ViaVersion support + compileOnly("com.viaversion:viaversion-api:4.7.0") } tasks { diff --git a/src/main/java/com/moulberry/axiom/AxiomConstants.java b/src/main/java/com/moulberry/axiom/AxiomConstants.java index f553d14..11de348 100644 --- a/src/main/java/com/moulberry/axiom/AxiomConstants.java +++ b/src/main/java/com/moulberry/axiom/AxiomConstants.java @@ -1,16 +1,10 @@ package com.moulberry.axiom; -import net.minecraft.core.BlockPos; import org.bukkit.NamespacedKey; public class AxiomConstants { - public static final long MIN_POSITION_LONG = BlockPos.asLong(-33554432, -2048, -33554432); - static { - if (MIN_POSITION_LONG != 0b1000000000000000000000000010000000000000000000000000100000000000L) { - throw new Error("BlockPos representation changed!"); - } - } + public static final long MIN_POSITION_LONG = 0b1000000000000000000000000010000000000000000000000000100000000000L; public static final int API_VERSION = 5; public static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index"); diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index 0b096eb..b187d2b 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -1,105 +1,67 @@ package com.moulberry.axiom; -import com.moulberry.axiom.buffer.CompressedBlockEntity; +import com.moulberry.axiom.integration.PaperFailMoveListener; import com.moulberry.axiom.packet.*; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.papermc.paper.event.player.PlayerFailMoveEvent; -import io.papermc.paper.network.ChannelInitializeListener; -import io.papermc.paper.network.ChannelInitializeListenerHolder; -import net.kyori.adventure.key.Key; -import net.minecraft.network.Connection; -import net.minecraft.network.ConnectionProtocol; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.PacketFlow; -import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket; -import org.bukkit.*; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; +import org.bukkit.Bukkit; import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.plugin.messaging.Messenger; -import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.*; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; public class AxiomPaper extends JavaPlugin implements Listener { - @Override - public void onEnable() { - Bukkit.getPluginManager().registerEvents(this, this); - CompressedBlockEntity.initialize(this); + public static final String PERMISSION = "axiom.*"; - Messenger msg = Bukkit.getMessenger(); - - msg.registerOutgoingPluginChannel(this, "axiom:enable"); - msg.registerOutgoingPluginChannel(this, "axiom:initialize_hotbars"); - msg.registerOutgoingPluginChannel(this, "axiom:set_editor_views"); - msg.registerOutgoingPluginChannel(this, "axiom:block_entities"); - - final Set activeAxiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - msg.registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this, activeAxiomPlayers)); - msg.registerIncomingPluginChannel(this, "axiom:set_gamemode", new SetGamemodePacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:set_fly_speed", new SetFlySpeedPacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:set_block", new SetBlockPacketListener(this)); - msg.registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", new SetHotbarSlotPacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", new SwitchActiveHotbarPacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:teleport", new TeleportPacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:set_editor_views", new SetEditorViewsPacketListener()); - msg.registerIncomingPluginChannel(this, "axiom:request_block_entity", new RequestBlockEntityPacketListener(this)); - - SetBlockBufferPacketListener setBlockBufferPacketListener = new SetBlockBufferPacketListener(this); - - ChannelInitializeListenerHolder.addListener(Key.key("axiom:handle_big_payload"), new ChannelInitializeListener() { - @Override - public void afterInitChannel(@NonNull Channel channel) { - var packets = ConnectionProtocol.PLAY.getPacketsByIds(PacketFlow.SERVERBOUND); - int payloadId = -1; - for (Map.Entry>> entry : packets.entrySet()) { - if (entry.getValue() == ServerboundCustomPayloadPacket.class) { - payloadId = entry.getKey(); - break; - } - } - if (payloadId < 0) { - throw new RuntimeException("Failed to find ServerboundCustomPayloadPacket id"); - } - - Connection connection = (Connection) channel.pipeline().get("packet_handler"); - channel.pipeline().addBefore("decoder", "axiom-big-payload-handler", - new AxiomBigPayloadHandler(payloadId, connection, setBlockBufferPacketListener)); - } - }); - - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> { - HashSet newActiveAxiomPlayers = new HashSet<>(); - - for (Player player : Bukkit.getServer().getOnlinePlayers()) { - if (activeAxiomPlayers.contains(player.getUniqueId())) { - if (!player.hasPermission("axiom.*")) { - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeBoolean(false); - player.sendPluginMessage(this, "axiom:enable", buf.accessByteBufWithCorrectSize()); - } else { - newActiveAxiomPlayers.add(player.getUniqueId()); - } - } - } - - activeAxiomPlayers.clear(); - activeAxiomPlayers.addAll(newActiveAxiomPlayers); - }, 20, 20); + private static AxiomPaper instance; + public static Plugin getPlugin() { + return instance; } - @EventHandler - public void onFailMove(PlayerFailMoveEvent event) { - if (event.getPlayer().hasPermission("axiom.*") && - event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) { - event.setAllowed(true); + private static final Map listeners = new ConcurrentHashMap<>(); + + public static AxiomPacketListener getListener(String channel) { + return listeners.get(channel); + } + + @Override + public void onEnable() { + instance = this; + + try { + Bukkit.getPluginManager().registerEvents(new PaperFailMoveListener(), this); + } catch (NoClassDefFoundError e) { + getLogger().log(Level.WARNING, "Axiom players may move too quickly according to the server. Use a current Paper version or increase the 'moved-too-quickly-multiplier' in spigot.yml."); } + + for(OutChannel channel : OutChannel.values()) { + Bukkit.getMessenger().registerOutgoingPluginChannel(this, channel.getChannel()); + } + + //TODO multiversion (Except setGamemode) + registerInChannel("hello", new HelloPacketListener()); + registerInChannel("set_gamemode", new SetGamemodePacketListener()); + registerInChannel("set_fly_speed", new SetFlySpeedPacketListener()); + registerInChannel("set_block", new SetBlockPacketListener()); + registerInChannel("set_hotbar_slot", new SetHotbarSlotPacketListener()); + registerInChannel("switch_active_hotbar", new SwitchActiveHotbarPacketListener()); + registerInChannel("teleport", new TeleportPacketListener()); + registerInChannel("set_editor_views", new SetEditorViewsPacketListener()); + registerInChannel("request_block_entity", new RequestBlockEntityPacketListener()); + registerInChannel("handle_big_payload", new SetBlockBufferPacketListener()); + } + + private void registerInChannel(String channel, AxiomPacketListener handler) { + listeners.put(channel, handler); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:" + channel, (ch, player, message) -> { + if (!player.hasPermission(PERMISSION)) + return; + + handler.onMessage(player, Unpooled.wrappedBuffer(message)); + }); } } diff --git a/src/main/java/com/moulberry/axiom/OutChannel.java b/src/main/java/com/moulberry/axiom/OutChannel.java new file mode 100644 index 0000000..62dbf7e --- /dev/null +++ b/src/main/java/com/moulberry/axiom/OutChannel.java @@ -0,0 +1,32 @@ +package com.moulberry.axiom; + +import io.netty.buffer.ByteBuf; +import org.bukkit.entity.Player; + +public enum OutChannel { + ENABLE, + INITIALIZE_HOTBARS, + SET_EDITOR_VIEWS, + BLOCK_ENTITIES; + + private final String channel; + + OutChannel() { + channel = "axiom:" + name(); + } + + public String getChannel() { + return channel; + } + + public void send(Player player, ByteBuf buf) { + // trustin https://stackoverflow.com/a/19309312 CC BY-SA 3.0 + byte[] array = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), array); + send(player, array); + } + + public void send(Player player, byte[] array) { + player.sendPluginMessage(AxiomPaper.getPlugin(), channel, array); + } +} diff --git a/src/main/java/com/moulberry/axiom/View.java b/src/main/java/com/moulberry/axiom/View.java index 5dd2138..7d219dd 100644 --- a/src/main/java/com/moulberry/axiom/View.java +++ b/src/main/java/com/moulberry/axiom/View.java @@ -2,8 +2,6 @@ package com.moulberry.axiom; import com.moulberry.axiom.persistence.UUIDDataType; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java index 76f9bba..f57e400 100644 --- a/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java +++ b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java @@ -3,7 +3,6 @@ package com.moulberry.axiom.buffer; import com.github.luben.zstd.Zstd; import com.github.luben.zstd.ZstdDictCompress; import com.github.luben.zstd.ZstdDictDecompress; -import com.moulberry.axiom.AxiomPaper; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; import net.minecraft.network.FriendlyByteBuf; @@ -13,16 +12,15 @@ import java.util.Objects; public record CompressedBlockEntity(int originalSize, byte compressionDict, byte[] compressed) { - private static ZstdDictCompress zstdDictCompress = null; - private static ZstdDictDecompress zstdDictDecompress = null; - - public static void initialize(AxiomPaper plugin) { - try (InputStream is = Objects.requireNonNull(plugin.getResource("zstd_dictionaries/block_entities_v1.dict"))) { + private static final ZstdDictCompress zstdDictCompress; + private static final ZstdDictDecompress zstdDictDecompress; + static { + try (InputStream is = Objects.requireNonNull(CompressedBlockEntity.class.getClassLoader().getResourceAsStream("zstd_dictionaries/block_entities_v1.dict"))) { byte[] bytes = is.readAllBytes(); zstdDictCompress = new ZstdDictCompress(bytes, Zstd.defaultCompressionLevel()); zstdDictDecompress = new ZstdDictDecompress(bytes); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (IOException e) { + throw new IllegalStateException(e); } } diff --git a/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java b/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java index 438872e..b57e307 100644 --- a/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java +++ b/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java @@ -1,7 +1,6 @@ package com.moulberry.axiom.buffer; import com.moulberry.axiom.AxiomConstants; -import com.moulberry.axiom.AxiomPaper; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java index a914830..361ab89 100644 --- a/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java +++ b/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java @@ -1,6 +1,5 @@ package com.moulberry.axiom.event; -import org.bukkit.GameMode; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; diff --git a/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java index 86da786..e3f110b 100644 --- a/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java +++ b/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java @@ -1,7 +1,6 @@ package com.moulberry.axiom.event; import org.bukkit.GameMode; -import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; diff --git a/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java index 2b7f800..bed8a60 100644 --- a/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java +++ b/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java @@ -1,7 +1,6 @@ package com.moulberry.axiom.event; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; diff --git a/src/main/java/com/moulberry/axiom/integration/PaperFailMoveListener.java b/src/main/java/com/moulberry/axiom/integration/PaperFailMoveListener.java new file mode 100644 index 0000000..ebb15a8 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/PaperFailMoveListener.java @@ -0,0 +1,18 @@ +package com.moulberry.axiom.integration; + +import com.moulberry.axiom.AxiomPaper; +import io.papermc.paper.event.player.PlayerFailMoveEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class PaperFailMoveListener implements Listener { + + @EventHandler + public void onFailMove(PlayerFailMoveEvent event) { + if (event.getPlayer().hasPermission(AxiomPaper.PERMISSION) && + event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) { + event.setAllowed(true); + } + } + +} diff --git a/src/main/java/com/moulberry/axiom/integration/VersionTranslator.java b/src/main/java/com/moulberry/axiom/integration/VersionTranslator.java new file mode 100644 index 0000000..9597458 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/VersionTranslator.java @@ -0,0 +1,22 @@ +package com.moulberry.axiom.integration; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.function.IntFunction; + +public interface VersionTranslator { + + VersionTranslator impl = Bukkit.getPluginManager().getPlugin("ViaVersion") != null ? new ViaVersionTranslator() : new Dummy(); + + IntFunction blockStateMapper(Player player); + + class Dummy implements VersionTranslator { + + @Override + public IntFunction blockStateMapper(Player player) { + return id -> id; + } + } + +} diff --git a/src/main/java/com/moulberry/axiom/integration/ViaVersionDummy.java b/src/main/java/com/moulberry/axiom/integration/ViaVersionDummy.java new file mode 100644 index 0000000..cd8c822 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/ViaVersionDummy.java @@ -0,0 +1,14 @@ +package com.moulberry.axiom.integration; + +import org.bukkit.entity.Player; + +import java.util.function.IntFunction; + +public class ViaVersionDummy implements VersionTranslator { + + @Override + public IntFunction blockStateMapper(Player player) { + return id -> id; + } + +} diff --git a/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java b/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java new file mode 100644 index 0000000..164eb25 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java @@ -0,0 +1,44 @@ +package com.moulberry.axiom.integration; + +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.protocol.ProtocolManager; +import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; +import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntFunction; + +public class ViaVersionTranslator implements VersionTranslator { + + private final ProtocolManager protocolManager; + private final ServerProtocolVersion serverVersion; + + public ViaVersionTranslator() { + protocolManager = Via.getManager().getProtocolManager(); + serverVersion = Via.getAPI().getServerVersion(); + } + + @Override + public IntFunction blockStateMapper(Player player) { + List path = protocolManager.getProtocolPath(Via.getAPI().getPlayerVersion(player.getUniqueId()), serverVersion.highestSupportedVersion()); + if(path == null) + return id -> id; + + List> mappers = new ArrayList<>(path.size()); + for(ProtocolPathEntry entry : path) { + MappingData mappings = entry.protocol().getMappingData(); + if(mappings != null) + mappers.add(mappings::getNewBlockStateId); + } + + return id -> { + for(IntFunction transformer : mappers) + id = transformer.apply(id); + return id; + }; + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java index eb64174..48ef9e0 100644 --- a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java +++ b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java @@ -1,64 +1,63 @@ package com.moulberry.axiom.packet; +import com.moulberry.axiom.AxiomPaper; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.papermc.paper.network.ConnectionEvent; -import net.minecraft.network.Connection; +import net.minecraft.network.ConnectionProtocol; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.PacketFlow; +import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -import java.util.List; +@ChannelHandler.Sharable +public class AxiomBigPayloadHandler extends ChannelInboundHandlerAdapter { -public class AxiomBigPayloadHandler extends ByteToMessageDecoder { + private static final int PLUGINMESSAGE_PACKETID = ConnectionProtocol.PLAY.getPacketId(PacketFlow.SERVERBOUND, new ServerboundCustomPayloadPacket(null, null)); - private static final ResourceLocation SET_BUFFER = new ResourceLocation("axiom", "set_buffer"); - private final int payloadId; - private final Connection connection; - private final SetBlockBufferPacketListener listener; + private final Player player; - public AxiomBigPayloadHandler(int payloadId, Connection connection, SetBlockBufferPacketListener listener) { - this.payloadId = payloadId; - this.connection = connection; - this.listener = listener; + public AxiomBigPayloadHandler(Player player) { + this.player = player; } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - try { - int readerIndex = in.readerIndex(); - int i = in.readableBytes(); - if (i != 0) { - FriendlyByteBuf buf = new FriendlyByteBuf(in); - int packetId = buf.readVarInt(); - - if (packetId == payloadId) { - ResourceLocation identifier = buf.readResourceLocation(); - if (identifier.equals(SET_BUFFER)) { - ServerPlayer player = connection.getPlayer(); - if (player != null && player.getBukkitEntity().hasPermission("axiom.*")) { - if (listener.onReceive(player, buf)) { - return; - } - } - } - } - } - in.readerIndex(readerIndex); - } catch (Exception e) { - e.printStackTrace(); + public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception { + if(!(msg instanceof ByteBuf in)) { + ctx.fireChannelRead(msg); + return; } - ctx.fireChannelRead(in.retain()); + int readerIndexBackup = in.readerIndex(); + + if(in.readableBytes() != 0) { + FriendlyByteBuf buf = new FriendlyByteBuf((ByteBuf) msg); + + if (buf.readVarInt() == PLUGINMESSAGE_PACKETID) { + ResourceLocation identifier = buf.readResourceLocation(); + if (identifier.getNamespace().equals("axiom") && player.hasPermission(AxiomPaper.PERMISSION)) { + AxiomPacketListener listener = AxiomPaper.getListener(identifier.getPath()); + //TODO sync! + listener.onMessage(player, in); + in.release(); + return; + } + } + } + + in.readerIndex(readerIndexBackup); + ctx.fireChannelRead(in); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt == ConnectionEvent.COMPRESSION_THRESHOLD_SET || evt == ConnectionEvent.COMPRESSION_DISABLED) { ctx.channel().pipeline().remove("axiom-big-payload-handler"); - ctx.channel().pipeline().addBefore("decoder", "axiom-big-payload-handler", - new AxiomBigPayloadHandler(payloadId, connection, listener)); + ctx.channel().pipeline().addBefore("decoder", "axiom-big-payload-handler", this); } super.userEventTriggered(ctx, evt); } diff --git a/src/main/java/com/moulberry/axiom/packet/AxiomPacketListener.java b/src/main/java/com/moulberry/axiom/packet/AxiomPacketListener.java new file mode 100644 index 0000000..1c4dc71 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/AxiomPacketListener.java @@ -0,0 +1,10 @@ +package com.moulberry.axiom.packet; + +import io.netty.buffer.ByteBuf; +import org.bukkit.entity.Player; + +public interface AxiomPacketListener { + + void onMessage(Player player, ByteBuf buf); + +} diff --git a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java index e09e520..1b2f7fd 100644 --- a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java @@ -2,49 +2,62 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.OutChannel; import com.moulberry.axiom.View; import com.moulberry.axiom.event.AxiomHandshakeEvent; import com.moulberry.axiom.persistence.ItemStackDataType; import com.moulberry.axiom.persistence.UUIDDataType; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.adventure.text.Component; import net.minecraft.network.FriendlyByteBuf; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.plugin.messaging.PluginMessageListener; -import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; -public class HelloPacketListener implements PluginMessageListener { +public class HelloPacketListener implements AxiomPacketListener, Listener { - private final AxiomPaper plugin; - private final Set activeAxiomPlayers; + private final Set axiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - public HelloPacketListener(AxiomPaper plugin, Set activeAxiomPlayers) { - this.plugin = plugin; - this.activeAxiomPlayers = activeAxiomPlayers; + public HelloPacketListener() { + //TODO cleanup (side effects of class) + Bukkit.getPluginManager().registerEvents(this, AxiomPaper.getPlugin()); + Bukkit.getScheduler().scheduleSyncRepeatingTask(AxiomPaper.getPlugin(), () -> axiomPlayers.removeIf(player -> { + if (!player.hasPermission(AxiomPaper.PERMISSION)) { + OutChannel.ENABLE.send(player, new byte[] { 0 }); + return true; + } + + return false; + }), 20, 20); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + axiomPlayers.remove(event.getPlayer()); } @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(Player player, ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); int apiVersion = friendlyByteBuf.readVarInt(); friendlyByteBuf.readNbt(); // Discard if (apiVersion != AxiomConstants.API_VERSION) { - player.kick(Component.text("Unsupported Axiom API Version. Server supports " + AxiomConstants.API_VERSION + - ", while client is " + apiVersion)); + player.sendMessage(Component.text("Unsupported Axiom API Version. Server supports " + AxiomConstants.API_VERSION + ", while client is " + apiVersion)); return; } @@ -55,52 +68,54 @@ public class HelloPacketListener implements PluginMessageListener { return; } - activeAxiomPlayers.add(player.getUniqueId()); + axiomPlayers.add(player); + //TODO thread safety + ((CraftPlayer)player).getHandle().connection.connection.channel.pipeline().addBefore("decoder", "axiom-big-payload-handler", new AxiomBigPayloadHandler(player)); // Enable - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeBoolean(true); - buf.writeByte(0); // todo: world properties - buf.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size - buf.writeBoolean(false); // No source info - buf.writeBoolean(false); // No source settings - buf.writeVarInt(5); // Maximum Reach - buf.writeVarInt(16); // Max editor views - buf.writeBoolean(true); // Editable Views - player.sendPluginMessage(this.plugin, "axiom:enable", buf.accessByteBufWithCorrectSize()); + FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer()); + out.writeBoolean(true); + out.writeByte(0); // todo: world properties + out.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size + out.writeBoolean(false); // No source info + out.writeBoolean(false); // No source settings + out.writeVarInt(5); // Maximum Reach + out.writeVarInt(16); // Max editor views + out.writeBoolean(true); // Editable Views + OutChannel.ENABLE.send(player, out); // Initialize Hotbars PersistentDataContainer container = player.getPersistentDataContainer(); int activeHotbarIndex = container.getOrDefault(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0); PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); if (hotbarItems != null) { - buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeByte((byte) activeHotbarIndex); + out = new FriendlyByteBuf(Unpooled.buffer()); + out.writeByte((byte) activeHotbarIndex); for (int i=0; i<9*9; i++) { // Ignore selected hotbar if (i / 9 == activeHotbarIndex) { - buf.writeItem(net.minecraft.world.item.ItemStack.EMPTY); + out.writeItem(net.minecraft.world.item.ItemStack.EMPTY); } else { ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE); - buf.writeItem(CraftItemStack.asNMSCopy(stack)); + out.writeItem(CraftItemStack.asNMSCopy(stack)); } } - player.sendPluginMessage(this.plugin, "axiom:initialize_hotbars", buf.accessByteBufWithCorrectSize()); + OutChannel.INITIALIZE_HOTBARS.send(player, out); } // Initialize Views UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE); if (activeView != null) { - buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeUUID(activeView); + out = new FriendlyByteBuf(Unpooled.buffer()); + out.writeUUID(activeView); PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY); - buf.writeVarInt(views.length); + out.writeVarInt(views.length); for (PersistentDataContainer view : views) { - View.load(view).write(buf); + View.load(view).write(out); } - player.sendPluginMessage(this.plugin, "axiom:set_editor_views", buf.accessByteBufWithCorrectSize()); + OutChannel.SET_EDITOR_VIEWS.send(player, out); } } diff --git a/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java b/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java index 84d3cf0..fc224c5 100644 --- a/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java @@ -1,9 +1,11 @@ package com.moulberry.axiom.packet; -import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.OutChannel; import com.moulberry.axiom.buffer.CompressedBlockEntity; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.longs.*; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -16,27 +18,20 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; import java.io.ByteArrayOutputStream; -public class RequestBlockEntityPacketListener implements PluginMessageListener { - - private final AxiomPaper plugin; - - public RequestBlockEntityPacketListener(AxiomPaper plugin) { - this.plugin = plugin; - } +public class RequestBlockEntityPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); long id = friendlyByteBuf.readLong(); if (!bukkitPlayer.hasPermission("axiom.*")) { // We always send an 'empty' response in order to make the client happy - sendEmptyResponse(bukkitPlayer, id); + sendEmptyResponse(bukkitPlayer, id); //TODO return; } @@ -70,21 +65,21 @@ public class RequestBlockEntityPacketListener implements PluginMessageListener { } // Send response packet - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); - buf.writeLong(id); - buf.writeVarInt(map.size()); + FriendlyByteBuf out = new FriendlyByteBuf(Unpooled.buffer(16)); + out.writeLong(id); + out.writeVarInt(map.size()); for (Long2ObjectMap.Entry entry : map.long2ObjectEntrySet()) { - buf.writeLong(entry.getLongKey()); - entry.getValue().write(buf); + out.writeLong(entry.getLongKey()); + entry.getValue().write(out); } - bukkitPlayer.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); + OutChannel.BLOCK_ENTITIES.send(bukkitPlayer, out); } private void sendEmptyResponse(Player player, long id) { - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); + ByteBuf buf = Unpooled.buffer(16); buf.writeLong(id); buf.writeByte(0); // no block entities - player.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); + OutChannel.BLOCK_ENTITIES.send(player, buf); } } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index 44b7479..e9d8f6d 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -1,26 +1,11 @@ package com.moulberry.axiom.packet; -import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.buffer.BiomeBuffer; import com.moulberry.axiom.buffer.BlockBuffer; import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.integration.RegionProtection; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.Flags; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.RegionContainer; -import com.sk89q.worldguard.protection.regions.RegionQuery; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import net.minecraft.core.BlockPos; @@ -48,26 +33,19 @@ import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LightEngine; import org.bukkit.Bukkit; -import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; -import org.jetbrains.annotations.NotNull; import xyz.jpenilla.reflectionremapper.ReflectionRemapper; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; -public class SetBlockBufferPacketListener { +public class SetBlockBufferPacketListener implements AxiomPacketListener { - private final AxiomPaper plugin; private final Method updateBlockEntityTicker; - public SetBlockBufferPacketListener(AxiomPaper plugin) { - this.plugin = plugin; - + public SetBlockBufferPacketListener() { ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class); @@ -80,9 +58,11 @@ public class SetBlockBufferPacketListener { } } - public boolean onReceive(ServerPlayer player, FriendlyByteBuf friendlyByteBuf) { - MinecraftServer server = player.getServer(); - if (server == null) return false; + @Override + public void onMessage(Player player, ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); + MinecraftServer server = ((CraftServer)player.getServer()).getServer(); + if (server == null) return; ResourceKey worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers @@ -102,205 +82,199 @@ public class SetBlockBufferPacketListener { } else { throw new RuntimeException("Unknown buffer type: " + type); } - - return true; } - private void applyBlockBuffer(ServerPlayer player, MinecraftServer server, BlockBuffer buffer, ResourceKey worldKey) { - server.execute(() -> { - ServerLevel world = server.getLevel(worldKey); - if (world == null) return; + private void applyBlockBuffer(Player player, MinecraftServer server, BlockBuffer buffer, ResourceKey worldKey) { + ServerLevel world = server.getLevel(worldKey); + if (world == null) return; - // Call AxiomModifyWorldEvent event - AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player.getBukkitEntity(), world.getWorld()); - Bukkit.getPluginManager().callEvent(modifyWorldEvent); - if (modifyWorldEvent.isCancelled()) return; + // Call AxiomModifyWorldEvent event + AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world.getWorld()); + Bukkit.getPluginManager().callEvent(modifyWorldEvent); + if (modifyWorldEvent.isCancelled()) return; - RegionProtection regionProtection = new RegionProtection(player.getBukkitEntity(), world.getWorld()); + RegionProtection regionProtection = new RegionProtection(player, world.getWorld()); - // Allowed, apply buffer - BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); + // Allowed, apply buffer + BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); - var lightEngine = world.getChunkSource().getLightEngine(); + var lightEngine = world.getChunkSource().getLightEngine(); - BlockState emptyState = BlockBuffer.EMPTY_STATE; + BlockState emptyState = BlockBuffer.EMPTY_STATE; - for (Long2ObjectMap.Entry> entry : buffer.entrySet()) { - int cx = BlockPos.getX(entry.getLongKey()); - int cy = BlockPos.getY(entry.getLongKey()); - int cz = BlockPos.getZ(entry.getLongKey()); - PalettedContainer container = entry.getValue(); + for (Long2ObjectMap.Entry> entry : buffer.entrySet()) { + int cx = BlockPos.getX(entry.getLongKey()); + int cy = BlockPos.getY(entry.getLongKey()); + int cz = BlockPos.getZ(entry.getLongKey()); + PalettedContainer container = entry.getValue(); - if (cy < world.getMinSection() || cy >= world.getMaxSection()) { - continue; + if (cy < world.getMinSection() || cy >= world.getMaxSection()) { + continue; + } + + if (!regionProtection.canBuildInSection(cx, cy, cz)) { + continue; + } + + LevelChunk chunk = world.getChunk(cx, cz); + chunk.setUnsaved(true); + + LevelChunkSection section = chunk.getSection(world.getSectionIndexFromSectionY(cy)); + PalettedContainer sectionStates = section.getStates(); + boolean hasOnlyAir = section.hasOnlyAir(); + + Heightmap worldSurface = null; + Heightmap oceanFloor = null; + Heightmap motionBlocking = null; + Heightmap motionBlockingNoLeaves = null; + for (Map.Entry heightmap : chunk.getHeightmaps()) { + switch (heightmap.getKey()) { + case WORLD_SURFACE -> worldSurface = heightmap.getValue(); + case OCEAN_FLOOR -> oceanFloor = heightmap.getValue(); + case MOTION_BLOCKING -> motionBlocking = heightmap.getValue(); + case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue(); + default -> {} } + } - if (!regionProtection.canBuildInSection(cx, cy, cz)) { - continue; - } + Short2ObjectMap blockEntityChunkMap = buffer.getBlockEntityChunkMap(entry.getLongKey()); - LevelChunk chunk = world.getChunk(cx, cz); - chunk.setUnsaved(true); + sectionStates.acquire(); + try { + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + BlockState blockState = container.get(x, y, z); + if (blockState == emptyState) continue; - LevelChunkSection section = chunk.getSection(world.getSectionIndexFromSectionY(cy)); - PalettedContainer sectionStates = section.getStates(); - boolean hasOnlyAir = section.hasOnlyAir(); + int bx = cx*16 + x; + int by = cy*16 + y; + int bz = cz*16 + z; - Heightmap worldSurface = null; - Heightmap oceanFloor = null; - Heightmap motionBlocking = null; - Heightmap motionBlockingNoLeaves = null; - for (Map.Entry heightmap : chunk.getHeightmaps()) { - switch (heightmap.getKey()) { - case WORLD_SURFACE -> worldSurface = heightmap.getValue(); - case OCEAN_FLOOR -> oceanFloor = heightmap.getValue(); - case MOTION_BLOCKING -> motionBlocking = heightmap.getValue(); - case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue(); - default -> {} - } - } + blockPos.set(bx, by, bz); - Short2ObjectMap blockEntityChunkMap = buffer.getBlockEntityChunkMap(entry.getLongKey()); + if (hasOnlyAir && blockState.isAir()) { + continue; + } - sectionStates.acquire(); - try { - for (int x = 0; x < 16; x++) { - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - BlockState blockState = container.get(x, y, z); - if (blockState == emptyState) continue; + BlockState old = section.setBlockState(x, y, z, blockState, false); + if (blockState != old) { + Block block = blockState.getBlock(); + motionBlocking.update(x, by, z, blockState); + motionBlockingNoLeaves.update(x, by, z, blockState); + oceanFloor.update(x, by, z, blockState); + worldSurface.update(x, by, z, blockState); - int bx = cx*16 + x; - int by = cy*16 + y; - int bz = cz*16 + z; + if (false) { // Full update + old.onRemove(world, blockPos, blockState, false); - blockPos.set(bx, by, bz); - - if (hasOnlyAir && blockState.isAir()) { - continue; + if (sectionStates.get(x, y, z).is(block)) { + blockState.onPlace(world, blockPos, old, false); + } } - BlockState old = section.setBlockState(x, y, z, blockState, false); - if (blockState != old) { - Block block = blockState.getBlock(); - motionBlocking.update(x, by, z, blockState); - motionBlockingNoLeaves.update(x, by, z, blockState); - oceanFloor.update(x, by, z, blockState); - worldSurface.update(x, by, z, blockState); + if (blockState.hasBlockEntity()) { + BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); - if (false) { // Full update - old.onRemove(world, blockPos, blockState, false); - - if (sectionStates.get(x, y, z).is(block)) { - blockState.onPlace(world, blockPos, old, false); + if (blockEntity == null) { + // There isn't a block entity here, create it! + blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); + if (blockEntity != null) { + chunk.addAndRegisterBlockEntity(blockEntity); } - } + } else if (blockEntity.getType().isValid(blockState)) { + // Block entity is here and the type is correct + blockEntity.setBlockState(blockState); - if (blockState.hasBlockEntity()) { - BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); - - if (blockEntity == null) { - // There isn't a block entity here, create it! - blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); - if (blockEntity != null) { - chunk.addAndRegisterBlockEntity(blockEntity); - } - } else if (blockEntity.getType().isValid(blockState)) { - // Block entity is here and the type is correct - blockEntity.setBlockState(blockState); - - try { - this.updateBlockEntityTicker.invoke(chunk, blockEntity); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } else { - // Block entity type isn't correct, we need to recreate it - chunk.removeBlockEntity(blockPos); - - blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); - if (blockEntity != null) { - chunk.addAndRegisterBlockEntity(blockEntity); - } + try { + this.updateBlockEntityTicker.invoke(chunk, blockEntity); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); } - if (blockEntity != null && blockEntityChunkMap != null) { - int key = x | (y << 4) | (z << 8); - CompressedBlockEntity savedBlockEntity = blockEntityChunkMap.get((short) key); - if (savedBlockEntity != null) { - blockEntity.load(savedBlockEntity.decompress()); - } - } - } else if (old.hasBlockEntity()) { + } else { + // Block entity type isn't correct, we need to recreate it chunk.removeBlockEntity(blockPos); - } - world.getChunkSource().blockChanged(blockPos); // todo: maybe simply resend chunk instead of this? - - if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) { - lightEngine.checkBlock(blockPos); + blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); + if (blockEntity != null) { + chunk.addAndRegisterBlockEntity(blockEntity); + } } + if (blockEntity != null && blockEntityChunkMap != null) { + int key = x | (y << 4) | (z << 8); + CompressedBlockEntity savedBlockEntity = blockEntityChunkMap.get((short) key); + if (savedBlockEntity != null) { + blockEntity.load(savedBlockEntity.decompress()); + } + } + } else if (old.hasBlockEntity()) { + chunk.removeBlockEntity(blockPos); + } + + world.getChunkSource().blockChanged(blockPos); // todo: maybe simply resend chunk instead of this? + + if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) { + lightEngine.checkBlock(blockPos); } } } } - } finally { - sectionStates.release(); - } - - boolean nowHasOnlyAir = section.hasOnlyAir(); - if (hasOnlyAir != nowHasOnlyAir) { - world.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir); } + } finally { + sectionStates.release(); } - }); + + boolean nowHasOnlyAir = section.hasOnlyAir(); + if (hasOnlyAir != nowHasOnlyAir) { + world.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir); + } + } } private void applyBiomeBuffer(MinecraftServer server, BiomeBuffer biomeBuffer, ResourceKey worldKey) { - server.execute(() -> { - ServerLevel world = server.getLevel(worldKey); - if (world == null) return; + ServerLevel world = server.getLevel(worldKey); + if (world == null) return; - Set changedChunks = new HashSet<>(); + Set changedChunks = new HashSet<>(); - int minSection = world.getMinSection(); - int maxSection = world.getMaxSection(); + int minSection = world.getMinSection(); + int maxSection = world.getMaxSection(); - Optional> registryOptional = world.registryAccess().registry(Registries.BIOME); - if (registryOptional.isEmpty()) return; + Optional> registryOptional = world.registryAccess().registry(Registries.BIOME); + if (registryOptional.isEmpty()) return; - Registry registry = registryOptional.get(); + Registry registry = registryOptional.get(); - biomeBuffer.forEachEntry((x, y, z, biome) -> { - int cy = y >> 2; - if (cy < minSection || cy >= maxSection) { - return; - } - - var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false); - if (chunk == null) return; - - var section = chunk.getSection(cy - minSection); - PalettedContainer> container = (PalettedContainer>) section.getBiomes(); - - var holder = registry.getHolder(biome); - if (holder.isPresent()) { - container.set(x & 3, y & 3, z & 3, holder.get()); - changedChunks.add(chunk); - } - }); - - var chunkMap = world.getChunkSource().chunkMap; - HashMap> map = new HashMap<>(); - for (LevelChunk chunk : changedChunks) { - chunk.setUnsaved(true); - ChunkPos chunkPos = chunk.getPos(); - for (ServerPlayer serverPlayer2 : chunkMap.getPlayers(chunkPos, false)) { - map.computeIfAbsent(serverPlayer2, serverPlayer -> new ArrayList<>()).add(chunk); - } + biomeBuffer.forEachEntry((x, y, z, biome) -> { + int cy = y >> 2; + if (cy < minSection || cy >= maxSection) { + return; + } + + var chunk = (LevelChunk) world.getChunk(x >> 2, z >> 2, ChunkStatus.FULL, false); + if (chunk == null) return; + + var section = chunk.getSection(cy - minSection); + PalettedContainer> container = (PalettedContainer>) section.getBiomes(); + + var holder = registry.getHolder(biome); + if (holder.isPresent()) { + container.set(x & 3, y & 3, z & 3, holder.get()); + changedChunks.add(chunk); } - map.forEach((serverPlayer, list) -> serverPlayer.connection.send(ClientboundChunksBiomesPacket.forChunks(list))); }); + + var chunkMap = world.getChunkSource().chunkMap; + HashMap> map = new HashMap<>(); + for (LevelChunk chunk : changedChunks) { + chunk.setUnsaved(true); + ChunkPos chunkPos = chunk.getPos(); + for (ServerPlayer serverPlayer2 : chunkMap.getPlayers(chunkPos, false)) { + map.computeIfAbsent(serverPlayer2, serverPlayer -> new ArrayList<>()).add(chunk); + } + } + map.forEach((serverPlayer, list) -> serverPlayer.connection.send(ClientboundChunksBiomesPacket.forChunks(list))); } } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java index f1a5c7c..8585f24 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java @@ -1,8 +1,8 @@ package com.moulberry.axiom.packet; -import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.event.AxiomModifyWorldEvent; -import io.netty.buffer.Unpooled; +import com.moulberry.axiom.integration.VersionTranslator; +import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.FriendlyByteBuf; @@ -19,23 +19,18 @@ import net.minecraft.world.level.lighting.LightEngine; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; import xyz.jpenilla.reflectionremapper.ReflectionRemapper; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; -import java.util.logging.Level; -public class SetBlockPacketListener implements PluginMessageListener { +public class SetBlockPacketListener implements AxiomPacketListener { - private final AxiomPaper plugin; private final Method updateBlockEntityTicker; - public SetBlockPacketListener(AxiomPaper plugin) { - this.plugin = plugin; - + public SetBlockPacketListener() { ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); String methodName = reflectionRemapper.remapMethodName(LevelChunk.class, "updateBlockEntityTicker", BlockEntity.class); @@ -49,20 +44,16 @@ public class SetBlockPacketListener implements PluginMessageListener { } @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { - if (!bukkitPlayer.hasPermission("axiom.*")) { - return; - } - + public void onMessage(@NotNull Player bukkitPlayer, @NotNull ByteBuf buf) { // Check if player is allowed to modify this world AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(bukkitPlayer, bukkitPlayer.getWorld()); Bukkit.getPluginManager().callEvent(modifyWorldEvent); if (modifyWorldEvent.isCancelled()) return; // Read packet - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); BlockPos blockPos = friendlyByteBuf.readBlockPos(); - BlockState blockState = friendlyByteBuf.readById(Block.BLOCK_STATE_REGISTRY); + BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(VersionTranslator.impl.blockStateMapper(bukkitPlayer).apply(friendlyByteBuf.readVarInt())); boolean updateNeighbors = friendlyByteBuf.readBoolean(); int sequenceId = friendlyByteBuf.readInt(); diff --git a/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java index 1fd9bda..642fd1d 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java @@ -3,26 +3,21 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.View; import com.moulberry.axiom.persistence.UUIDDataType; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import net.minecraft.network.FriendlyByteBuf; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.UUID; -public class SetEditorViewsPacketListener implements PluginMessageListener { +public class SetEditorViewsPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); UUID uuid = friendlyByteBuf.readUUID(); List views = friendlyByteBuf.readList(View::read); diff --git a/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java index 83b5d44..b44c283 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java @@ -1,28 +1,17 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.event.AxiomFlySpeedChangeEvent; -import com.moulberry.axiom.event.AxiomGameModeChangeEvent; -import io.netty.buffer.Unpooled; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.level.GameType; +import io.netty.buffer.ByteBuf; import org.bukkit.Bukkit; -import org.bukkit.GameMode; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; -public class SetFlySpeedPacketListener implements PluginMessageListener { +public class SetFlySpeedPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - float flySpeed = friendlyByteBuf.readFloat(); + public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + float flySpeed = buf.readFloat(); // Call event AxiomFlySpeedChangeEvent flySpeedChangeEvent = new AxiomFlySpeedChangeEvent(player, flySpeed); diff --git a/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java index c9c18e3..0eeaf04 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java @@ -1,34 +1,26 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.event.AxiomGameModeChangeEvent; -import io.netty.buffer.Unpooled; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.level.GameType; +import io.netty.buffer.ByteBuf; import org.bukkit.Bukkit; import org.bukkit.GameMode; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; -import org.jetbrains.annotations.NotNull; -public class SetGamemodePacketListener implements PluginMessageListener { +public class SetGamemodePacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - GameType gameType = GameType.byId(friendlyByteBuf.readByte()); + public void onMessage(Player player, ByteBuf buf) { + @SuppressWarnings("deprecation") + GameMode gameMode = GameMode.getByValue(buf.readByte()); + assert gameMode != null; // Call event - AxiomGameModeChangeEvent gameModeChangeEvent = new AxiomGameModeChangeEvent(player, GameMode.getByValue(gameType.getId())); + AxiomGameModeChangeEvent gameModeChangeEvent = new AxiomGameModeChangeEvent(player, gameMode); Bukkit.getPluginManager().callEvent(gameModeChangeEvent); if (gameModeChangeEvent.isCancelled()) return; // Change gamemode - ((CraftPlayer)player).getHandle().setGameMode(gameType); + player.setGameMode(gameMode); } } diff --git a/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java index a91cd63..b9c1f0f 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java @@ -2,26 +2,20 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.persistence.ItemStackDataType; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import net.minecraft.network.FriendlyByteBuf; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; -public class SetHotbarSlotPacketListener implements PluginMessageListener { +public class SetHotbarSlotPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); int index = friendlyByteBuf.readByte(); if (index < 0 || index >= 9*9) return; net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem(); diff --git a/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java index 38badf8..6006180 100644 --- a/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java @@ -2,7 +2,7 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.persistence.ItemStackDataType; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import net.minecraft.network.FriendlyByteBuf; import org.bukkit.GameMode; import org.bukkit.Material; @@ -12,18 +12,13 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; -public class SwitchActiveHotbarPacketListener implements PluginMessageListener { +public class SwitchActiveHotbarPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); int oldHotbarIndex = friendlyByteBuf.readByte(); int activeHotbarIndex = friendlyByteBuf.readByte(); diff --git a/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java index 871cc2a..9bd60eb 100644 --- a/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java @@ -1,26 +1,23 @@ package com.moulberry.axiom.packet; -import com.moulberry.axiom.event.AxiomGameModeChangeEvent; import com.moulberry.axiom.event.AxiomTeleportEvent; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import net.minecraft.core.registries.Registries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.entity.Player; -import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; -public class TeleportPacketListener implements PluginMessageListener { +public class TeleportPacketListener implements AxiomPacketListener { @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(buf); ResourceKey resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); double x = friendlyByteBuf.readDouble(); double y = friendlyByteBuf.readDouble(); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 67339da..ce18f5a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,8 +2,12 @@ name: $name version: $version main: com.moulberry.axiom.AxiomPaper description: $description +softdepend: + - ViaVersion + - WorldGuard authors: - Moulberry + - Lixfel api-version: "$apiVersion" permissions: axiom.*: