diff --git a/src/main/java/com/moulberry/axiom/AxiomPaper.java b/src/main/java/com/moulberry/axiom/AxiomPaper.java index 95dcfa0..619958b 100644 --- a/src/main/java/com/moulberry/axiom/AxiomPaper.java +++ b/src/main/java/com/moulberry/axiom/AxiomPaper.java @@ -6,10 +6,11 @@ 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.network.protocol.Packet; +import net.minecraft.server.level.*; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunk; import org.bukkit.Bukkit; @@ -19,6 +20,7 @@ import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -66,6 +68,23 @@ public class AxiomPaper extends JavaPlugin implements Listener { return getChunkSource.invoke(level); } + private static final Reflection.Method getLightEngine = Reflection.getTypedMethod(ServerChunkCache.class, ThreadedLevelLightEngine.class); + public static ThreadedLevelLightEngine getLightEngine(ServerChunkCache chunkSource) { + return getLightEngine.invoke(chunkSource); + } + + private static final Reflection.Method send = Reflection.getMethod(ServerPlayerConnection.class, Packet.class); + public static void sendPacket(ServerPlayer player, Packet packet) { + send.invoke(AxiomPaper.getConnection(player), packet); + } + + 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); + public static List getPlayersSeeingChunk(ServerLevel level, ChunkPos pos) { + ChunkMap map = chunkMap.get(AxiomPaper.getChunkSource(level)); + return (List)getPlayers.invoke(map, pos, false); + } + @Override public void onEnable() { instance = this; diff --git a/src/main/java/com/moulberry/axiom/Reflection.java b/src/main/java/com/moulberry/axiom/Reflection.java index 135f29f..066f792 100644 --- a/src/main/java/com/moulberry/axiom/Reflection.java +++ b/src/main/java/com/moulberry/axiom/Reflection.java @@ -127,6 +127,12 @@ public class Reflection { private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName(); + public static final int VERSION; // Format: 2 digit minor version, 2 digit CraftBukkit revision: eg. 2001 for v1_20_R1 + static { + String[] version = ORG_BUKKIT_CRAFTBUKKIT.substring(ORG_BUKKIT_CRAFTBUKKIT.lastIndexOf('.')).split("_"); + VERSION = Integer.parseInt(version[1]) * 100 + Integer.parseInt(version[2].substring(1)); + } + public static Class getClass(String name) { if(name.startsWith("org.bukkit.craftbukkit")) name = ORG_BUKKIT_CRAFTBUKKIT + name.substring(22); diff --git a/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java b/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java index 5c292bf..eed7664 100644 --- a/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java +++ b/src/main/java/com/moulberry/axiom/integration/ViaVersionTranslator.java @@ -49,7 +49,7 @@ public class ViaVersionTranslator implements VersionTranslator { @Override public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer consumer) { - //TODO GlobalPaletteBits depend on player version + //TODO GlobalPaletteBits depend on player version (currently only 1.20.1, depending on Axiom version) DataPalette container; try { container = new PaletteType1_18(PaletteType.BLOCKS, 15).read(buf); diff --git a/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java b/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java index 1ace875..cabc41d 100644 --- a/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java +++ b/src/main/java/com/moulberry/axiom/packet/ChunkSectionModifier.java @@ -2,12 +2,12 @@ package com.moulberry.axiom.packet; import com.moulberry.axiom.AxiomPaper; import com.moulberry.axiom.Reflection; +import com.moulberry.axiom.version.VersionWrapper; import net.minecraft.core.BlockPos; 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; @@ -18,7 +18,6 @@ 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; -import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.lighting.LightEventListener; import org.bukkit.World; @@ -42,7 +41,6 @@ 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); @@ -53,7 +51,7 @@ public class ChunkSectionModifier { ServerLevel level = AxiomPaper.convert(world); chunkSource = AxiomPaper.getChunkSource(level); - lightEngine = getLightEngine.invoke(chunkSource); + lightEngine = AxiomPaper.getLightEngine(chunkSource); chunk = AxiomPaper.getChunk(level, cx, cz); section = getSection.invoke(chunk, cy - world.getMinHeight() / 16); @@ -67,7 +65,6 @@ public class ChunkSectionModifier { 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) { BlockState old = setBlockState.invoke(section, sx, sy, sz, state, false); @@ -88,7 +85,7 @@ public class ChunkSectionModifier { blockChanged.invoke(chunkSource, pos); - if (hasDifferentLightProperties.invoke(null, chunk, pos, old, state)) { + if (VersionWrapper.impl.hasDifferentLightProperties(chunk, pos, old, state)) { checkBlock.invoke(lightEngine, pos); } } diff --git a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java index cc03732..26a8be8 100644 --- a/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java +++ b/src/main/java/com/moulberry/axiom/packet/SetBlockBufferPacketListener.java @@ -2,30 +2,22 @@ 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; import com.moulberry.axiom.event.AxiomModifyWorldEvent; import com.moulberry.axiom.integration.RegionProtection; import com.moulberry.axiom.integration.VersionTranslator; +import com.moulberry.axiom.version.VersionWrapper; 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.LevelChunk; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Player; -import java.util.*; +import java.util.HashSet; +import java.util.Set; public class SetBlockBufferPacketListener implements AxiomPacketListener { @@ -80,10 +72,6 @@ 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) -> { @@ -93,17 +81,7 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener { changedChunks.add(world.getChunkAt(cx, cz)); }); - ServerLevel level = AxiomPaper.convert(world); - ChunkMap map = chunkMap.get(AxiomPaper.getChunkSource(level)); - HashMap> playerChunks = new HashMap<>(); - for (Chunk chunk : changedChunks) { - ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); - 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); - } - } - playerChunks.forEach((serverPlayer, list) -> send.invoke(AxiomPaper.getConnection(serverPlayer), forChunks.invoke(null, list))); + VersionWrapper.impl.publishBiomeChange(AxiomPaper.convert(world), changedChunks); } } diff --git a/src/main/java/com/moulberry/axiom/version/Version19R2.java b/src/main/java/com/moulberry/axiom/version/Version19R2.java new file mode 100644 index 0000000..b4bf8b7 --- /dev/null +++ b/src/main/java/com/moulberry/axiom/version/Version19R2.java @@ -0,0 +1,41 @@ +package com.moulberry.axiom.version; + +import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.Reflection; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Chunk; + +import java.util.Set; + +public class Version19R2 implements VersionWrapper { + + private static final Reflection.Method getLightBlock = Reflection.getTypedMethod(BlockState.class, int.class, BlockGetter.class, BlockPos.class); + private static final Reflection.Field lightEmission = Reflection.getField(BlockState.class, int.class); + private static final Reflection.Field useShapeForLightOcclusion = Reflection.getField(BlockState.class, boolean.class); + @Override + public boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state) { + return getLightBlock.invoke(old, chunk, pos) != getLightBlock.invoke(state, chunk, pos) || lightEmission.get(old) != lightEmission.get(state) || useShapeForLightOcclusion.get(old) || useShapeForLightOcclusion.get(state); + } + + @Override + public void publishBiomeChange(ServerLevel level, Set chunks) { + ThreadedLevelLightEngine lightEngine = AxiomPaper.getLightEngine(AxiomPaper.getChunkSource(level)); + + for (Chunk chunk : chunks) { + ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); + LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ()); + ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(nativeChunk, lightEngine, null, null, true); + for (ServerPlayer player : AxiomPaper.getPlayersSeeingChunk(level, chunkPos)) { + AxiomPaper.sendPacket(player, packet); + } + } + } +} diff --git a/src/main/java/com/moulberry/axiom/version/Version20R1.java b/src/main/java/com/moulberry/axiom/version/Version20R1.java new file mode 100644 index 0000000..91ec9cf --- /dev/null +++ b/src/main/java/com/moulberry/axiom/version/Version20R1.java @@ -0,0 +1,42 @@ +package com.moulberry.axiom.version; + +import com.moulberry.axiom.AxiomPaper; +import com.moulberry.axiom.Reflection; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.lighting.LightEngine; +import org.bukkit.Chunk; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +public class Version20R1 implements VersionWrapper { + + private static final Reflection.Method hasDifferentLightProperties = Reflection.getTypedMethod(LightEngine.class, boolean.class, BlockGetter.class, BlockPos.class, BlockState.class, BlockState.class); + @Override + public boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state) { + return hasDifferentLightProperties.invoke(null, chunk, pos, old, state); + } + + private static final Reflection.Method forChunks = Reflection.getTypedMethod(ClientboundChunksBiomesPacket.class, ClientboundChunksBiomesPacket.class, List.class); + @Override + public void publishBiomeChange(ServerLevel level, Set chunks) { + HashMap> playerChunks = new HashMap<>(); + for (Chunk chunk : chunks) { + ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ()); + LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ()); + for (ServerPlayer player : AxiomPaper.getPlayersSeeingChunk(level, chunkPos)) { + playerChunks.computeIfAbsent(player, p -> new ArrayList<>()).add(nativeChunk); + } + } + playerChunks.forEach((serverPlayer, list) -> AxiomPaper.sendPacket(serverPlayer, forChunks.invoke(null, list))); + } +} diff --git a/src/main/java/com/moulberry/axiom/version/VersionWrapper.java b/src/main/java/com/moulberry/axiom/version/VersionWrapper.java new file mode 100644 index 0000000..c684acd --- /dev/null +++ b/src/main/java/com/moulberry/axiom/version/VersionWrapper.java @@ -0,0 +1,18 @@ +package com.moulberry.axiom.version; + +import com.moulberry.axiom.Reflection; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Chunk; + +import java.util.Set; + +public interface VersionWrapper { + VersionWrapper impl = Reflection.VERSION > 1902 ? new Version20R1() : new Version19R2(); + + boolean hasDifferentLightProperties(BlockGetter chunk, BlockPos pos, BlockState old, BlockState state); + void publishBiomeChange(ServerLevel level, Set chunks); + +}