From 82e575e897cc120f7e7800a22bfed2782091340a Mon Sep 17 00:00:00 2001 From: LOOHP Date: Thu, 5 Nov 2020 16:08:04 +0800 Subject: [PATCH] 0.3.7-ALPHA Exterimental Light Engine (Reset mapping.json to use) --- pom.xml | 7 +- .../loohp/limbo/Server/ClientConnection.java | 20 +++- .../Packets/PacketPlayOutLightUpdate.java | 111 ++++++++++++++++++ src/com/loohp/limbo/World/BlockState.java | 30 +++++ src/com/loohp/limbo/World/Environment.java | 69 ++++++++++- src/com/loohp/limbo/World/LightEngine.java | 43 +++++++ .../loohp/limbo/World/LightEngineBlock.java | 75 ++++++++++++ src/com/loohp/limbo/World/LightEngineSky.java | 87 ++++++++++++++ src/com/loohp/limbo/World/Schematic.java | 7 +- src/com/loohp/limbo/World/World.java | 51 +++++++- src/mapping.json | 1 + 11 files changed, 489 insertions(+), 12 deletions(-) create mode 100644 src/com/loohp/limbo/Server/Packets/PacketPlayOutLightUpdate.java create mode 100644 src/com/loohp/limbo/World/LightEngine.java create mode 100644 src/com/loohp/limbo/World/LightEngineBlock.java create mode 100644 src/com/loohp/limbo/World/LightEngineSky.java diff --git a/pom.xml b/pom.xml index 4617b0c..4636645 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.loohp Limbo - 0.3.6-ALPHA + 0.3.7-ALPHA src @@ -64,6 +64,11 @@ + + org.apache.commons + commons-lang3 + 3.11 + com.github.Querz NBT diff --git a/src/com/loohp/limbo/Server/ClientConnection.java b/src/com/loohp/limbo/Server/ClientConnection.java index 6c29737..cbc6b30 100644 --- a/src/com/loohp/limbo/Server/ClientConnection.java +++ b/src/com/loohp/limbo/Server/ClientConnection.java @@ -39,6 +39,7 @@ import com.loohp.limbo.Server.Packets.PacketPlayInRotation; import com.loohp.limbo.Server.Packets.PacketPlayInTabComplete; import com.loohp.limbo.Server.Packets.PacketPlayOutDeclareCommands; import com.loohp.limbo.Server.Packets.PacketPlayOutDisconnect; +import com.loohp.limbo.Server.Packets.PacketPlayOutLightUpdate; import com.loohp.limbo.Server.Packets.PacketPlayOutLogin; import com.loohp.limbo.Server.Packets.PacketPlayOutMapChunk; import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities; @@ -300,10 +301,27 @@ public class ClientConnection extends Thread { if (chunk != null) { PacketPlayOutMapChunk chunkdata = new PacketPlayOutMapChunk(x, z, chunk, world.getEnvironment()); sendPacket(chunkdata); - //Limbo.getInstance().getConsole().sendMessage(x + ", " + z); } } } + + for (int x = 0; x < world.getChunks().length; x++) { + for (int z = 0; z < world.getChunks()[x].length; z++) { + List blockChunk = world.getLightEngineBlock().getBlockLightBitMask(x, z); + if (blockChunk == null) { + blockChunk = new ArrayList<>(); + } + List skyChunk = null; + if (world.hasSkyLight()) { + skyChunk = world.getLightEngineSky().getSkyLightBitMask(x, z); + } + if (skyChunk == null) { + skyChunk = new ArrayList<>(); + } + PacketPlayOutLightUpdate chunkdata = new PacketPlayOutLightUpdate(x, z, true, skyChunk, blockChunk); + sendPacket(chunkdata); + } + } SkinResponse skinresponce = isBungeecord ? bungeeSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName()); PlayerSkinProperty skin = skinresponce != null ? new PlayerSkinProperty(skinresponce.getSkin(), skinresponce.getSignature()) : null; diff --git a/src/com/loohp/limbo/Server/Packets/PacketPlayOutLightUpdate.java b/src/com/loohp/limbo/Server/Packets/PacketPlayOutLightUpdate.java new file mode 100644 index 0000000..3cc883e --- /dev/null +++ b/src/com/loohp/limbo/Server/Packets/PacketPlayOutLightUpdate.java @@ -0,0 +1,111 @@ +package com.loohp.limbo.Server.Packets; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; + +import com.loohp.limbo.Utils.DataTypeIO; + +public class PacketPlayOutLightUpdate extends PacketOut { + + private int chunkX; + private int chunkZ; + private boolean trustEdges; + private int skyLightBitMask; + private int blockLightBitMask; + private List skylightArrays; + private List blocklightArrays; + + public PacketPlayOutLightUpdate(int chunkX, int chunkZ, boolean trustEdges, List skylightArrays, List blocklightArrays) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.trustEdges = trustEdges; + this.skylightArrays = skylightArrays; + this.blocklightArrays = blocklightArrays; + + skyLightBitMask = 0; + for (int i = Math.min(17, skylightArrays.size() - 1); i >= 0; i--) { + skyLightBitMask = skyLightBitMask >> 1; + if (skylightArrays.get(i) != null) { + skyLightBitMask |= 131072; + } + } + + blockLightBitMask = 0; + for (int i = Math.min(17, blocklightArrays.size() - 1); i >= 0; i--) { + blockLightBitMask = blockLightBitMask >> 1; + if (blocklightArrays.get(i) != null) { + blockLightBitMask |= 131072; + } + } + } + + public int getChunkX() { + return chunkX; + } + + public int getChunkZ() { + return chunkZ; + } + + public boolean isTrustEdges() { + return trustEdges; + } + + public int getSkyLightBitMask() { + return skyLightBitMask; + } + + public int getBlockLightBitMask() { + return blockLightBitMask; + } + + public List getSkylightArrays() { + return skylightArrays; + } + + public List getBlocklightArrays() { + return blocklightArrays; + } + + @Override + public byte[] serializePacket() throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + DataOutputStream output = new DataOutputStream(buffer); + output.writeByte(Packet.getPlayOut().get(getClass())); + DataTypeIO.writeVarInt(output, chunkX); + DataTypeIO.writeVarInt(output, chunkZ); + output.writeBoolean(trustEdges); + DataTypeIO.writeVarInt(output, skyLightBitMask); + DataTypeIO.writeVarInt(output, blockLightBitMask); + DataTypeIO.writeVarInt(output, ~skyLightBitMask & 262143); + DataTypeIO.writeVarInt(output, ~blockLightBitMask & 262143); + + for (int i = skylightArrays.size() - 1; i >= 0; i--) { + Byte[] array = skylightArrays.get(i); + if (array != null) { + DataTypeIO.writeVarInt(output, 2048); + //System.out.println(Arrays.toString(ArrayUtils.toPrimitive(array))); + for (int u = 0; u < array.length; u++) { + output.writeByte(array[u]); + } + } + } + + for (int i = blocklightArrays.size() - 1; i >= 0; i--) { + Byte[] array = blocklightArrays.get(i); + if (array != null) { + DataTypeIO.writeVarInt(output, 2048); + //System.out.println(Arrays.toString(ArrayUtils.toPrimitive(array))); + for (int u = 0; u < array.length; u++) { + output.writeByte(array[u]); + } + } + } + + return buffer.toByteArray(); + } + +} diff --git a/src/com/loohp/limbo/World/BlockState.java b/src/com/loohp/limbo/World/BlockState.java index 31bbcbf..1527c98 100644 --- a/src/com/loohp/limbo/World/BlockState.java +++ b/src/com/loohp/limbo/World/BlockState.java @@ -60,4 +60,34 @@ public class BlockState { properties.putString(key, ((T) value).toString()); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((tag == null) ? 0 : tag.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BlockState other = (BlockState) obj; + if (tag == null) { + if (other.tag != null) { + return false; + } + } else if (!tag.equals(other.tag)) { + return false; + } + return true; + } + } diff --git a/src/com/loohp/limbo/World/Environment.java b/src/com/loohp/limbo/World/Environment.java index 152c928..fb5431a 100644 --- a/src/com/loohp/limbo/World/Environment.java +++ b/src/com/loohp/limbo/World/Environment.java @@ -1,13 +1,18 @@ package com.loohp.limbo.World; +import java.util.HashSet; +import java.util.Set; + import com.loohp.limbo.Utils.NamespacedKey; public class Environment { + + public static final Environment NORMAL = new Environment(new NamespacedKey("minecraft:overworld"), true); + public static final Environment NETHER = new Environment(new NamespacedKey("minecraft:the_nether"), false); + public static final Environment END = new Environment(new NamespacedKey("minecraft:the_end"), false); - public static final Environment NORMAL = new Environment(new NamespacedKey("minecraft:overworld")); - public static final Environment NETHER = new Environment(new NamespacedKey("minecraft:the_nether")); - public static final Environment END = new Environment(new NamespacedKey("minecraft:the_end")); - + public static final Set REGISTERED_ENVIRONMENTS = new HashSet<>(); + public static Environment fromNamespacedKey(NamespacedKey key) { if (key.equals(NORMAL.getNamespacedKey())) { return NORMAL; @@ -19,19 +24,71 @@ public class Environment { return null; } + @Deprecated public static Environment createCustom(NamespacedKey key) { - return new Environment(key); + return createCustom(key, true); + } + + public static Environment createCustom(NamespacedKey key, boolean hasSkyLight) { + if (REGISTERED_ENVIRONMENTS.stream().anyMatch(each -> each.getNamespacedKey().equals(key))) { + throw new IllegalArgumentException("An Environment is already created with this NamespacedKey"); + } + return new Environment(key, hasSkyLight); + } + + public static Environment getCustom(NamespacedKey key) { + return REGISTERED_ENVIRONMENTS.stream().filter(each -> each.getNamespacedKey().equals(key)).findFirst().orElse(null); } //========================= private NamespacedKey key; + private boolean hasSkyLight; - private Environment(NamespacedKey key) { + private Environment(NamespacedKey key, boolean hasSkyLight) { this.key = key; + this.hasSkyLight = hasSkyLight; } public NamespacedKey getNamespacedKey() { return key; } + + public boolean hasSkyLight() { + return hasSkyLight; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (hasSkyLight ? 1231 : 1237); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Environment other = (Environment) obj; + if (hasSkyLight != other.hasSkyLight) { + return false; + } + if (key == null) { + if (other.key != null) { + return false; + } + } else if (!key.equals(other.key)) { + return false; + } + return true; + } } \ No newline at end of file diff --git a/src/com/loohp/limbo/World/LightEngine.java b/src/com/loohp/limbo/World/LightEngine.java new file mode 100644 index 0000000..35b103f --- /dev/null +++ b/src/com/loohp/limbo/World/LightEngine.java @@ -0,0 +1,43 @@ +package com.loohp.limbo.World; + +import java.util.HashMap; +import java.util.Map; + +public abstract class LightEngine { + + private static Map blockLightLevelMapping = new HashMap<>(); + + static { + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:torch", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + blockLightLevelMapping.put("minecraft:beacon", (byte) 15); + + } + + public static int getBlockLight(BlockState block) { + return blockLightLevelMapping.getOrDefault(block.getType().toString(), (byte) 0); + } + +} diff --git a/src/com/loohp/limbo/World/LightEngineBlock.java b/src/com/loohp/limbo/World/LightEngineBlock.java new file mode 100644 index 0000000..bc56daf --- /dev/null +++ b/src/com/loohp/limbo/World/LightEngineBlock.java @@ -0,0 +1,75 @@ +package com.loohp.limbo.World; + +import java.util.ArrayList; +import java.util.List; + +public class LightEngineBlock extends LightEngine { + + private World world; + private byte[][][] blockLightArray; + + public LightEngineBlock(World world) { + blockLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16]; + this.world = world; + updateWorld(); + } + + public void updateWorld() { + blockLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16]; + for (int x = 0; x < world.getWidth(); x++) { + for (int y = 0; y < 256; y++) { + for (int z = 0; z < world.getLength(); z++) { + updateBlock(x, y, z); + } + } + } + } + + private void updateBlock(int x, int y, int z) { + BlockState block = world.getBlock(x, y, z); + int lightLevel = getBlockLight(block); + if (lightLevel > 0) { + propergate(lightLevel, x, y, z); + } + } + + private void propergate(int level, int x, int y, int z) { + if (blockLightArray[x][y + 16][z] < level) { + blockLightArray[x][y + 16][z] = (byte) level; + if (level > 1) { + propergate(level - 1, x + 1, y, z); + propergate(level - 1, x - 1, y, z); + propergate(level - 1, x, y + 1, z); + propergate(level - 1, x, y - 1, z); + propergate(level - 1, x, y, z + 1); + propergate(level - 1, x, y, z - 1); + } + } + } + + public List getBlockLightBitMask(int chunkX, int chunkZ) { + List subchunks = new ArrayList<>(18); + int startX = chunkX * 16; + int endingX = startX + 16; + int startZ = chunkZ * 16; + int endingZ = startZ + 16; + + for (int sub = 17; sub >= 0; sub--) { + List array = new ArrayList<>(); + for (int y = sub * 16; y < (sub * 16) + 16; y++) { + for (int z = startZ; z < endingZ; z++) { + for (int x = startX; x < endingX; x += 2) { + int bit = blockLightArray[x][y][z]; + bit = bit << 4; + bit |= blockLightArray[x + 1][y][z]; + array.add((byte) bit); + } + } + } + subchunks.add(array.toArray(new Byte[2048])); + } + + return subchunks; + } + +} diff --git a/src/com/loohp/limbo/World/LightEngineSky.java b/src/com/loohp/limbo/World/LightEngineSky.java new file mode 100644 index 0000000..76e650e --- /dev/null +++ b/src/com/loohp/limbo/World/LightEngineSky.java @@ -0,0 +1,87 @@ +package com.loohp.limbo.World; + +import java.util.ArrayList; +import java.util.List; + +public class LightEngineSky extends LightEngine { + + private World world; + private byte[][][] skyLightArray; + + public LightEngineSky(World world) { + skyLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16]; + /* + for (byte[][] arrayarray : skyLightArray) { + for (byte[] array : arrayarray) { + Arrays.fill(array, (byte) 0); + } + } + */ + this.world = world; + updateWorld(); + } + + public void updateWorld() { + skyLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16]; + for (int x = 0; x < world.getWidth(); x++) { + for (int z = 0; z < world.getLength(); z++) { + updateColumn(x, z); + } + } + } + + private void updateColumn(int x, int z) { + for (int y = 272; y >= 256; y--) { + propergate(15, x, y, z); + } + for (int y = 255; y >= 0; y--) { + BlockState block = world.getBlock(x, y, z); + //System.out.println("X:" + x + " Y: " + y + " Z: " + z + " Block: " + block.getType().toString()); + if (!block.getType().toString().equals("minecraft:air")) { + break; + } + propergate(15, x, y, z); + } + } + + private void propergate(int level, int x, int y, int z) { + try { + if (skyLightArray[x][y + 16][z] < level) { + skyLightArray[x][y + 16][z] = (byte) level; + if (level > 1) { + propergate(level - 1, x + 1, y, z); + propergate(level - 1, x - 1, y, z); + propergate(level - 1, x, y + 1, z); + propergate(level - 1, x, y, z + 1); + propergate(level - 1, x, y, z - 1); + } + } + } catch (ArrayIndexOutOfBoundsException e) {} + } + + public List getSkyLightBitMask(int chunkX, int chunkZ) { + List subchunks = new ArrayList<>(18); + int startX = chunkX * 16; + int endingX = startX + 16; + int startZ = chunkZ * 16; + int endingZ = startZ + 16; + + for (int sub = 17; sub >= 0; sub--) { + List array = new ArrayList<>(); + for (int y = sub * 16; y < (sub * 16) + 16; y++) { + for (int z = startZ; z < endingZ; z++) { + for (int x = startX; x < endingX; x += 2) { + int bit = skyLightArray[x][y][z]; + bit = bit << 4; + bit |= skyLightArray[x + 1][y][z]; + array.add((byte) bit); + } + } + } + subchunks.add(array.toArray(new Byte[2048])); + } + + return subchunks; + } + +} diff --git a/src/com/loohp/limbo/World/Schematic.java b/src/com/loohp/limbo/World/Schematic.java index 17e1d1f..7dc6ef9 100644 --- a/src/com/loohp/limbo/World/Schematic.java +++ b/src/com/loohp/limbo/World/Schematic.java @@ -78,11 +78,16 @@ public class Schematic { heightMap.putLongArray("MOTION_BLOCKING", new long[] {1371773531765642314L,1389823183635651148L,1371738278539598925L,1389823183635388492L,1353688558756731469L,1389823114781694027L,1317765589597723213L,1371773531899860042L,1389823183635651149L,1371773462911685197L,1389823183635650636L,1353688626805119565L,1371773531900123211L,1335639250618849869L,1371738278674077258L,1389823114781694028L,1353723811310638154L,1371738278674077259L,1335674228429068364L,1335674228429067338L,1335674228698027594L,1317624576693539402L,1335709481520370249L,1299610178184057417L,1335638906349064264L,1299574993811968586L,1299574924958011464L,1299610178184056904L,1299574924958011464L,1299610109330100296L,1299574924958011464L,1299574924823793736L,1299574924958011465L,1281525273222484040L,1299574924958011464L,1281525273222484040L,9548107335L}); chunk.setHeightMaps(heightMap); chunk.setBiomes(new int[256]); - //chunk.cleanupPalettesAndBlockStates(); + chunk.cleanupPalettesAndBlockStates(); } } } + world.getLightEngineBlock().updateWorld(); + if (world.hasSkyLight()) { + world.getLightEngineSky().updateWorld(); + } + return world; } } \ No newline at end of file diff --git a/src/com/loohp/limbo/World/World.java b/src/com/loohp/limbo/World/World.java index 4598e1f..194b9b5 100644 --- a/src/com/loohp/limbo/World/World.java +++ b/src/com/loohp/limbo/World/World.java @@ -13,11 +13,17 @@ public class World { private String name; private Environment environment; private Chunk[][] chunks; + private int width; + private int length; + private LightEngineBlock lightEngineBlock; + private LightEngineSky lightEngineSky; public World(String name, int width, int length, Environment environment) { this.name = name; this.environment = environment; this.chunks = new Chunk[(width >> 4) + 1][(length >> 4) + 1]; + this.width = width; + this.length = length; for (int x = 0; x < chunks.length; x++) { for (int z = 0; z < chunks[x].length; z++) { @@ -26,7 +32,7 @@ public class World { chunk.cleanupPalettesAndBlockStates(); CompoundTag heightMap = new CompoundTag(); heightMap.putLongArray("MOTION_BLOCKING", - new long[] { 1371773531765642314L, 1389823183635651148L, 1371738278539598925L, + new long[] {1371773531765642314L, 1389823183635651148L, 1371738278539598925L, 1389823183635388492L, 1353688558756731469L, 1389823114781694027L, 1317765589597723213L, 1371773531899860042L, 1389823183635651149L, 1371773462911685197L, 1389823183635650636L, 1353688626805119565L, 1371773531900123211L, 1335639250618849869L, 1371738278674077258L, @@ -35,12 +41,29 @@ public class World { 1299610178184057417L, 1335638906349064264L, 1299574993811968586L, 1299574924958011464L, 1299610178184056904L, 1299574924958011464L, 1299610109330100296L, 1299574924958011464L, 1299574924823793736L, 1299574924958011465L, 1281525273222484040L, 1299574924958011464L, - 1281525273222484040L, 9548107335L }); + 1281525273222484040L, 9548107335L}); chunk.setHeightMaps(heightMap); chunk.setBiomes(new int[256]); chunk.setTileEntities(new ListTag(CompoundTag.class)); } } + + this.lightEngineBlock = new LightEngineBlock(this); + if (environment.hasSkyLight()) { + this.lightEngineSky = new LightEngineSky(this); + } + } + + public LightEngineBlock getLightEngineBlock() { + return lightEngineBlock; + } + + public LightEngineSky getLightEngineSky() { + return lightEngineSky; + } + + public boolean hasSkyLight() { + return lightEngineSky != null; } protected void setBlock(int x, int y, int z, String blockdata) { @@ -59,7 +82,13 @@ public class World { chunk = Chunk.newChunk(); this.chunks[(x >> 4)][(z >> 4)] = chunk; } - return new BlockState(chunk.getBlockStateAt(x % 16, y % 16, z % 16)); + + CompoundTag tag = chunk.getBlockStateAt(x, y, z); + if (tag == null) { + tag = new CompoundTag(); + tag.putString("Name", "minecraft:air"); + } + return new BlockState(tag); } public void setBlock(int x, int y, int z, BlockState state) { @@ -87,6 +116,22 @@ public class World { return environment; } + public int getWidth() { + return width; + } + + public int getLength() { + return length; + } + + public int getChunkWidth() { + return (width >> 4) + 1; + } + + public int getChunkLength() { + return (length >> 4) + 1; + } + @Override public int hashCode() { final int prime = 31; diff --git a/src/mapping.json b/src/mapping.json index 05c0e6c..330089b 100644 --- a/src/mapping.json +++ b/src/mapping.json @@ -27,6 +27,7 @@ "PacketPlayOutChat": "0x0E", "PacketPlayOutPlayerAbilities": "0x30", "PacketPlayOutMapChunk": "0x20", + "PacketPlayOutLightUpdate": "0x23", "PacketPlayOutKeepAlive": "0x1F", "PacketPlayOutPlayerInfo": "0x32", "PacketPlayOutUpdateViewPosition": "0x40",