From c90a97294ab76faeaaa6e6d4013842f4f64332c6 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 10:34:53 +0800 Subject: [PATCH 1/9] Refactor lambda listeners into classes --- build.gradle.kts | 7 +- .../com/moulberry/axiom/AxiomConstants.java | 22 ++ .../java/com/moulberry/axiom/AxiomPaper.java | 212 +----------------- .../moulberry/axiom/buffer/BlockBuffer.java | 27 +-- .../axiom/packet/HelloPacketListener.java | 98 ++++++++ .../axiom/packet/SetBlockPacketListener.java | 32 ++- .../packet/SetEditorViewsPacketListener.java | 41 ++++ .../packet/SetFlySpeedPacketListener.java | 24 ++ .../packet/SetGamemodePacketListener.java | 24 ++ .../packet/SetHotbarSlotPacketListener.java | 36 +++ .../SwitchActiveHotbarPacketListener.java | 59 +++++ .../axiom/packet/TeleportPacketListener.java | 39 ++++ 12 files changed, 377 insertions(+), 244 deletions(-) create mode 100644 src/main/java/com/moulberry/axiom/AxiomConstants.java create mode 100644 src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java create mode 100644 src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java diff --git a/build.gradle.kts b/build.gradle.kts index 468881e..c0ee842 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,8 +7,8 @@ plugins { id("com.github.johnrengelman.shadow") version "8.1.1" } -group = "com.moulberry.com.moulberry.axiom" -version = "1.0.0-SNAPSHOT" +group = "com.moulberry.axiom" +version = "1.1.0" description = "Serverside component for Axiom on Paper" java { @@ -24,6 +24,9 @@ repositories { dependencies { paperweight.paperDevBundle("1.20.1-R0.1-SNAPSHOT") implementation("xyz.jpenilla:reflection-remapper:0.1.0-SNAPSHOT") + + // Zstd Compression Library + implementation("com.github.luben:zstd-jni:1.5.5-4") } tasks { diff --git a/src/main/java/com/moulberry/axiom/AxiomConstants.java b/src/main/java/com/moulberry/axiom/AxiomConstants.java new file mode 100644 index 0000000..a80c6cd --- /dev/null +++ b/src/main/java/com/moulberry/axiom/AxiomConstants.java @@ -0,0 +1,22 @@ +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 int API_VERSION = 4; + public static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index"); + public static final NamespacedKey HOTBAR_DATA = new NamespacedKey("axiom", "hotbar_data"); + + public static final NamespacedKey ACTIVE_VIEW = new NamespacedKey("axiom", "active_view"); + public static final NamespacedKey VIEWS = new NamespacedKey("axiom", "views"); + +} diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index eff5682..8ad8238 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -1,61 +1,31 @@ package com.moulberry.axiom; -import com.moulberry.axiom.packet.AxiomBigPayloadHandler; -import com.moulberry.axiom.packet.SetBlockBufferPacketListener; -import com.moulberry.axiom.packet.SetBlockPacketListener; -import com.moulberry.axiom.persistence.ItemStackDataType; -import com.moulberry.axiom.persistence.UUIDDataType; +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.kyori.adventure.text.Component; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.Registries; 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 net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.GameType; -import net.minecraft.world.level.Level; import org.bukkit.*; -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.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.UUID; public class AxiomPaper extends JavaPlugin implements Listener { - public static final long MIN_POSITION_LONG = BlockPos.asLong(-33554432, -2048, -33554432); - static { - if (MIN_POSITION_LONG != 0b1000000000000000000000000010000000000000000000000000100000000000L) { - throw new Error("BlockPos representation changed!"); - } - } - - private static final int API_VERSION = 4; - private static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index"); - private static final NamespacedKey HOTBAR_DATA = new NamespacedKey("axiom", "hotbar_data"); - - private static final NamespacedKey ACTIVE_VIEW = new NamespacedKey("axiom", "active_view"); - private static final NamespacedKey VIEWS = new NamespacedKey("axiom", "views"); - @Override public void onEnable() { Bukkit.getPluginManager().registerEvents(this, this); @@ -66,180 +36,14 @@ public class AxiomPaper extends JavaPlugin implements Listener { HashSet activeAxiomPlayers = new HashSet<>(); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:hello", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - int apiVersion = friendlyByteBuf.readVarInt(); - friendlyByteBuf.readNbt(); // Discard - - if (apiVersion != API_VERSION) { - player.kick(Component.text("Unsupported Axiom API Version. Server supports " + API_VERSION + - ", while client is " + apiVersion)); - return; - } - - activeAxiomPlayers.add(player.getUniqueId()); - - // Enable - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeBoolean(true); - buf.writeByte(0); // todo: world properties - buf.writeInt(0x100000); // 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, "axiom:enable", buf.accessByteBufWithCorrectSize()); - - // Initialize Hotbars - PersistentDataContainer container = player.getPersistentDataContainer(); - int activeHotbarIndex = container.getOrDefault(ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) 0); - PersistentDataContainer hotbarItems = container.get(HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); - if (hotbarItems != null) { - buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.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); - } else { - ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE); - buf.writeItem(CraftItemStack.asNMSCopy(stack)); - } - } - player.sendPluginMessage(this, "axiom:initialize_hotbars", buf.accessByteBufWithCorrectSize()); - } - - // Initialize Views - UUID activeView = container.get(ACTIVE_VIEW, UUIDDataType.INSTANCE); - if (activeView != null) { - buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeUUID(activeView); - - PersistentDataContainer[] views = container.get(VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY); - buf.writeVarInt(views.length); - for (PersistentDataContainer view : views) { - View.load(view).write(buf); - } - - player.sendPluginMessage(this, "axiom:set_editor_views", buf.accessByteBufWithCorrectSize()); - } - }); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_gamemode", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - GameType gameType = GameType.byId(friendlyByteBuf.readByte()); - ((CraftPlayer)player).getHandle().setGameMode(gameType); - }); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_fly_speed", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - float flySpeed = friendlyByteBuf.readFloat(); - ((CraftPlayer)player).getHandle().getAbilities().setFlyingSpeed(flySpeed); - }); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this, activeAxiomPlayers)); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_gamemode", new SetGamemodePacketListener()); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_fly_speed", new SetFlySpeedPacketListener()); Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_block", new SetBlockPacketListener(this)); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - int index = friendlyByteBuf.readByte(); - if (index < 0 || index >= 9*9) return; - net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem(); - - PersistentDataContainer container = player.getPersistentDataContainer(); - PersistentDataContainer hotbarItems = container.get(HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); - if (hotbarItems == null) hotbarItems = container.getAdapterContext().newPersistentDataContainer(); - hotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, CraftItemStack.asCraftMirror(nmsStack)); - container.set(HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, hotbarItems); - }); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - int oldHotbarIndex = friendlyByteBuf.readByte(); - int activeHotbarIndex = friendlyByteBuf.readByte(); - - ItemStack[] hotbarItems = new ItemStack[9]; - for (int i=0; i<9; i++) { - hotbarItems[i] = CraftItemStack.asCraftMirror(friendlyByteBuf.readItem()); - } - - PersistentDataContainer container = player.getPersistentDataContainer(); - PersistentDataContainer containerHotbarItems = container.get(HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); - if (containerHotbarItems == null) containerHotbarItems = container.getAdapterContext().newPersistentDataContainer(); - - for (int i=0; i<9; i++) { - if (oldHotbarIndex != activeHotbarIndex) { - int index = oldHotbarIndex*9 + i; - ItemStack stack = player.getInventory().getItem(i); - if (stack == null) { - stack = new ItemStack(Material.AIR); - } else { - stack = stack.clone(); - } - containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, stack); - } - int index = activeHotbarIndex*9 + i; - containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, hotbarItems[i].clone()); - if (player.getGameMode() == GameMode.CREATIVE) player.getInventory().setItem(i, hotbarItems[i]); - } - - container.set(HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, containerHotbarItems); - container.set(ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) activeHotbarIndex); - }); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:teleport", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - ResourceKey resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); - double x = friendlyByteBuf.readDouble(); - double y = friendlyByteBuf.readDouble(); - double z = friendlyByteBuf.readDouble(); - float yRot = friendlyByteBuf.readFloat(); - float xRot = friendlyByteBuf.readFloat(); - - NamespacedKey namespacedKey = new NamespacedKey(resourceKey.location().getNamespace(), resourceKey.location().getPath()); - World world = Bukkit.getWorld(namespacedKey); - if (world != null) { - player.teleport(new Location(world, x, y, z, yRot, xRot)); - } - }); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_editor_views", (channel, player, message) -> { - if (!player.hasPermission("axiom.*")) { - return; - } - - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); - UUID uuid = friendlyByteBuf.readUUID(); - List views = friendlyByteBuf.readList(View::read); - - PersistentDataContainer container = player.getPersistentDataContainer(); - container.set(ACTIVE_VIEW, UUIDDataType.INSTANCE, uuid); - - PersistentDataContainer[] containerArray = new PersistentDataContainer[views.size()]; - for (int i = 0; i < views.size(); i++) { - PersistentDataContainer viewContainer = container.getAdapterContext().newPersistentDataContainer(); - views.get(i).save(viewContainer); - containerArray[i] = viewContainer; - } - container.set(VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY, containerArray); - }); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", new SetHotbarSlotPacketListener()); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", new SwitchActiveHotbarPacketListener()); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:teleport", new TeleportPacketListener()); + Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_editor_views", new SetEditorViewsPacketListener()); SetBlockBufferPacketListener setBlockBufferPacketListener = new SetBlockBufferPacketListener(this); diff --git a/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java b/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java index cb3f90c..77992d3 100644 --- a/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java +++ b/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java @@ -1,9 +1,11 @@ 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 it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.block.Block; @@ -18,8 +20,7 @@ public class BlockBuffer { private final Long2ObjectMap> values; private PalettedContainer last = null; - private long lastId = AxiomPaper.MIN_POSITION_LONG; - private int count; + private long lastId = AxiomConstants.MIN_POSITION_LONG; public BlockBuffer() { this.values = new Long2ObjectOpenHashMap<>(); @@ -29,17 +30,13 @@ public class BlockBuffer { this.values = values; } - public int getCount() { - return this.count; - } - public void save(FriendlyByteBuf friendlyByteBuf) { for (Long2ObjectMap.Entry> entry : this.entrySet()) { friendlyByteBuf.writeLong(entry.getLongKey()); entry.getValue().write(friendlyByteBuf); } - friendlyByteBuf.writeLong(AxiomPaper.MIN_POSITION_LONG); + friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG); } public static BlockBuffer load(FriendlyByteBuf friendlyByteBuf) { @@ -47,7 +44,7 @@ public class BlockBuffer { while (true) { long index = friendlyByteBuf.readLong(); - if (index == AxiomPaper.MIN_POSITION_LONG) break; + if (index == AxiomConstants.MIN_POSITION_LONG) break; PalettedContainer palettedContainer = buffer.getOrCreateSection(index); palettedContainer.read(friendlyByteBuf); @@ -58,7 +55,7 @@ public class BlockBuffer { public void clear() { this.last = null; - this.lastId = AxiomPaper.MIN_POSITION_LONG; + this.lastId = AxiomConstants.MIN_POSITION_LONG; this.values.clear(); } @@ -79,23 +76,11 @@ public class BlockBuffer { public void set(int x, int y, int z, BlockState state) { var container = this.getOrCreateSectionForCoord(x, y, z); var old = container.getAndSet(x & 0xF, y & 0xF, z & 0xF, state); - - if (old == EMPTY_STATE) { - if (state != EMPTY_STATE) this.count += 1; - } else if (state == EMPTY_STATE) { - this.count -= 1; - } } public void set(int cx, int cy, int cz, int lx, int ly, int lz, BlockState state) { var container = this.getOrCreateSection(BlockPos.asLong(cx, cy, cz)); var old = container.getAndSet(lx, ly, lz, state); - - if (old == EMPTY_STATE) { - if (state != EMPTY_STATE) this.count += 1; - } else if (state == EMPTY_STATE) { - this.count -= 1; - } } public BlockState remove(int x, int y, int z) { diff --git a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java new file mode 100644 index 0000000..63041a1 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java @@ -0,0 +1,98 @@ +package com.moulberry.axiom.packet; + +import com.moulberry.axiom.AxiomConstants; +import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.View; +import com.moulberry.axiom.persistence.ItemStackDataType; +import com.moulberry.axiom.persistence.UUIDDataType; +import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.Component; +import net.minecraft.network.FriendlyByteBuf; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +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; + +import java.util.Set; +import java.util.UUID; + +public class HelloPacketListener implements PluginMessageListener { + + private final AxiomPaper plugin; + private final Set activeAxiomPlayers; + + public HelloPacketListener(AxiomPaper plugin, Set activeAxiomPlayers) { + this.plugin = plugin; + this.activeAxiomPlayers = activeAxiomPlayers; + } + + @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)); + 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)); + return; + } + + activeAxiomPlayers.add(player.getUniqueId()); + + // Enable + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeBoolean(true); + buf.writeByte(0); // todo: world properties + buf.writeInt(0x100000); // 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()); + + // 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); + for (int i=0; i<9*9; i++) { + // Ignore selected hotbar + if (i / 9 == activeHotbarIndex) { + buf.writeItem(net.minecraft.world.item.ItemStack.EMPTY); + } else { + ItemStack stack = hotbarItems.get(new NamespacedKey("axiom", "slot_"+i), ItemStackDataType.INSTANCE); + buf.writeItem(CraftItemStack.asNMSCopy(stack)); + } + } + player.sendPluginMessage(this.plugin, "axiom:initialize_hotbars", buf.accessByteBufWithCorrectSize()); + } + + // Initialize Views + UUID activeView = container.get(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE); + if (activeView != null) { + buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeUUID(activeView); + + PersistentDataContainer[] views = container.get(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY); + buf.writeVarInt(views.length); + for (PersistentDataContainer view : views) { + View.load(view).write(buf); + } + + player.sendPluginMessage(this.plugin, "axiom:set_editor_views", buf.accessByteBufWithCorrectSize()); + } + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java index 44ae910..a5aa974 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java @@ -111,25 +111,23 @@ public class SetBlockPacketListener implements PluginMessageListener { if (blockEntity != null) { chunk.addAndRegisterBlockEntity(blockEntity); } + } else if (blockEntity.getType().isValid(blockState)) { + // Block entity is here and the type is correct + // Just update the state and ticker and move on + blockEntity.setBlockState(blockState); + + try { + this.updateBlockEntityTicker.invoke(chunk, blockEntity); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } } else { - if (blockEntity.getType().isValid(blockState)) { - // Block entity is here and the type is correct - // Just update the state and ticker and move on - blockEntity.setBlockState(blockState); + // Block entity type isn't correct, we need to recreate it + chunk.removeBlockEntity(blockPos); - 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); - } + blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); + if (blockEntity != null) { + chunk.addAndRegisterBlockEntity(blockEntity); } } } else if (old.hasBlockEntity()) { diff --git a/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java new file mode 100644 index 0000000..1fd9bda --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/SetEditorViewsPacketListener.java @@ -0,0 +1,41 @@ +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 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 { + + @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)); + UUID uuid = friendlyByteBuf.readUUID(); + List views = friendlyByteBuf.readList(View::read); + + PersistentDataContainer container = player.getPersistentDataContainer(); + container.set(AxiomConstants.ACTIVE_VIEW, UUIDDataType.INSTANCE, uuid); + + PersistentDataContainer[] containerArray = new PersistentDataContainer[views.size()]; + for (int i = 0; i < views.size(); i++) { + PersistentDataContainer viewContainer = container.getAdapterContext().newPersistentDataContainer(); + views.get(i).save(viewContainer); + containerArray[i] = viewContainer; + } + container.set(AxiomConstants.VIEWS, PersistentDataType.TAG_CONTAINER_ARRAY, containerArray); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java new file mode 100644 index 0000000..e355a61 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java @@ -0,0 +1,24 @@ +package com.moulberry.axiom.packet; + +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.GameType; +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 { + + @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(); + ((CraftPlayer)player).getHandle().getAbilities().setFlyingSpeed(flySpeed); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java new file mode 100644 index 0000000..638203b --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java @@ -0,0 +1,24 @@ +package com.moulberry.axiom.packet; + +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.GameType; +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 { + + @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()); + ((CraftPlayer)player).getHandle().setGameMode(gameType); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java new file mode 100644 index 0000000..a91cd63 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/SetHotbarSlotPacketListener.java @@ -0,0 +1,36 @@ +package com.moulberry.axiom.packet; + +import com.moulberry.axiom.AxiomConstants; +import com.moulberry.axiom.persistence.ItemStackDataType; +import io.netty.buffer.Unpooled; +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 { + + @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)); + int index = friendlyByteBuf.readByte(); + if (index < 0 || index >= 9*9) return; + net.minecraft.world.item.ItemStack nmsStack = friendlyByteBuf.readItem(); + + PersistentDataContainer container = player.getPersistentDataContainer(); + PersistentDataContainer hotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); + if (hotbarItems == null) hotbarItems = container.getAdapterContext().newPersistentDataContainer(); + hotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, CraftItemStack.asCraftMirror(nmsStack)); + container.set(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, hotbarItems); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java new file mode 100644 index 0000000..38badf8 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/SwitchActiveHotbarPacketListener.java @@ -0,0 +1,59 @@ +package com.moulberry.axiom.packet; + +import com.moulberry.axiom.AxiomConstants; +import com.moulberry.axiom.persistence.ItemStackDataType; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +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 { + + @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)); + int oldHotbarIndex = friendlyByteBuf.readByte(); + int activeHotbarIndex = friendlyByteBuf.readByte(); + + ItemStack[] hotbarItems = new ItemStack[9]; + for (int i=0; i<9; i++) { + hotbarItems[i] = CraftItemStack.asCraftMirror(friendlyByteBuf.readItem()); + } + + PersistentDataContainer container = player.getPersistentDataContainer(); + PersistentDataContainer containerHotbarItems = container.get(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER); + if (containerHotbarItems == null) containerHotbarItems = container.getAdapterContext().newPersistentDataContainer(); + + for (int i=0; i<9; i++) { + if (oldHotbarIndex != activeHotbarIndex) { + int index = oldHotbarIndex*9 + i; + ItemStack stack = player.getInventory().getItem(i); + if (stack == null) { + stack = new ItemStack(Material.AIR); + } else { + stack = stack.clone(); + } + containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, stack); + } + int index = activeHotbarIndex*9 + i; + containerHotbarItems.set(new NamespacedKey("axiom", "slot_"+index), ItemStackDataType.INSTANCE, hotbarItems[i].clone()); + if (player.getGameMode() == GameMode.CREATIVE) player.getInventory().setItem(i, hotbarItems[i]); + } + + container.set(AxiomConstants.HOTBAR_DATA, PersistentDataType.TAG_CONTAINER, containerHotbarItems); + container.set(AxiomConstants.ACTIVE_HOTBAR_INDEX, PersistentDataType.BYTE, (byte) activeHotbarIndex); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java new file mode 100644 index 0000000..81b07a9 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java @@ -0,0 +1,39 @@ +package com.moulberry.axiom.packet; + +import io.netty.buffer.Unpooled; +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.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 { + + @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)); + ResourceKey resourceKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); + double x = friendlyByteBuf.readDouble(); + double y = friendlyByteBuf.readDouble(); + double z = friendlyByteBuf.readDouble(); + float yRot = friendlyByteBuf.readFloat(); + float xRot = friendlyByteBuf.readFloat(); + + NamespacedKey namespacedKey = new NamespacedKey(resourceKey.location().getNamespace(), resourceKey.location().getPath()); + World world = Bukkit.getWorld(namespacedKey); + if (world != null) { + player.teleport(new Location(world, x, y, z, yRot, xRot)); + } + } + +} From cd759984108059c634f4303350088dc199eee9ed Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 11:01:12 +0800 Subject: [PATCH 2/9] Implement ProtocolV5 (Block Entity Support) --- .../com/moulberry/axiom/AxiomConstants.java | 2 +- .../java/com/moulberry/axiom/AxiomPaper.java | 33 ++++--- .../moulberry/axiom/buffer/BlockBuffer.java | 49 ++++++++++ .../axiom/buffer/CompressedBlockEntity.java | 66 +++++++++++++ .../RequestBlockEntityPacketListener.java | 90 ++++++++++++++++++ .../packet/SetBlockBufferPacketListener.java | 53 +++++++---- .../zstd_dictionaries/block_entities_v1.dict | Bin 0 -> 32768 bytes 7 files changed, 260 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java create mode 100644 src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java create mode 100644 src/main/resources/zstd_dictionaries/block_entities_v1.dict diff --git a/src/main/java/com/moulberry/axiom/AxiomConstants.java b/src/main/java/com/moulberry/axiom/AxiomConstants.java index a80c6cd..f553d14 100644 --- a/src/main/java/com/moulberry/axiom/AxiomConstants.java +++ b/src/main/java/com/moulberry/axiom/AxiomConstants.java @@ -12,7 +12,7 @@ public class AxiomConstants { } } - public static final int API_VERSION = 4; + public static final int API_VERSION = 5; public static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index"); public static final NamespacedKey HOTBAR_DATA = new NamespacedKey("axiom", "hotbar_data"); diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index 8ad8238..d79faa7 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -18,11 +18,11 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.messaging.Messenger; import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.HashSet; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class AxiomPaper extends JavaPlugin implements Listener { @@ -30,20 +30,23 @@ public class AxiomPaper extends JavaPlugin implements Listener { public void onEnable() { Bukkit.getPluginManager().registerEvents(this, this); - Bukkit.getMessenger().registerOutgoingPluginChannel(this, "axiom:enable"); - Bukkit.getMessenger().registerOutgoingPluginChannel(this, "axiom:initialize_hotbars"); - Bukkit.getMessenger().registerOutgoingPluginChannel(this, "axiom:set_editor_views"); + Messenger msg = Bukkit.getMessenger(); - HashSet activeAxiomPlayers = new HashSet<>(); + msg.registerOutgoingPluginChannel(this, "axiom:enable"); + msg.registerOutgoingPluginChannel(this, "axiom:initialize_hotbars"); + msg.registerOutgoingPluginChannel(this, "axiom:set_editor_views"); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this, activeAxiomPlayers)); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_gamemode", new SetGamemodePacketListener()); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_fly_speed", new SetFlySpeedPacketListener()); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_block", new SetBlockPacketListener(this)); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", new SetHotbarSlotPacketListener()); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", new SwitchActiveHotbarPacketListener()); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:teleport", new TeleportPacketListener()); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "axiom:set_editor_views", new SetEditorViewsPacketListener()); + 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); diff --git a/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java b/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java index 77992d3..6cc39bb 100644 --- a/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java +++ b/src/main/java/com/moulberry/axiom/buffer/BlockBuffer.java @@ -6,12 +6,14 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectSet; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.PalettedContainer; +import org.jetbrains.annotations.Nullable; public class BlockBuffer { @@ -21,6 +23,7 @@ public class BlockBuffer { private PalettedContainer last = null; private long lastId = AxiomConstants.MIN_POSITION_LONG; + private final Long2ObjectMap> blockEntities = new Long2ObjectOpenHashMap<>(); public BlockBuffer() { this.values = new Long2ObjectOpenHashMap<>(); @@ -34,6 +37,17 @@ public class BlockBuffer { for (Long2ObjectMap.Entry> entry : this.entrySet()) { friendlyByteBuf.writeLong(entry.getLongKey()); entry.getValue().write(friendlyByteBuf); + + Short2ObjectMap blockEntities = this.blockEntities.get(entry.getLongKey()); + if (blockEntities != null) { + friendlyByteBuf.writeVarInt(blockEntities.size()); + for (Short2ObjectMap.Entry entry2 : blockEntities.short2ObjectEntrySet()) { + friendlyByteBuf.writeShort(entry2.getShortKey()); + entry2.getValue().write(friendlyByteBuf); + } + } else { + friendlyByteBuf.writeVarInt(0); + } } friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG); @@ -48,6 +62,17 @@ public class BlockBuffer { PalettedContainer palettedContainer = buffer.getOrCreateSection(index); palettedContainer.read(friendlyByteBuf); + + int blockEntitySize = Math.min(4096, friendlyByteBuf.readVarInt()); + if (blockEntitySize > 0) { + Short2ObjectMap map = new Short2ObjectOpenHashMap<>(blockEntitySize); + for (int i = 0; i < blockEntitySize; i++) { + short offset = friendlyByteBuf.readShort(); + CompressedBlockEntity blockEntity = CompressedBlockEntity.read(friendlyByteBuf); + map.put(offset, blockEntity); + } + buffer.blockEntities.put(index, map); + } } return buffer; @@ -59,6 +84,30 @@ public class BlockBuffer { this.values.clear(); } + public void putBlockEntity(int x, int y, int z, CompressedBlockEntity blockEntity) { + long cpos = BlockPos.asLong(x >> 4, y >> 4, z >> 4); + Short2ObjectMap chunkMap = this.blockEntities.computeIfAbsent(cpos, k -> new Short2ObjectOpenHashMap<>()); + + int key = (x & 0xF) | ((y & 0xF) << 4) | ((z & 0xF) << 8); + chunkMap.put((short)key, blockEntity); + } + + @Nullable + public CompressedBlockEntity getBlockEntity(int x, int y, int z) { + long cpos = BlockPos.asLong(x >> 4, y >> 4, z >> 4); + Short2ObjectMap chunkMap = this.blockEntities.get(cpos); + + if (chunkMap == null) return null; + + int key = (x & 0xF) | ((y & 0xF) << 4) | ((z & 0xF) << 8); + return chunkMap.get((short)key); + } + + @Nullable + public Short2ObjectMap getBlockEntityChunkMap(long cpos) { + return this.blockEntities.get(cpos); + } + public BlockState get(int x, int y, int z) { var container = this.getSectionForCoord(x, y, z); if (container == null) { diff --git a/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java new file mode 100644 index 0000000..76f9bba --- /dev/null +++ b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java @@ -0,0 +1,66 @@ +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; + +import java.io.*; +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"))) { + byte[] bytes = is.readAllBytes(); + zstdDictCompress = new ZstdDictCompress(bytes, Zstd.defaultCompressionLevel()); + zstdDictDecompress = new ZstdDictDecompress(bytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) { + try { + baos.reset(); + DataOutputStream dos = new DataOutputStream(baos); + NbtIo.write(tag, dos); + byte[] uncompressed = baos.toByteArray(); + byte[] compressed = Zstd.compress(uncompressed, zstdDictCompress); + return new CompressedBlockEntity(uncompressed.length, (byte) 0, compressed); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public CompoundTag decompress() { + if (this.compressionDict != 0) throw new UnsupportedOperationException("Unknown compression dict: " + this.compressionDict); + + try { + byte[] nbt = Zstd.decompress(this.compressed, zstdDictDecompress, this.originalSize); + return NbtIo.read(new DataInputStream(new ByteArrayInputStream(nbt))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static CompressedBlockEntity read(FriendlyByteBuf friendlyByteBuf) { + int originalSize = friendlyByteBuf.readVarInt(); + byte compressionDict = friendlyByteBuf.readByte(); + byte[] compressed = friendlyByteBuf.readByteArray(); + return new CompressedBlockEntity(originalSize, compressionDict, compressed); + } + + public void write(FriendlyByteBuf friendlyByteBuf) { + friendlyByteBuf.writeVarInt(this.originalSize); + friendlyByteBuf.writeByte(this.compressionDict); + friendlyByteBuf.writeByteArray(this.compressed); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java b/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java new file mode 100644 index 0000000..84d3cf0 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/packet/RequestBlockEntityPacketListener.java @@ -0,0 +1,90 @@ +package com.moulberry.axiom.packet; + +import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.buffer.CompressedBlockEntity; +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.longs.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +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; + } + + @Override + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player bukkitPlayer, @NotNull byte[] message) { + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); + long id = friendlyByteBuf.readLong(); + + if (!bukkitPlayer.hasPermission("axiom.*")) { + // We always send an 'empty' response in order to make the client happy + sendEmptyResponse(bukkitPlayer, id); + return; + } + + ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle(); + MinecraftServer server = player.getServer(); + if (server == null) { + sendEmptyResponse(bukkitPlayer, id); + return; + } + + ResourceKey worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); + ServerLevel level = server.getLevel(worldKey); + if (level == null) { + sendEmptyResponse(bukkitPlayer, id); + return; + } + + Long2ObjectMap map = new Long2ObjectOpenHashMap<>(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + + // Save and compress block entities + int count = friendlyByteBuf.readVarInt(); + for (int i = 0; i < count; i++) { + long pos = friendlyByteBuf.readLong(); + BlockEntity blockEntity = level.getBlockEntity(mutableBlockPos.set(pos)); + if (blockEntity != null) { + CompoundTag tag = blockEntity.saveWithoutMetadata(); + map.put(pos, CompressedBlockEntity.compress(tag, baos)); + } + } + + // Send response packet + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); + buf.writeLong(id); + buf.writeVarInt(map.size()); + for (Long2ObjectMap.Entry entry : map.long2ObjectEntrySet()) { + buf.writeLong(entry.getLongKey()); + entry.getValue().write(buf); + } + bukkitPlayer.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); + } + + private void sendEmptyResponse(Player player, long id) { + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(16)); + buf.writeLong(id); + buf.writeByte(0); // no block entities + player.sendPluginMessage(this.plugin, "axiom:block_entities", buf.accessByteBufWithCorrectSize()); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index 03c1aa2..97a515f 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -3,8 +3,10 @@ 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 io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.Registry; @@ -125,6 +127,8 @@ public class SetBlockBufferPacketListener { } } + Short2ObjectMap blockEntityChunkMap = buffer.getBlockEntityChunkMap(entry.getLongKey()); + sectionStates.acquire(); try { for (int x = 0; x < 16; x++) { @@ -159,26 +163,41 @@ public class SetBlockBufferPacketListener { } } - boolean oldHasBlockEntity = old.hasBlockEntity(); - if (old.is(block)) { - if (blockState.hasBlockEntity()) { - BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); - if (blockEntity == null) { - blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState); - if (blockEntity != null) { - chunk.addAndRegisterBlockEntity(blockEntity); - } - } else { - blockEntity.setBlockState(blockState); + if (blockState.hasBlockEntity()) { + BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); - try { - this.updateBlockEntityTicker.invoke(chunk, blockEntity); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } + 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); } } - } else if (oldHasBlockEntity) { + 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); } diff --git a/src/main/resources/zstd_dictionaries/block_entities_v1.dict b/src/main/resources/zstd_dictionaries/block_entities_v1.dict new file mode 100644 index 0000000000000000000000000000000000000000..f6d838a708a80cd0f557a88c4c8bd66308e503f1 GIT binary patch literal 32768 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 EKe6YLy8r+H literal 0 HcmV?d00001 From 37a64653325db2956761b0ba0392f36395ab2091 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 11:33:13 +0800 Subject: [PATCH 3/9] Add AxiomHandshakeEvent and AxiomModifyWorldEvent --- .../axiom/event/AxiomHandshakeEvent.java | 52 ++++++++++++++++++ .../axiom/event/AxiomModifyWorldEvent.java | 54 +++++++++++++++++++ .../axiom/packet/AxiomBigPayloadHandler.java | 1 + .../axiom/packet/HelloPacketListener.java | 11 +++- .../packet/SetBlockBufferPacketListener.java | 15 ++++++ .../axiom/packet/SetBlockPacketListener.java | 9 ++++ 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/moulberry/axiom/event/AxiomHandshakeEvent.java create mode 100644 src/main/java/com/moulberry/axiom/event/AxiomModifyWorldEvent.java diff --git a/src/main/java/com/moulberry/axiom/event/AxiomHandshakeEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomHandshakeEvent.java new file mode 100644 index 0000000..735c762 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/event/AxiomHandshakeEvent.java @@ -0,0 +1,52 @@ +package com.moulberry.axiom.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class AxiomHandshakeEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private boolean cancelled = false; + private int maxBufferSize = 0x100000; + + public AxiomHandshakeEvent(Player player) { + this.player = player; + } + + public Player getPlayer() { + return this.player; + } + + public int getMaxBufferSize() { + return this.maxBufferSize; + } + + public void setMaxBufferSize(int maxBufferSize) { + this.maxBufferSize = maxBufferSize; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/com/moulberry/axiom/event/AxiomModifyWorldEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomModifyWorldEvent.java new file mode 100644 index 0000000..405948f --- /dev/null +++ b/src/main/java/com/moulberry/axiom/event/AxiomModifyWorldEvent.java @@ -0,0 +1,54 @@ +package com.moulberry.axiom.event; + +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class AxiomModifyWorldEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private final World world; + private boolean cancelled; + + public AxiomModifyWorldEvent(Player player, World world) { + this.player = player; + this.world = world; + + // By default, changes are only allowed if the player is in the same world + // This behaviour can be changed by doing setCancelled(false) + this.cancelled = player.getWorld() != world; + } + + public World getWorld() { + return world; + } + + public Player getPlayer() { + return this.player; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java index 0d85bbc..ec19e9e 100644 --- a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java +++ b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java @@ -39,6 +39,7 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder { ServerPlayer player = connection.getPlayer(); if (player != null && player.getBukkitEntity().hasPermission("axiom.*")) { listener.onReceive(player, buf); + in.readerIndex(in.writerIndex()); return; } } diff --git a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java index 63041a1..e09e520 100644 --- a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java @@ -3,11 +3,13 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomPaper; 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.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.inventory.CraftItemStack; import org.bukkit.entity.Player; @@ -46,13 +48,20 @@ public class HelloPacketListener implements PluginMessageListener { return; } + // Call handshake event + AxiomHandshakeEvent handshakeEvent = new AxiomHandshakeEvent(player); + Bukkit.getPluginManager().callEvent(handshakeEvent); + if (handshakeEvent.isCancelled()) { + return; + } + activeAxiomPlayers.add(player.getUniqueId()); // Enable FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); buf.writeBoolean(true); buf.writeByte(0); // todo: world properties - buf.writeInt(0x100000); // Max Buffer Size + buf.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size buf.writeBoolean(false); // No source info buf.writeBoolean(false); // No source settings buf.writeVarInt(5); // Maximum Reach diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index 97a515f..bac9c25 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -4,6 +4,7 @@ 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 io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; @@ -31,6 +32,10 @@ import net.minecraft.world.level.chunk.LevelChunkSection; 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.World; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; @@ -66,6 +71,16 @@ public class SetBlockBufferPacketListener { if (server == null) return; ResourceKey worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); + NamespacedKey namespacedKey = new NamespacedKey(worldKey.location().getNamespace(), worldKey.location().getPath()); + World world = Bukkit.getWorld(namespacedKey); + if (world != null) { + AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player.getBukkitEntity(), world); + Bukkit.getPluginManager().callEvent(modifyWorldEvent); + if (modifyWorldEvent.isCancelled()) return; + } else { + return; + } + friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers boolean continuation = friendlyByteBuf.readBoolean(); diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java index a5aa974..f1a5c7c 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java @@ -1,6 +1,7 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.event.AxiomModifyWorldEvent; import io.netty.buffer.Unpooled; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -15,6 +16,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.levelgen.Heightmap; 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; @@ -52,6 +54,12 @@ public class SetBlockPacketListener implements PluginMessageListener { return; } + // 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)); BlockPos blockPos = friendlyByteBuf.readBlockPos(); BlockState blockState = friendlyByteBuf.readById(Block.BLOCK_STATE_REGISTRY); @@ -60,6 +68,7 @@ public class SetBlockPacketListener implements PluginMessageListener { ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle(); + // Update blocks if (updateNeighbors) { player.level().setBlock(blockPos, blockState, 3); } else { From 88932668b448bcd6d8794125f5d55b944ac7596e Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 11:34:52 +0800 Subject: [PATCH 4/9] Fix compile errors with relocated constant --- .../com/moulberry/axiom/buffer/Position2ByteMap.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java b/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java index d4c7d6f..438872e 100644 --- a/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java +++ b/src/main/java/com/moulberry/axiom/buffer/Position2ByteMap.java @@ -1,5 +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; @@ -20,7 +21,7 @@ public class Position2ByteMap { private final LongFunction defaultFunction; private final Long2ObjectMap map = new Long2ObjectOpenHashMap<>(); - private long lastChunkPos = AxiomPaper.MIN_POSITION_LONG; + private long lastChunkPos = AxiomConstants.MIN_POSITION_LONG; private byte[] lastChunk = null; public Position2ByteMap() { @@ -47,7 +48,7 @@ public class Position2ByteMap { friendlyByteBuf.writeLong(entry.getLongKey()); friendlyByteBuf.writeBytes(entry.getValue()); } - friendlyByteBuf.writeLong(AxiomPaper.MIN_POSITION_LONG); + friendlyByteBuf.writeLong(AxiomConstants.MIN_POSITION_LONG); } public static Position2ByteMap load(FriendlyByteBuf friendlyByteBuf) { @@ -55,7 +56,7 @@ public class Position2ByteMap { while (true) { long pos = friendlyByteBuf.readLong(); - if (pos == AxiomPaper.MIN_POSITION_LONG) break; + if (pos == AxiomConstants.MIN_POSITION_LONG) break; byte[] bytes = new byte[16*16*16]; friendlyByteBuf.readBytes(bytes); @@ -67,7 +68,7 @@ public class Position2ByteMap { public void clear() { this.map.clear(); - this.lastChunkPos = AxiomPaper.MIN_POSITION_LONG; + this.lastChunkPos = AxiomConstants.MIN_POSITION_LONG; this.lastChunk = null; } From 40493b997e2e79e104eccc0ed7c97517f3a53160 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 11:51:45 +0800 Subject: [PATCH 5/9] Fix async event call --- build.gradle.kts | 2 +- .../axiom/packet/AxiomBigPayloadHandler.java | 4 ++-- .../packet/SetBlockBufferPacketListener.java | 20 ++++++++----------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c0ee842..0121061 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "com.moulberry.axiom" -version = "1.1.0" +version = "1.2.1" description = "Serverside component for Axiom on Paper" java { diff --git a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java index ec19e9e..10e83e2 100644 --- a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java +++ b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java @@ -26,10 +26,10 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + int readerIndex = in.readerIndex(); try { int i = in.readableBytes(); if (i != 0) { - int readerIndex = in.readerIndex(); FriendlyByteBuf buf = new FriendlyByteBuf(in); int packetId = buf.readVarInt(); @@ -44,9 +44,9 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder { } } } - in.readerIndex(readerIndex); } } catch (Exception e) { + in.readerIndex(readerIndex); e.printStackTrace(); } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index bac9c25..75efc5b 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -71,16 +71,6 @@ public class SetBlockBufferPacketListener { if (server == null) return; ResourceKey worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); - NamespacedKey namespacedKey = new NamespacedKey(worldKey.location().getNamespace(), worldKey.location().getPath()); - World world = Bukkit.getWorld(namespacedKey); - if (world != null) { - AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player.getBukkitEntity(), world); - Bukkit.getPluginManager().callEvent(modifyWorldEvent); - if (modifyWorldEvent.isCancelled()) return; - } else { - return; - } - friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers boolean continuation = friendlyByteBuf.readBoolean(); @@ -91,7 +81,7 @@ public class SetBlockBufferPacketListener { byte type = friendlyByteBuf.readByte(); if (type == 0) { BlockBuffer buffer = BlockBuffer.load(friendlyByteBuf); - applyBlockBuffer(server, buffer, worldKey); + applyBlockBuffer(player, server, buffer, worldKey); } else if (type == 1) { BiomeBuffer buffer = BiomeBuffer.load(friendlyByteBuf); applyBiomeBuffer(server, buffer, worldKey); @@ -100,11 +90,17 @@ public class SetBlockBufferPacketListener { } } - private void applyBlockBuffer(MinecraftServer server, BlockBuffer buffer, ResourceKey worldKey) { + private void applyBlockBuffer(ServerPlayer player, MinecraftServer server, BlockBuffer buffer, ResourceKey worldKey) { server.execute(() -> { 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; + + // Allowed, apply buffer BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); var lightEngine = world.getChunkSource().getLightEngine(); From 6acfa7cf2bc1cf8274536e23a9b54c1480c72ef5 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 12:01:07 +0800 Subject: [PATCH 6/9] Fix payload handling --- .../moulberry/axiom/packet/AxiomBigPayloadHandler.java | 10 +++++----- .../axiom/packet/SetBlockBufferPacketListener.java | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java index 10e83e2..eb64174 100644 --- a/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java +++ b/src/main/java/com/moulberry/axiom/packet/AxiomBigPayloadHandler.java @@ -26,8 +26,8 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - int readerIndex = in.readerIndex(); try { + int readerIndex = in.readerIndex(); int i = in.readableBytes(); if (i != 0) { FriendlyByteBuf buf = new FriendlyByteBuf(in); @@ -38,15 +38,15 @@ public class AxiomBigPayloadHandler extends ByteToMessageDecoder { if (identifier.equals(SET_BUFFER)) { ServerPlayer player = connection.getPlayer(); if (player != null && player.getBukkitEntity().hasPermission("axiom.*")) { - listener.onReceive(player, buf); - in.readerIndex(in.writerIndex()); - return; + if (listener.onReceive(player, buf)) { + return; + } } } } } - } catch (Exception e) { in.readerIndex(readerIndex); + } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index 75efc5b..5a0d2be 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -66,9 +66,9 @@ public class SetBlockBufferPacketListener { } } - public void onReceive(ServerPlayer player, FriendlyByteBuf friendlyByteBuf) { + public boolean onReceive(ServerPlayer player, FriendlyByteBuf friendlyByteBuf) { MinecraftServer server = player.getServer(); - if (server == null) return; + if (server == null) return false; ResourceKey worldKey = friendlyByteBuf.readResourceKey(Registries.DIMENSION); friendlyByteBuf.readUUID(); // Discard, we don't need to associate buffers @@ -88,6 +88,8 @@ public class SetBlockBufferPacketListener { } else { throw new RuntimeException("Unknown buffer type: " + type); } + + return true; } private void applyBlockBuffer(ServerPlayer player, MinecraftServer server, BlockBuffer buffer, ResourceKey worldKey) { From a720c1da4b34574a6a630a3bf8c00f9dca5fb848 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 12:05:15 +0800 Subject: [PATCH 7/9] Fix Block Entity protocol --- src/main/java/com/moulberry/axiom/AxiomPaper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index d79faa7..0b096eb 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -1,5 +1,6 @@ package com.moulberry.axiom; +import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.packet.*; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -29,12 +30,14 @@ public class AxiomPaper extends JavaPlugin implements Listener { @Override public void onEnable() { Bukkit.getPluginManager().registerEvents(this, this); + CompressedBlockEntity.initialize(this); 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<>()); From 02c65bc807e5606264b61602cc3c631c7ead3ff3 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 12:22:38 +0800 Subject: [PATCH 8/9] Add events for fly speed, gamemode and teleport --- .../axiom/event/AxiomFlySpeedChangeEvent.java | 50 ++++++++++++++++++ .../axiom/event/AxiomGameModeChangeEvent.java | 51 +++++++++++++++++++ .../axiom/event/AxiomTeleportEvent.java | 51 +++++++++++++++++++ .../packet/SetFlySpeedPacketListener.java | 12 +++++ .../packet/SetGamemodePacketListener.java | 10 ++++ .../axiom/packet/TeleportPacketListener.java | 19 ++++--- 6 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java create mode 100644 src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java create mode 100644 src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java diff --git a/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java new file mode 100644 index 0000000..a914830 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/event/AxiomFlySpeedChangeEvent.java @@ -0,0 +1,50 @@ +package com.moulberry.axiom.event; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class AxiomFlySpeedChangeEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private final float flySpeed; + private boolean cancelled = false; + + public AxiomFlySpeedChangeEvent(Player player, float flySpeed) { + this.player = player; + this.flySpeed = flySpeed; + } + + public float getFlySpeed() { + return flySpeed; + } + + public Player getPlayer() { + return this.player; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java new file mode 100644 index 0000000..86da786 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/event/AxiomGameModeChangeEvent.java @@ -0,0 +1,51 @@ +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; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class AxiomGameModeChangeEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private final GameMode gameMode; + private boolean cancelled = false; + + public AxiomGameModeChangeEvent(Player player, GameMode gameMode) { + this.player = player; + this.gameMode = gameMode; + } + + public GameMode getGameMode() { + return gameMode; + } + + public Player getPlayer() { + return this.player; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java b/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java new file mode 100644 index 0000000..2b7f800 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/event/AxiomTeleportEvent.java @@ -0,0 +1,51 @@ +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; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class AxiomTeleportEvent extends Event implements Cancellable { + + private static final HandlerList HANDLERS = new HandlerList(); + + private final Player player; + private final Location location; + private boolean cancelled = false; + + public AxiomTeleportEvent(Player player, Location location) { + this.player = player; + this.location = location; + } + + public Location getLocation() { + return this.location.clone(); + } + + public Player getPlayer() { + return this.player; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java index e355a61..83b5d44 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetFlySpeedPacketListener.java @@ -1,8 +1,12 @@ 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 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; @@ -16,8 +20,16 @@ public class SetFlySpeedPacketListener implements PluginMessageListener { return; } + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); float flySpeed = friendlyByteBuf.readFloat(); + + // Call event + AxiomFlySpeedChangeEvent flySpeedChangeEvent = new AxiomFlySpeedChangeEvent(player, flySpeed); + Bukkit.getPluginManager().callEvent(flySpeedChangeEvent); + if (flySpeedChangeEvent.isCancelled()) return; + + // Change flying speed ((CraftPlayer)player).getHandle().getAbilities().setFlyingSpeed(flySpeed); } diff --git a/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java index 638203b..c9c18e3 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetGamemodePacketListener.java @@ -1,8 +1,11 @@ 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 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; @@ -18,6 +21,13 @@ public class SetGamemodePacketListener implements PluginMessageListener { FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message)); GameType gameType = GameType.byId(friendlyByteBuf.readByte()); + + // Call event + AxiomGameModeChangeEvent gameModeChangeEvent = new AxiomGameModeChangeEvent(player, GameMode.getByValue(gameType.getId())); + Bukkit.getPluginManager().callEvent(gameModeChangeEvent); + if (gameModeChangeEvent.isCancelled()) return; + + // Change gamemode ((CraftPlayer)player).getHandle().setGameMode(gameType); } diff --git a/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java index 81b07a9..871cc2a 100644 --- a/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/TeleportPacketListener.java @@ -1,14 +1,13 @@ package com.moulberry.axiom.packet; +import com.moulberry.axiom.event.AxiomGameModeChangeEvent; +import com.moulberry.axiom.event.AxiomTeleportEvent; import io.netty.buffer.Unpooled; 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.Bukkit; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; @@ -31,9 +30,15 @@ public class TeleportPacketListener implements PluginMessageListener { NamespacedKey namespacedKey = new NamespacedKey(resourceKey.location().getNamespace(), resourceKey.location().getPath()); World world = Bukkit.getWorld(namespacedKey); - if (world != null) { - player.teleport(new Location(world, x, y, z, yRot, xRot)); - } + if (world == null) return; + + // Call event + AxiomTeleportEvent teleportEvent = new AxiomTeleportEvent(player, new Location(world, x, y, z, yRot, xRot)); + Bukkit.getPluginManager().callEvent(teleportEvent); + if (teleportEvent.isCancelled()) return; + + // Do teleport + player.teleport(new Location(world, x, y, z, yRot, xRot)); } } From d4eb0a37211489ce0644b1aac5d424edc28d8d47 Mon Sep 17 00:00:00 2001 From: Moulberry Date: Wed, 6 Sep 2023 13:31:10 +0800 Subject: [PATCH 9/9] Preliminary WorldGuard support --- build.gradle.kts | 11 ++++ .../axiom/integration/RegionProtection.java | 27 +++++++++ .../RegionProtectionWorldGuard.java | 57 +++++++++++++++++++ .../packet/SetBlockBufferPacketListener.java | 22 ++++++- 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/moulberry/axiom/integration/RegionProtection.java create mode 100644 src/main/java/com/moulberry/axiom/integration/RegionProtectionWorldGuard.java diff --git a/build.gradle.kts b/build.gradle.kts index 0121061..a98ff0c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,8 +17,11 @@ java { } repositories { + mavenCentral() maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") maven("https://jitpack.io") + maven("https://maven.enginehub.org/repo/") + maven("https://repo.papermc.io/repository/maven-public/") } dependencies { @@ -27,6 +30,14 @@ dependencies { // Zstd Compression Library implementation("com.github.luben:zstd-jni:1.5.5-4") + + // WorldGuard support + compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT") + + // PlotSquared support + implementation(platform("com.intellectualsites.bom:bom-newest:1.37")) + compileOnly("com.intellectualsites.plotsquared:plotsquared-core") + compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } } tasks { diff --git a/src/main/java/com/moulberry/axiom/integration/RegionProtection.java b/src/main/java/com/moulberry/axiom/integration/RegionProtection.java new file mode 100644 index 0000000..9b95e46 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/RegionProtection.java @@ -0,0 +1,27 @@ +package com.moulberry.axiom.integration; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; + +public class RegionProtection { + + private final RegionProtectionWorldGuard worldGuard; + + public RegionProtection(Player player, World world) { + if (Bukkit.getPluginManager().isPluginEnabled("WorldGuard")) { + this.worldGuard = RegionProtectionWorldGuard.tryCreate(player, world); + } else { + this.worldGuard = null; + } + } + + public boolean canBuildInSection(int cx, int cy, int cz) { + if (this.worldGuard != null && !this.worldGuard.canBuildInSection(cx, cy, cz)) return false; + // todo: PlotSquared + return true; + } + + + +} diff --git a/src/main/java/com/moulberry/axiom/integration/RegionProtectionWorldGuard.java b/src/main/java/com/moulberry/axiom/integration/RegionProtectionWorldGuard.java new file mode 100644 index 0000000..784fb3e --- /dev/null +++ b/src/main/java/com/moulberry/axiom/integration/RegionProtectionWorldGuard.java @@ -0,0 +1,57 @@ +package com.moulberry.axiom.integration; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.internal.platform.WorldGuardPlatform; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.Flags; +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 org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +public class RegionProtectionWorldGuard { + + private final LocalPlayer player; + private final RegionManager regionManager; + + public RegionProtectionWorldGuard(LocalPlayer player, RegionManager regionManager) { + this.player = player; + this.regionManager = regionManager; + } + + @Nullable + public static RegionProtectionWorldGuard tryCreate(Player player, World world) { + WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform(); + + RegionContainer regionContainer = platform.getRegionContainer(); + com.sk89q.worldedit.world.World worldEditWorld = BukkitAdapter.adapt(world); + LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player); + + // Don't do any protection if player has bypass + if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldEditWorld)) { + return null; + } + + RegionManager regionManager = regionContainer.get(worldEditWorld); + if (regionManager == null) return null; + + return new RegionProtectionWorldGuard(worldGuardPlayer, regionManager); + } + + public boolean canBuildInSection(int cx, int cy, int cz) { + BlockVector3 min = BlockVector3.at(cx*16, cy*16, cz*16); + BlockVector3 max = BlockVector3.at(cx*16+15, cy*16+15, cz*16+15); + ProtectedRegion test = new ProtectedCuboidRegion("dummy", min, max); + ApplicableRegionSet regions = this.regionManager.getApplicableRegions(test, RegionQuery.QueryOption.COMPUTE_PARENTS); + return regions.testState(this.player, Flags.BUILD); + } + +} diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index 5a0d2be..44b7479 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -5,6 +5,21 @@ 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 it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; @@ -34,7 +49,6 @@ 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.World; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; @@ -102,6 +116,8 @@ public class SetBlockBufferPacketListener { Bukkit.getPluginManager().callEvent(modifyWorldEvent); if (modifyWorldEvent.isCancelled()) return; + RegionProtection regionProtection = new RegionProtection(player.getBukkitEntity(), world.getWorld()); + // Allowed, apply buffer BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); @@ -119,6 +135,10 @@ public class SetBlockBufferPacketListener { continue; } + if (!regionProtection.canBuildInSection(cx, cy, cz)) { + continue; + } + LevelChunk chunk = world.getChunk(cx, cz); chunk.setUnsaved(true);