Browse Source

More optimised ticking

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 4 years ago
parent
commit
5b36cf6b84

+ 0 - 1
common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java

@@ -29,7 +29,6 @@ public class ClothScreen {
                 }).setDefaultValue(-1).setTextGetter(integer -> new TextComponent(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlay.secondaryLevel = integer).build());
         general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.showNumber"), LightOverlay.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.showNumber = bool).build());
         general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.underwater"), LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build());
-        general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.mushroom"), LightOverlay.mushroom).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.mushroom = bool).build());
         general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.lineWidth"), Mth.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> new TextComponent("Light Width: " + LightOverlay.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build());
         general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.yellowColor"), LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build());
         general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.redColor"), LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build());

+ 7 - 0
common/src/main/java/me/shedaniel/lightoverlay/common/LevelChunkAccess.java

@@ -0,0 +1,7 @@
+package me.shedaniel.lightoverlay.common;
+
+import net.minecraft.world.level.chunk.LevelChunkSection;
+
+public interface LevelChunkAccess {
+    LevelChunkSection[] lightoverlay_getSections();
+}

+ 0 - 5
common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java

@@ -26,7 +26,6 @@ public class LightOverlay {
     public static boolean caching = false;
     public static boolean showNumber = false;
     public static boolean underwater = false;
-    public static boolean mushroom = false;
     public static float lineWidth = 1.0F;
     public static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF;
     public static File configFile;
@@ -97,7 +96,6 @@ public class LightOverlay {
             caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true");
             showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
             underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
-            mushroom = ((String) properties.computeIfAbsent("mushroom", a -> "false")).equalsIgnoreCase("true");
             lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
             {
                 int r, g, b;
@@ -133,7 +131,6 @@ public class LightOverlay {
             caching = false;
             showNumber = false;
             underwater = false;
-            mushroom = false;
             try {
                 saveConfig(file);
             } catch (IOException ex) {
@@ -163,8 +160,6 @@ public class LightOverlay {
         fos.write("\n".getBytes());
         fos.write(("underwater=" + underwater).getBytes());
         fos.write("\n".getBytes());
-        fos.write(("mushroom=" + mushroom).getBytes());
-        fos.write("\n".getBytes());
         fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
         fos.write("\n".getBytes());
         fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());

+ 88 - 47
common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayTicker.java

@@ -16,12 +16,14 @@ import net.minecraft.world.entity.MobCategory;
 import net.minecraft.world.level.BlockGetter;
 import net.minecraft.world.level.Level;
 import net.minecraft.world.level.LightLayer;
-import net.minecraft.world.level.biome.Biome;
 import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
 import net.minecraft.world.level.chunk.ChunkStatus;
 import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.LevelChunkSection;
 import net.minecraft.world.level.lighting.LayerLightEventListener;
 import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.Shapes;
 import org.apache.logging.log4j.LogManager;
 
 import java.util.Collections;
@@ -49,7 +51,25 @@ public class LightOverlayTicker {
     
     public void queueChunk(CubicChunkPos pos) {
         if (LightOverlay.enabled && LightOverlay.caching && !CALCULATING_POS.contains(pos)) {
-            POS.add(pos);
+            if (minecraft.level != null) {
+                var chunk = minecraft.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
+                if (chunk == null) return;
+                var sections = ((LevelChunkAccess) chunk).lightoverlay_getSections();
+                var firstSectionIndex = pos.y << 1 - chunk.getMinSection();
+                if (firstSectionIndex >= 0 && firstSectionIndex < sections.length) {
+                    var section = sections[firstSectionIndex];
+                    if (!LevelChunkSection.isEmpty(section)) {
+                        POS.add(pos);
+                        return;
+                    }
+                }
+                if (firstSectionIndex + 1 >= 0 && firstSectionIndex + 1 < sections.length) {
+                    var section = sections[firstSectionIndex + 1];
+                    if (!LevelChunkSection.isEmpty(section)) {
+                        POS.add(pos);
+                    }
+                }
+            }
         }
     }
     
@@ -83,18 +103,18 @@ public class LightOverlayTicker {
                     CHUNK_MAP.put(new CubicChunkPos(0, 0, 0), chunkData);
                     for (var blockPos : iterate) {
                         downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
-                        if (LightOverlay.showNumber) {
-                            var level = getCrossLevel(blockPos, downPos, world, block, collisionContext);
-                            if (level >= 0) {
-                                chunkData.put(blockPos.asLong(), (byte) level);
-                            }
-                        } else {
-                            var biome = !LightOverlay.mushroom ? world.getBiome(blockPos) : null;
-                            var type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext);
-                            if (type != LightOverlay.CROSS_NONE) {
-                                chunkData.put(blockPos.asLong(), type);
-                            }
-                        }
+//                        if (LightOverlay.showNumber) {
+//                            var level = getCrossLevel(blockPos, downPos, world, block, collisionContext);
+//                            if (level >= 0) {
+//                                chunkData.put(blockPos.asLong(), (byte) level);
+//                            }
+//                        } else {
+//                            var biome = !LightOverlay.mushroom ? world.getBiome(blockPos) : null;
+//                            var type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext);
+//                            if (type != LightOverlay.CROSS_NONE) {
+//                                chunkData.put(blockPos.asLong(), type);
+//                            }
+//                        }
                     }
                 } else {
                     var height = Mth.ceil(Minecraft.getInstance().level.getHeight() / 32.0);
@@ -169,11 +189,9 @@ public class LightOverlayTicker {
                                 var playerPosY1 = ((int) minecraft.player.getY()) >> 5;
                                 var playerPosZ1 = ((int) minecraft.player.getZ()) >> 4;
                                 var count = 0;
-                                var stopwatch = Stopwatch.createStarted();
                                 if (finalC1 != null) count += processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
                                 if (finalC2 != null) count += processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
                                 if (finalC3 != null) count += processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
-                                System.out.println(stopwatch.stop());
                                 synchronized (this) {
                                     LightOverlay.blocksScanned += count;
                                 }
@@ -196,37 +214,27 @@ public class LightOverlayTicker {
         if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || POS.contains(pos)) {
             return 0;
         }
+        var stopwatch = Stopwatch.createStarted();
         try {
             return calculateChunk(minecraft.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), minecraft.level, pos, context);
         } catch (Throwable throwable) {
             LogManager.getLogger().throwing(throwable);
+        } finally {
+            System.out.println(stopwatch.stop());
         }
         return 0;
     }
     
     private int calculateChunk(LevelChunk chunk, Level world, CubicChunkPos chunkPos, CollisionContext collisionContext) {
         if (world != null && chunk != null) {
-            Long2ByteMap chunkData = new Long2ByteOpenHashMap();
             var block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
             var sky = LightOverlay.showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
-            var down = new BlockPos.MutableBlockPos();
+            Long2ByteMap chunkData = new Long2ByteOpenHashMap();
             var count = 0;
-            for (var pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) {
-                down.setWithOffset(pos, 0, -1, 0);
-                count++;
-                if (LightOverlay.showNumber) {
-                    var level = getCrossLevel(pos, down, chunk, block, collisionContext);
-                    if (level >= 0) {
-                        chunkData.put(pos.asLong(), (byte) level);
-                    }
-                } else {
-                    var biome = !LightOverlay.mushroom ? world.getBiome(pos) : null;
-                    var type = getCrossType(pos, biome, down, chunk, block, sky, collisionContext);
-                    if (type != LightOverlay.CROSS_NONE) {
-                        chunkData.put(pos.asLong(), type);
-                    }
-                }
-            }
+            var sections = ((LevelChunkAccess) chunk).lightoverlay_getSections();
+            var firstSectionIndex = chunkPos.y << 1 - chunk.getMinSection();
+            calculateSection(chunkData, block, sky, world, chunk, sections, firstSectionIndex, chunkPos, 0, collisionContext);
+            calculateSection(chunkData, block, sky, world, chunk, sections, firstSectionIndex + 1, chunkPos, 16, collisionContext);
             CHUNK_MAP.put(chunkPos, chunkData);
             return count;
         } else {
@@ -235,14 +243,51 @@ public class LightOverlayTicker {
         }
     }
     
-    public byte getCrossType(BlockPos pos, Biome biome, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) {
-        var blockBelowState = world.getBlockState(down);
-        var blockUpperState = world.getBlockState(pos);
-        var upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+    private void calculateSection(Long2ByteMap chunkData, LayerLightEventListener block, LayerLightEventListener sky, Level world, LevelChunk chunk, LevelChunkSection[] sections,
+            int sectionIndex, CubicChunkPos chunkPos, int yOffset, CollisionContext collisionContext) {
+        if (sectionIndex >= 0 && sectionIndex < sections.length) {
+            var section = sections[sectionIndex];
+            if (!LevelChunkSection.isEmpty(section)) {
+                var startingYBelow = chunkPos.getMinBlockY() - 1 + yOffset;
+                var skipOneLayer = startingYBelow < world.getMinBuildHeight();
+                for (var x = 0; x < 16; x++) {
+                    for (var z = 0; z < 16; z++) {
+                        var lastDownPos = new BlockPos.MutableBlockPos(chunkPos.getMinBlockX() + x, startingYBelow, chunkPos.getMinBlockZ() + z);
+                        var lastDown = skipOneLayer ? null : chunk.getBlockState(lastDownPos);
+                        var currentPos = new BlockPos.MutableBlockPos(lastDownPos.getX(), lastDownPos.getY() + 1, lastDownPos.getZ());
+                        for (var y = 0; y < 16; y++) {
+                            var current = section.getBlockState(x, y, z);
+                            if (lastDown != null) {
+                                if (LightOverlay.showNumber) {
+                                    var level = getCrossLevel(current, lastDown, currentPos, lastDownPos, chunk, block, collisionContext);
+                                    if (level >= 0) {
+                                        chunkData.put(currentPos.asLong(), (byte) level);
+                                    }
+                                } else {
+                                    var type = getCrossType(current, lastDown, currentPos, lastDownPos, chunk, block, sky, collisionContext);
+                                    if (type != LightOverlay.CROSS_NONE) {
+                                        chunkData.put(currentPos.asLong(), type);
+                                    }
+                                }
+                            }
+                            lastDown = current;
+                            lastDownPos.set(currentPos);
+                            currentPos.move(0, 1, 0);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    public byte getCrossType(BlockState blockUpperState, BlockState blockBelowState, BlockPos pos, BlockPos down, BlockGetter world,
+            LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) {
         if (!LightOverlay.underwater && !blockUpperState.getFluidState().isEmpty())
             return LightOverlay.CROSS_NONE;
+        var upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+        var upperCollisionShapeFace = upperCollisionShape.getFaceShape(Direction.UP);
         // Check if the outline is full
-        if (Block.isFaceFull(upperCollisionShape, Direction.UP))
+        if (upperCollisionShapeFace == Shapes.block() || Block.isShapeFullBlock(upperCollisionShapeFace))
             return LightOverlay.CROSS_NONE;
         // TODO: Not to hard code no redstone
         if (blockUpperState.isSignalSource())
@@ -255,8 +300,6 @@ public class LightOverlayTicker {
         // Check block state allow spawning (excludes bedrock and barriers automatically)
         if (!blockBelowState.isValidSpawn(world, down, TESTING_ENTITY_TYPE.get()))
             return LightOverlay.CROSS_NONE;
-        if (!LightOverlay.mushroom && Biome.BiomeCategory.MUSHROOM == biome.getBiomeCategory())
-            return LightOverlay.CROSS_NONE;
         var blockLightLevel = block.getLightValue(pos);
         var skyLightLevel = sky.getLightValue(pos);
         if (blockLightLevel > LightOverlay.higherCrossLevel)
@@ -266,18 +309,16 @@ public class LightOverlayTicker {
         return LightOverlay.lowerCrossLevel >= 0 && blockLightLevel > LightOverlay.lowerCrossLevel ? LightOverlay.CROSS_SECONDARY : LightOverlay.CROSS_RED;
     }
     
-    public static int getCrossLevel(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener view, CollisionContext collisionContext) {
-        var blockBelowState = world.getBlockState(down);
-        var blockUpperState = world.getBlockState(pos);
-        var collisionShape = blockBelowState.getCollisionShape(world, down, collisionContext);
-        var upperCollisionShape = blockUpperState.getCollisionShape(world, pos, collisionContext);
+    public int getCrossLevel(BlockState blockUpperState, BlockState blockBelowState, BlockPos pos, BlockPos down, BlockGetter world,
+            LayerLightEventListener view, CollisionContext collisionContext) {
         if (!LightOverlay.underwater && !blockUpperState.getFluidState().isEmpty())
             return -1;
         if (!blockBelowState.getFluidState().isEmpty())
             return -1;
         if (blockBelowState.isAir())
             return -1;
-        if (Block.isFaceFull(upperCollisionShape, Direction.DOWN))
+        var upperCollisionShape = blockUpperState.getCollisionShape(world, pos, collisionContext).getFaceShape(Direction.DOWN);
+        if (upperCollisionShape == Shapes.block() || Block.isShapeFullBlock(upperCollisionShape))
             return -1;
         return view.getLightValue(pos);
     }

+ 18 - 0
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinLevelChunk.java

@@ -0,0 +1,18 @@
+package me.shedaniel.lightoverlay.fabric.mixin;
+
+import me.shedaniel.lightoverlay.common.LevelChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(LevelChunk.class)
+public class MixinLevelChunk implements LevelChunkAccess {
+    @Shadow @Final private LevelChunkSection[] sections;
+    
+    @Override
+    public LevelChunkSection[] lightoverlay_getSections() {
+        return sections;
+    }
+}

+ 2 - 2
fabric/src/main/resources/lightoverlay.mixins.json

@@ -3,9 +3,9 @@
   "package": "me.shedaniel.lightoverlay.fabric.mixin",
   "minVersion": "0.7.11",
   "compatibilityLevel": "JAVA_8",
-  "mixins": [],
   "client": [
-    "MixinClientConnection"
+    "MixinClientConnection",
+    "MixinLevelChunk"
   ],
   "injectors": {
     "defaultRequire": 1