From a0367320c3e3d2a62be1a240237f1d6ec21de65e Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 12 Sep 2023 17:32:07 +0200 Subject: [PATCH] Full Multiversion (untested) --- .../java/com/moulberry/axiom/AxiomPaper.java | 26 ++++- .../java/com/moulberry/axiom/Reflection.java | 12 ++- .../axiom/buffer/CompressedBlockEntity.java | 2 +- .../integration/NoVersionTranslator.java | 2 +- .../axiom/packet/ChunkSectionModifier.java | 102 ++++++++++-------- .../axiom/packet/HelloPacketListener.java | 4 +- .../packet/SetBlockBufferPacketListener.java | 23 ++-- .../axiom/packet/SetBlockPacketListener.java | 38 +++++-- .../axiom/persistence/ItemStackDataType.java | 2 +- 9 files changed, 133 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index a3be60c..87e3959 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -6,8 +6,12 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunk; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Player; @@ -17,7 +21,6 @@ import org.bukkit.plugin.java.JavaPlugin; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; public class AxiomPaper extends JavaPlugin implements Listener { @@ -43,11 +46,26 @@ public class AxiomPaper extends JavaPlugin implements Listener { return worldGetHandle.invoke(world); } + private static final Reflection.Method getChunk = Reflection.getTypedMethod(Level.class, LevelChunk.class, int.class, int.class); + public static LevelChunk getChunk(Level level, int cx, int cz) { + return getChunk.invoke(level, cx, cz); + } + private static final Reflection.Method of = Reflection.getTypedMethod(BlockPos.class, BlockPos.class, long.class); public static BlockPos convert(long packed) { return of.invoke(null, packed); } + private static final Reflection.Field serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class); + public static ServerGamePacketListenerImpl getConnection(ServerPlayer player) { + return serverPlayerConnection.get(player); + } + + private static final Reflection.Method getChunkSource = Reflection.getTypedMethod(ServerLevel.class, ServerChunkCache.class); + public static ServerChunkCache getChunkSource(ServerLevel level) { + return getChunkSource.invoke(level); + } + @Override public void onEnable() { instance = this; @@ -55,7 +73,7 @@ public class AxiomPaper extends JavaPlugin implements Listener { 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."); + getLogger().log(java.util.logging.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()) { @@ -65,13 +83,13 @@ public class AxiomPaper extends JavaPlugin implements Listener { registerInChannel("hello", new HelloPacketListener(new AxiomPlayerManager())); registerInChannel("set_gamemode", new SetGamemodePacketListener()); registerInChannel("set_fly_speed", new SetFlySpeedPacketListener()); - registerInChannel("set_block", new SetBlockPacketListener()); //TODO multiversion + 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()); //TODO multiversion + registerInChannel("handle_big_payload", new SetBlockBufferPacketListener()); } private void registerInChannel(String channel, AxiomPacketListener handler) { diff --git a/src/main/java/com/moulberry/axiom/Reflection.java b/src/main/java/com/moulberry/axiom/Reflection.java index a3929b4..135f29f 100644 --- a/src/main/java/com/moulberry/axiom/Reflection.java +++ b/src/main/java/com/moulberry/axiom/Reflection.java @@ -74,12 +74,16 @@ public class Reflection { R invoke(T target, Object... arguments); } - public static Method getMethod(Class clazz, Class... params) { - return getTypedMethod(clazz, null, null, 0, params); + public static Method getMethod(Class clazz, Class... params) { + return getTypedMethod(clazz, null, Void.TYPE, 0, params); } - public static Method getMethod(Class clazz, String methodName, Class... params) { - return getTypedMethod(clazz, methodName, null, 0, params); + public static Method getMethod(Class clazz, int index, Class... params) { + return getTypedMethod(clazz, null, Void.TYPE, index, params); + } + + public static Method getMethod(Class clazz, String methodName, Class... params) { + return getTypedMethod(clazz, methodName, Void.TYPE, 0, params); } public static Method getTypedMethod(Class clazz, Class returnType, Class... params) { diff --git a/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java index c759301..d6553fe 100644 --- a/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java +++ b/src/main/java/com/moulberry/axiom/buffer/CompressedBlockEntity.java @@ -25,7 +25,7 @@ public record CompressedBlockEntity(int originalSize, byte compressionDict, byte } } - private static final Reflection.Method write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class); + private static final Reflection.Method write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class); public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) { baos.reset(); DataOutputStream dos = new DataOutputStream(baos); diff --git a/src/main/java/com/moulberry/axiom/integration/NoVersionTranslator.java b/src/main/java/com/moulberry/axiom/integration/NoVersionTranslator.java index d78eaa7..c08473b 100644 --- a/src/main/java/com/moulberry/axiom/integration/NoVersionTranslator.java +++ b/src/main/java/com/moulberry/axiom/integration/NoVersionTranslator.java @@ -46,7 +46,7 @@ public class NoVersionTranslator implements VersionTranslator { } private static final PalettedContainer.Strategy SECTION_STATES = Reflection.getField(PalettedContainer.Strategy.class, PalettedContainer.Strategy.class).get(null); - private static final Reflection.Method read = Reflection.getMethod(PalettedContainer.class, FriendlyByteBuf.class); + private static final Reflection.Method read = Reflection.getMethod(PalettedContainer.class, FriendlyByteBuf.class); @Override public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer consumer) { //EMPTY_STATE is completely ignored diff --git a/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java b/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java index 13daf29..ef51980 100644 --- a/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java +++ b/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java @@ -7,10 +7,14 @@ import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.levelgen.Heightmap; @@ -18,13 +22,14 @@ import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.lighting.LightEventListener; import org.bukkit.World; +import java.util.Arrays; import java.util.List; +import java.util.Map; public class ChunkSectionModifier { - //TODO multiversioning - private static final List types = List.of(Heightmap.Types.WORLD_SURFACE, Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES); - //TODO end multiversioning + private static final Reflection.Method getSerializationKey = Reflection.getTypedMethod(Heightmap.Types.class, String.class); + private static final List types = Arrays.stream(Heightmap.Types.values()).filter(type -> List.of("WORLD_SURFACE", "OCEAN_FLOOR", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES").contains(getSerializationKey.invoke(type))).toList(); private final int cx; private final int cy; @@ -37,30 +42,35 @@ public class ChunkSectionModifier { private final Heightmap[] heightmaps; private final boolean hadOnlyAir; + private static final Reflection.Method getLightEngine = Reflection.getTypedMethod(ServerChunkCache.class, ThreadedLevelLightEngine.class); + private static final Reflection.Method getSection = Reflection.getTypedMethod(ChunkAccess.class, LevelChunkSection.class, int.class); + private static final Reflection.Method hasOnlyAir = Reflection.getTypedMethod(LevelChunkSection.class, boolean.class, 1); + private static final Reflection.Field chunkHeightmaps = Reflection.getField(ChunkAccess.class, Map.class); public ChunkSectionModifier(World world, int cx, int cy, int cz) { this.cx = cx; this.cy = cy; this.cz = cz; ServerLevel level = AxiomPaper.convert(world); - //TODO multiversioning - chunkSource = level.getChunkSource(); - lightEngine = chunkSource.getLightEngine(); - chunk = level.getChunk(cx, cz); + chunkSource = AxiomPaper.getChunkSource(level); + lightEngine = getLightEngine.invoke(chunkSource); + chunk = AxiomPaper.getChunk(level, cx, cz); - section = chunk.getSection(cy - world.getMinHeight() >> 4); - hadOnlyAir = section.hasOnlyAir(); + section = getSection.invoke(chunk, cy - world.getMinHeight() >> 4); + hadOnlyAir = hasOnlyAir.invoke(section); - heightmaps = types.stream().map(chunk.heightmaps::get).toArray(Heightmap[]::new); - //TODO end multiversioning + heightmaps = types.stream().map(chunkHeightmaps.get(chunk)::get).toArray(Heightmap[]::new); } + private static final Reflection.Method setBlockState = Reflection.getTypedMethod(LevelChunkSection.class, BlockState.class, int.class, int.class, int.class, BlockState.class, boolean.class); + private static final Reflection.Method update = Reflection.getTypedMethod(Heightmap.class, boolean.class, int.class, int.class, int.class, BlockState.class); + private static final Reflection.Method getBlock = Reflection.getTypedMethod(BlockState.class, Block.class); + private static final Reflection.Method removeBlockEntity = Reflection.getMethod(ChunkAccess.class, BlockPos.class); + private static final Reflection.Method blockChanged = Reflection.getMethod(ServerChunkCache.class, BlockPos.class); + private static final Reflection.Method hasDifferentLightProperties = Reflection.getTypedMethod(LightEngine.class, boolean.class, BlockGetter.class, BlockPos.class, BlockState.class, BlockState.class); + private static final Reflection.Method checkBlock = Reflection.getMethod(LightEventListener.class, BlockPos.class); public void setState(int sx, int sy, int sz, BlockState state) { - //TODO multiversioning - if (hadOnlyAir && state.isAir()) - return; - - BlockState old = section.setBlockState(sx, sy, sz, state, false); + BlockState old = setBlockState.invoke(section, sx, sy, sz, state, false); if (state == old) return; @@ -68,68 +78,70 @@ public class ChunkSectionModifier { BlockPos pos = new BlockPos(cx * 16 + sx, by, cz * 16 + sz); for (Heightmap heightmap : heightmaps) - heightmap.update(sx, by, sz, state); + update.invoke(heightmap, sx, by, sz, state); - if (state.hasBlockEntity()) { + if (getBlock.invoke(state) instanceof EntityBlock) { setBlockEntity(state, pos); - } else if (old.hasBlockEntity()) { - chunk.removeBlockEntity(pos); + } else if (getBlock.invoke(old) instanceof EntityBlock) { + removeBlockEntity.invoke(chunk, pos); } - chunkSource.blockChanged(pos); + blockChanged.invoke(chunkSource, pos); - if (LightEngine.hasDifferentLightProperties(chunk, pos, old, state)) { - lightEngine.checkBlock(pos); + if (hasDifferentLightProperties.invoke(null, chunk, pos, old, state)) { + checkBlock.invoke(lightEngine, pos); } - //TODO end multiversioning } + private static final Reflection.Method getBlockEntity = Reflection.getTypedMethod(BlockGetter.class, BlockEntity.class, BlockPos.class); + private static final Reflection.Method load = Reflection.getMethod(BlockEntity.class, CompoundTag.class); public void setBlockEntity(int sx, int sy, int sz, CompoundTag tag) { - //TODO multiversioning - BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(cx * 16 + sx, cy * 16 + sy, cz * 16 + sz), LevelChunk.EntityCreationType.CHECK); + BlockEntity blockEntity = getBlockEntity.invoke(chunk, new BlockPos(cx * 16 + sx, cy * 16 + sy, cz * 16 + sz)); if(blockEntity != null) - blockEntity.load(tag); - //TODO end multiversioning + load.invoke(blockEntity, tag); } - private static final Reflection.Method updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, BlockEntity.class); + private static final Reflection.Method getType = Reflection.getTypedMethod(BlockEntity.class, BlockEntityType.class); + private static final Reflection.Method isValid = Reflection.getTypedMethod(BlockEntityType.class, boolean.class, BlockState.class); + private static final Reflection.Method blockEntitySetBlockState = Reflection.getMethod(BlockEntity.class, BlockState.class); + private static final Reflection.Method updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, 2, BlockEntity.class); + private static final Reflection.Method newBlockEntity = Reflection.getTypedMethod(EntityBlock.class, BlockEntity.class, BlockPos.class, BlockState.class); + private static final Reflection.Method addAndRegisterBlockEntity = Reflection.getMethod(LevelChunk.class, BlockEntity.class); private void setBlockEntity(BlockState state, BlockPos pos) { - //TODO multiversioning - BlockEntity blockEntity = chunk.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + BlockEntity blockEntity = getBlockEntity.invoke(chunk, pos, LevelChunk.EntityCreationType.CHECK); if (blockEntity != null) { - if (blockEntity.getType().isValid(state)) { + if (isValid.invoke(getType.invoke(blockEntity), state)) { // Block entity is here and the type is correct - blockEntity.setBlockState(state); + blockEntitySetBlockState.invoke(blockEntity, state); updateBlockEntityTicker.invoke(chunk, blockEntity); } else { // Block entity type isn't correct, we need to recreate it - chunk.removeBlockEntity(pos); + removeBlockEntity.invoke(chunk, pos); blockEntity = null; } } if (blockEntity == null) { // There isn't a block entity here, create it! - Block block = state.getBlock(); - blockEntity = ((EntityBlock) block).newBlockEntity(pos, state); + EntityBlock block = (EntityBlock)getBlock.invoke(state); + blockEntity = newBlockEntity.invoke(block, pos, state); if (blockEntity != null) { - chunk.addAndRegisterBlockEntity(blockEntity); + addAndRegisterBlockEntity.invoke(chunk, blockEntity); } } - //TODO end multiversioning } - + private static final Reflection.Method of = Reflection.getTypedMethod(SectionPos.class, SectionPos.class, int.class, int.class, int.class); + private static final Reflection.Method updateSectionStatus = Reflection.getMethod(LightEventListener.class, SectionPos.class, boolean.class); + private static final Reflection.Method setUnsaved = Reflection.getMethod(ChunkAccess.class, boolean.class); public void finish() { - //TODO multiversioning - boolean hasOnlyAir = section.hasOnlyAir(); - if (hadOnlyAir != hasOnlyAir) { - lightEngine.updateSectionStatus(SectionPos.of(cx, cy, cz), hasOnlyAir); + boolean onlyAir = hasOnlyAir.invoke(section); + if (hadOnlyAir != onlyAir) { + updateSectionStatus.invoke(lightEngine, of.invoke(null, cx, cy, cz), onlyAir); } - chunk.setUnsaved(true); - //TODO end multiversioning + setUnsaved.invoke(chunk, true); } } diff --git a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java index 785755f..ccdab10 100644 --- a/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/HelloPacketListener.java @@ -9,7 +9,6 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import net.kyori.adventure.text.Component; import net.minecraft.network.Connection; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -23,7 +22,6 @@ import java.util.UUID; public class HelloPacketListener implements AxiomPacketListener, Listener { - private static final Reflection.Field serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class); private static final Reflection.Field packetListenerConnection = Reflection.getField(ServerGamePacketListenerImpl.class, Connection.class); private static final Reflection.Field channel = Reflection.getField(Connection.class, Channel.class); @@ -54,7 +52,7 @@ public class HelloPacketListener implements AxiomPacketListener, Listener { // Welcome to multiversioning! channel.get( packetListenerConnection.get( - serverPlayerConnection.get( + AxiomPaper.getConnection( AxiomPaper.convert(player) ) ) diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index c04688d..091391f 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -2,6 +2,7 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomConstants; import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.Reflection; import com.moulberry.axiom.buffer.BiomeBuffer; import com.moulberry.axiom.buffer.CompressedBlockEntity; import com.moulberry.axiom.buffer.MojBuf; @@ -10,17 +11,18 @@ import com.moulberry.axiom.integration.RegionProtection; import com.moulberry.axiom.integration.VersionTranslator; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket; import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; -import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; import org.bukkit.entity.Player; import java.util.*; @@ -78,6 +80,10 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener { } } + private static final Reflection.Field chunkMap = Reflection.getField(ServerChunkCache.class, ChunkMap.class); + private static final Reflection.Method getPlayers = Reflection.getTypedMethod(ChunkMap.class, List.class, ChunkPos.class, boolean.class); + private static final Reflection.Method send = Reflection.getMethod(ServerPlayerConnection.class, Packet.class); + private static final Reflection.Method forChunks = Reflection.getTypedMethod(ClientboundChunksBiomesPacket.class, ClientboundChunksBiomesPacket.class, List.class); private void applyBiomeBuffer(World world, BiomeBuffer biomeBuffer) { Set changedChunks = new HashSet<>(); biomeBuffer.forEachEntry((x, y, z, biome) -> { @@ -86,17 +92,16 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener { }); ServerLevel level = AxiomPaper.convert(world); - //TODO multiversioning - ChunkMap chunkMap = level.getChunkSource().chunkMap; - HashMap> map = new HashMap<>(); + ChunkMap map = chunkMap.get(AxiomPaper.getChunkSource(level)); + HashMap> playerChunks = new HashMap<>(); for (Chunk chunk : changedChunks) { ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); - for (ServerPlayer serverPlayer2 : chunkMap.getPlayers(chunkPos, false)) { - map.computeIfAbsent(serverPlayer2, serverPlayer -> new ArrayList<>()).add((LevelChunk) ((CraftChunk)chunk).getHandle(ChunkStatus.BIOMES)); + LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ()); + for (ServerPlayer player : (List)getPlayers.invoke(map, chunkPos, false)) { + playerChunks.computeIfAbsent(player, p -> new ArrayList<>()).add(nativeChunk); } } - map.forEach((serverPlayer, list) -> serverPlayer.connection.send(ClientboundChunksBiomesPacket.forChunks(list))); - //TODO end multiversioning + playerChunks.forEach((serverPlayer, list) -> send.invoke(AxiomPaper.getConnection(serverPlayer), forChunks.invoke(null, list))); } } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java index b62ee46..39e1e0a 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockPacketListener.java @@ -1,23 +1,38 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.Reflection; import com.moulberry.axiom.buffer.MojBuf; import com.moulberry.axiom.event.AxiomModifyWorldEvent; +import com.moulberry.axiom.integration.RegionProtection; import com.moulberry.axiom.integration.VersionTranslator; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; public class SetBlockPacketListener implements AxiomPacketListener { + private static final Reflection.Method setBlock = Reflection.getTypedMethod(Level.class, boolean.class, BlockPos.class, BlockState.class, int.class); + + private static final Reflection.Method getX = Reflection.getTypedMethod(Vec3i.class, int.class, 0); + private static final Reflection.Method getY = Reflection.getTypedMethod(Vec3i.class, int.class, 1); + private static final Reflection.Method getZ = Reflection.getTypedMethod(Vec3i.class, int.class, 2); + + private static final Reflection.Method ackBlockChangesUpTo = Reflection.getMethod(ServerGamePacketListenerImpl.class, int.class); @Override public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) { + World world = player.getWorld(); + // Check if player is allowed to modify this world - AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, player.getWorld()); + AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world); Bukkit.getPluginManager().callEvent(modifyWorldEvent); if (modifyWorldEvent.isCancelled()) return; @@ -28,21 +43,24 @@ public class SetBlockPacketListener implements AxiomPacketListener { int sequenceId = buf.readInt(); ServerPlayer serverPlayer = AxiomPaper.convert(player); + int x = getX.invoke(pos); + int y = getY.invoke(pos); + int z = getZ.invoke(pos); // Update blocks - //TODO multiversioning - if (updateNeighbors) { - AxiomPaper.convert(player.getWorld()).setBlock(pos, state, 3); - } else { - ChunkSectionModifier section = new ChunkSectionModifier(player.getWorld(), pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4); - section.setState(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF, state); - section.finish(); + if(RegionProtection.getProtection.apply(player, world).canBuildInSection(x >> 4, y >> 4, z >> 4)) { + if (updateNeighbors) { + setBlock.invoke(AxiomPaper.convert(player.getWorld()), pos, state, 3); + } else { + ChunkSectionModifier section = new ChunkSectionModifier(player.getWorld(), x >> 4, y >> 4, z >> 4); + section.setState(x & 0xF, y & 0xF, z & 0xF, state); + section.finish(); + } } if (sequenceId >= 0) { - serverPlayer.connection.ackBlockChangesUpTo(sequenceId); + ackBlockChangesUpTo.invoke(AxiomPaper.getConnection(serverPlayer), sequenceId); } - //TODO end multiversioning } } diff --git a/src/main/java/com/moulberry/axiom/persistence/ItemStackDataType.java b/src/main/java/com/moulberry/axiom/persistence/ItemStackDataType.java index 96d907f..04ec0ee 100644 --- a/src/main/java/com/moulberry/axiom/persistence/ItemStackDataType.java +++ b/src/main/java/com/moulberry/axiom/persistence/ItemStackDataType.java @@ -25,7 +25,7 @@ public class ItemStackDataType implements PersistentDataType CraftPersistentDataContainer = Reflection.getClass("org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer"); - private static final Reflection.Method putAll = Reflection.getMethod(CraftPersistentDataContainer, "putAll", CompoundTag.class); + private static final Reflection.Method putAll = Reflection.getMethod(CraftPersistentDataContainer, "putAll", CompoundTag.class); private static final Reflection.Method toTagCompound = Reflection.getTypedMethod(CraftPersistentDataContainer, "toTagCompound", CompoundTag.class); private static final Reflection.Method save = Reflection.getTypedMethod(net.minecraft.world.item.ItemStack.class, CompoundTag.class, CompoundTag.class);