]> git.lizzy.rs Git - LightOverlay.git/blob - fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java
28883f82fd3e20521d8f51780f9aa17f82db034e
[LightOverlay.git] / fabric / src / main / java / me / shedaniel / lightoverlay / fabric / LightOverlay.java
1 package me.shedaniel.lightoverlay.fabric;
2
3 import com.google.common.collect.Lists;
4 import com.google.common.collect.Maps;
5 import com.mojang.blaze3d.platform.GlStateManager;
6 import com.mojang.blaze3d.systems.RenderSystem;
7 import me.shedaniel.cloth.hooks.ClothClientHooks;
8 import net.fabricmc.api.ClientModInitializer;
9 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
10 import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
11 import net.fabricmc.fabric.api.event.client.ClientTickCallback;
12 import net.fabricmc.loader.api.FabricLoader;
13 import net.minecraft.block.Block;
14 import net.minecraft.block.BlockState;
15 import net.minecraft.client.MinecraftClient;
16 import net.minecraft.client.font.TextRenderer;
17 import net.minecraft.client.network.ClientPlayerEntity;
18 import net.minecraft.client.render.*;
19 import net.minecraft.client.util.InputUtil;
20 import net.minecraft.client.util.math.Rotation3;
21 import net.minecraft.client.world.ClientWorld;
22 import net.minecraft.entity.Entity;
23 import net.minecraft.entity.EntityCategory;
24 import net.minecraft.entity.EntityContext;
25 import net.minecraft.entity.EntityType;
26 import net.minecraft.entity.player.PlayerEntity;
27 import net.minecraft.tag.BlockTags;
28 import net.minecraft.text.TranslatableText;
29 import net.minecraft.util.Identifier;
30 import net.minecraft.util.math.*;
31 import net.minecraft.util.shape.VoxelShape;
32 import net.minecraft.world.BlockView;
33 import net.minecraft.world.LightType;
34 import net.minecraft.world.World;
35 import net.minecraft.world.biome.Biome;
36 import net.minecraft.world.chunk.ChunkStatus;
37 import net.minecraft.world.chunk.WorldChunk;
38 import net.minecraft.world.chunk.light.ChunkLightingView;
39 import org.apache.logging.log4j.LogManager;
40 import org.lwjgl.opengl.GL11;
41
42 import java.io.File;
43 import java.io.FileInputStream;
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.text.DecimalFormat;
47 import java.util.*;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.Executors;
50
51 public class LightOverlay implements ClientModInitializer {
52     static final DecimalFormat FORMAT = new DecimalFormat("#.#");
53     private static final String KEYBIND_CATEGORY = "key.lightoverlay.category";
54     private static final Identifier ENABLE_OVERLAY_KEYBIND = new Identifier("lightoverlay", "enable_overlay");
55     private static final Identifier INCREASE_REACH_KEYBIND = new Identifier("lightoverlay", "increase_reach");
56     private static final Identifier DECREASE_REACH_KEYBIND = new Identifier("lightoverlay", "decrease_reach");
57     private static final Identifier INCREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "increase_line_width");
58     private static final Identifier DECREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "decrease_line_width");
59     static int reach = 12;
60     static int crossLevel = 7;
61     static boolean showNumber = false;
62     static boolean smoothLines = true;
63     static boolean underwater = false;
64     static float lineWidth = 1.0F;
65     static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
66     static File configFile = new File(FabricLoader.getInstance().getConfigDirectory(), "lightoverlay.properties");
67     private static FabricKeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
68     private static boolean enabled = false;
69     private static EntityType<Entity> testingEntityType;
70     private static int threadNumber = 0;
71     private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
72         Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
73         thread.setDaemon(true);
74         return thread;
75     });
76     private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
77     private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
78     private static long ticks = 0;
79     
80     static {
81         ClientTickCallback.EVENT.register(client -> {
82             try {
83                 ticks++;
84                 if (MinecraftClient.getInstance().player == null || !enabled) {
85                     POS.clear();
86                     CHUNK_MAP.clear();
87                 } else {
88                     ClientPlayerEntity player = MinecraftClient.getInstance().player;
89                     ClientWorld world = MinecraftClient.getInstance().world;
90                     EntityContext entityContext = EntityContext.of(player);
91                     Vec3d[] playerPos = {null};
92                     int playerPosX = ((int) player.getX()) >> 4;
93                     int playerPosZ = ((int) player.getZ()) >> 4;
94                     if (ticks % 20 == 0) {
95                         for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
96                             for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
97                                 ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
98                                 if (!CHUNK_MAP.containsKey(chunkPos))
99                                     queueChunk(chunkPos);
100                             }
101                         }
102                     }
103                     if (!POS.isEmpty()) {
104                         if (playerPos[0] == null) {
105                             playerPos[0] = player.getPos();
106                         }
107                         ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.toBlockPos(8, 0, 8).getSquaredDistance(playerPos[0].x, 0, playerPos[0].z, false))).get();
108                         EXECUTOR.submit(() -> {
109                             if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
110                                 calculateChunk(world.getChunkManager().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, entityContext);
111                             } else {
112                                 CHUNK_MAP.remove(pos);
113                             }
114                         });
115                         POS.remove(pos);
116                     }
117                     Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
118                     while (chunkMapIterator.hasNext()) {
119                         Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
120                         if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
121                             chunkMapIterator.remove();
122                         }
123                     }
124                 }
125             } catch (Exception e) {
126                 LogManager.getLogger().throwing(e);
127             }
128         });
129     }
130     
131     public static void queueChunkAndNear(ChunkPos pos) {
132         for (int xOffset = -1; xOffset <= 1; xOffset++) {
133             for (int zOffset = -1; zOffset <= 1; zOffset++) {
134                 queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
135             }
136         }
137     }
138     
139     public static void queueChunk(ChunkPos pos) {
140         if (!POS.contains(pos))
141             POS.add(0, pos);
142     }
143     
144     public static int getChunkRange() {
145         return Math.max(MathHelper.ceil(reach / 16f), 1);
146     }
147     
148     private static void calculateChunk(WorldChunk chunk, World world, ChunkPos chunkPos, EntityContext entityContext) {
149         Map<Long, Object> map = Maps.newHashMap();
150         if (chunk != null) {
151             ChunkLightingView block = chunk.getLightingProvider().get(LightType.BLOCK);
152             ChunkLightingView sky = showNumber ? null : chunk.getLightingProvider().get(LightType.SKY);
153             for (BlockPos pos : BlockPos.iterate(chunkPos.getStartX(), 0, chunkPos.getStartZ(), chunkPos.getEndX(), 256, chunkPos.getEndZ())) {
154                 BlockPos down = pos.down();
155                 if (showNumber) {
156                     int level = LightOverlay.getCrossLevel(pos, down, chunk, block, entityContext);
157                     if (level >= 0) {
158                         map.put(pos.asLong(), level);
159                     }
160                 } else {
161                     Biome biome = world.getBiomeAccess().getBiome(pos);
162                     if (biome.getMaxSpawnLimit() > 0 && !biome.getEntitySpawnList(EntityCategory.MONSTER).isEmpty()) {
163                         CrossType type = LightOverlay.getCrossType(pos, down, chunk, block, sky, entityContext);
164                         if (type != CrossType.NONE) {
165                             map.put(pos.asLong(), type);
166                         }
167                     }
168                 }
169             }
170         }
171         CHUNK_MAP.put(chunkPos, map);
172     }
173     
174     public static CrossType getCrossType(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView block, ChunkLightingView sky, EntityContext entityContext) {
175         BlockState blockBelowState = world.getBlockState(down);
176         BlockState blockUpperState = world.getBlockState(pos);
177         VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
178         if (!underwater && !blockUpperState.getFluidState().isEmpty())
179             return CrossType.NONE;
180         // Check if the outline is full
181         if (Block.isFaceFullSquare(upperCollisionShape, Direction.UP))
182             return CrossType.NONE;
183         // TODO: Not to hard code no redstone
184         if (blockUpperState.emitsRedstonePower())
185             return CrossType.NONE;
186         // Check if the collision has a bump
187         if (upperCollisionShape.getMaximum(Direction.Axis.Y) > 0)
188             return CrossType.NONE;
189         if (blockUpperState.getBlock().matches(BlockTags.RAILS))
190             return CrossType.NONE;
191         // Check block state allow spawning (excludes bedrock and barriers automatically)
192         if (!blockBelowState.allowsSpawning(world, down, testingEntityType))
193             return CrossType.NONE;
194         if (block.getLightLevel(pos) > crossLevel)
195             return CrossType.NONE;
196         if (sky.getLightLevel(pos) > crossLevel)
197             return CrossType.YELLOW;
198         return CrossType.RED;
199     }
200     
201     public static int getCrossLevel(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView view, EntityContext entityContext) {
202         BlockState blockBelowState = world.getBlockState(down);
203         BlockState blockUpperState = world.getBlockState(pos);
204         VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, entityContext);
205         VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
206         if (!underwater && !blockUpperState.getFluidState().isEmpty())
207             return -1;
208         if (!blockBelowState.getFluidState().isEmpty())
209             return -1;
210         if (blockBelowState.isAir())
211             return -1;
212         if (Block.isFaceFullSquare(upperCollisionShape, Direction.DOWN))
213             return -1;
214         return view.getLightLevel(pos);
215     }
216     
217     public static void renderCross(Tessellator tessellator, BufferBuilder buffer, Camera camera, World world, BlockPos pos, int color, EntityContext entityContext) {
218         double d0 = camera.getPos().x;
219         double d1 = camera.getPos().y - .005D;
220         VoxelShape upperOutlineShape = world.getBlockState(pos).getOutlineShape(world, pos, entityContext);
221         if (!upperOutlineShape.isEmpty())
222             d1 -= upperOutlineShape.getMaximum(Direction.Axis.Y);
223         double d2 = camera.getPos().z;
224         
225         buffer.begin(1, VertexFormats.POSITION_COLOR);
226         int red = (color >> 16) & 255;
227         int green = (color >> 8) & 255;
228         int blue = color & 255;
229         buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
230         buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
231         buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
232         buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
233         tessellator.draw();
234     }
235     
236     public static void renderLevel(MinecraftClient client, Camera camera, World world, BlockPos pos, BlockPos down, int level, EntityContext entityContext) {
237         String string_1 = String.valueOf(level);
238         TextRenderer textRenderer_1 = client.textRenderer;
239         double double_4 = camera.getPos().x;
240         double double_5 = camera.getPos().y;
241         VoxelShape upperOutlineShape = world.getBlockState(down).getOutlineShape(world, down, entityContext);
242         if (!upperOutlineShape.isEmpty())
243             double_5 += 1 - upperOutlineShape.getMaximum(Direction.Axis.Y);
244         double double_6 = camera.getPos().z;
245         RenderSystem.pushMatrix();
246         RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
247         RenderSystem.rotatef(90, 1, 0, 0);
248         RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
249         float size = 0.07F;
250         RenderSystem.scalef(-size, -size, size);
251         float float_3 = (float) (-textRenderer_1.getStringWidth(string_1)) / 2.0F + 0.4f;
252         RenderSystem.enableAlphaTest();
253         VertexConsumerProvider.Immediate vertexConsumerProvider$Immediate_1 = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
254         textRenderer_1.draw(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, Rotation3.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
255         vertexConsumerProvider$Immediate_1.draw();
256         RenderSystem.popMatrix();
257     }
258     
259     static void loadConfig(File file) {
260         try {
261             redColor = 0xFF0000;
262             yellowColor = 0xFFFF00;
263             if (!file.exists() || !file.canRead())
264                 saveConfig(file);
265             FileInputStream fis = new FileInputStream(file);
266             Properties properties = new Properties();
267             properties.load(fis);
268             fis.close();
269             reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
270             crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
271             showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
272             smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true");
273             underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
274             lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
275             {
276                 int r, g, b;
277                 r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
278                 g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
279                 b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
280                 yellowColor = (r << 16) + (g << 8) + b;
281             }
282             {
283                 int r, g, b;
284                 r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
285                 g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
286                 b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
287                 redColor = (r << 16) + (g << 8) + b;
288             }
289             saveConfig(file);
290         } catch (Exception e) {
291             e.printStackTrace();
292             reach = 12;
293             crossLevel = 7;
294             lineWidth = 1.0F;
295             redColor = 0xFF0000;
296             yellowColor = 0xFFFF00;
297             showNumber = false;
298             smoothLines = true;
299             underwater = false;
300             try {
301                 saveConfig(file);
302             } catch (IOException ex) {
303                 ex.printStackTrace();
304             }
305         }
306         CHUNK_MAP.clear();
307         POS.clear();
308     }
309     
310     static void saveConfig(File file) throws IOException {
311         FileOutputStream fos = new FileOutputStream(file, false);
312         fos.write("# Light Overlay Config".getBytes());
313         fos.write("\n".getBytes());
314         fos.write(("reach=" + reach).getBytes());
315         fos.write("\n".getBytes());
316         fos.write(("crossLevel=" + crossLevel).getBytes());
317         fos.write("\n".getBytes());
318         fos.write(("showNumber=" + showNumber).getBytes());
319         fos.write("\n".getBytes());
320         fos.write(("smoothLines=" + smoothLines).getBytes());
321         fos.write("\n".getBytes());
322         fos.write(("underwater=" + underwater).getBytes());
323         fos.write("\n".getBytes());
324         fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
325         fos.write("\n".getBytes());
326         fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
327         fos.write("\n".getBytes());
328         fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
329         fos.write("\n".getBytes());
330         fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
331         fos.write("\n".getBytes());
332         fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
333         fos.write("\n".getBytes());
334         fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
335         fos.write("\n".getBytes());
336         fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
337         fos.close();
338     }
339     
340     @Override
341     public void onInitializeClient() {
342         // Load Config
343         loadConfig(configFile);
344         
345         // Setup
346         testingEntityType = EntityType.Builder.create(EntityCategory.MONSTER).setDimensions(0f, 0f).disableSaving().build(null);
347         MinecraftClient client = MinecraftClient.getInstance();
348         KeyBindingRegistry.INSTANCE.addCategory(KEYBIND_CATEGORY);
349         KeyBindingRegistry.INSTANCE.register(enableOverlay = FabricKeyBinding.Builder.create(ENABLE_OVERLAY_KEYBIND, InputUtil.Type.KEYSYM, 296, KEYBIND_CATEGORY).build());
350         KeyBindingRegistry.INSTANCE.register(increaseReach = FabricKeyBinding.Builder.create(INCREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
351         KeyBindingRegistry.INSTANCE.register(decreaseReach = FabricKeyBinding.Builder.create(DECREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
352         KeyBindingRegistry.INSTANCE.register(increaseLineWidth = FabricKeyBinding.Builder.create(INCREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
353         KeyBindingRegistry.INSTANCE.register(decreaseLineWidth = FabricKeyBinding.Builder.create(DECREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
354         ClothClientHooks.HANDLE_INPUT.register(minecraftClient -> {
355             while (enableOverlay.wasPressed())
356                 enabled = !enabled;
357             while (increaseReach.wasPressed()) {
358                 if (reach < 64)
359                     reach++;
360                 try {
361                     saveConfig(configFile);
362                 } catch (IOException e) {
363                     e.printStackTrace();
364                 }
365                 client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
366             }
367             while (decreaseReach.wasPressed()) {
368                 if (reach > 1)
369                     reach--;
370                 try {
371                     saveConfig(configFile);
372                 } catch (IOException e) {
373                     e.printStackTrace();
374                 }
375                 client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
376             }
377             while (increaseLineWidth.wasPressed()) {
378                 if (lineWidth < 7)
379                     lineWidth += 0.1f;
380                 try {
381                     saveConfig(configFile);
382                 } catch (IOException e) {
383                     e.printStackTrace();
384                 }
385                 client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
386             }
387             while (decreaseLineWidth.wasPressed()) {
388                 if (lineWidth > 1)
389                     lineWidth -= 0.1F;
390                 try {
391                     saveConfig(configFile);
392                 } catch (IOException e) {
393                     e.printStackTrace();
394                 }
395                 client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
396             }
397         });
398         ClothClientHooks.DEBUG_RENDER_PRE.register(() -> {
399             if (LightOverlay.enabled) {
400                 PlayerEntity playerEntity = client.player;
401                 int playerPosX = ((int) playerEntity.getX()) >> 4;
402                 int playerPosZ = ((int) playerEntity.getZ()) >> 4;
403                 EntityContext entityContext = EntityContext.of(playerEntity);
404                 World world = client.world;
405                 BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
406                 Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
407                 if (showNumber) {
408                     RenderSystem.enableTexture();
409                     RenderSystem.depthMask(true);
410                     BlockPos.Mutable mutable = new BlockPos.Mutable();
411                     for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
412                         if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
413                             continue;
414                         }
415                         for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
416                             if (objectEntry.getValue() instanceof Integer) {
417                                 mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
418                                 if (mutable.isWithinDistance(playerPos, reach)) {
419                                     BlockPos down = mutable.down();
420                                     LightOverlay.renderLevel(client, camera, world, mutable, down, (Integer) objectEntry.getValue(), entityContext);
421                                 }
422                             }
423                         }
424                     }
425                     RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
426                     RenderSystem.enableDepthTest();
427                 } else {
428                     RenderSystem.enableDepthTest();
429                     RenderSystem.disableTexture();
430                     RenderSystem.enableBlend();
431                     RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
432                     RenderSystem.disableLighting();
433                     if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
434                     RenderSystem.lineWidth(lineWidth);
435                     Tessellator tessellator = Tessellator.getInstance();
436                     BufferBuilder buffer = tessellator.getBuffer();
437                     BlockPos.Mutable mutable = new BlockPos.Mutable();
438                     for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
439                         if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
440                             continue;
441                         }
442                         for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
443                             if (objectEntry.getValue() instanceof CrossType) {
444                                 mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
445                                 if (mutable.isWithinDistance(playerPos, reach)) {
446                                     BlockPos down = mutable.down();
447                                     int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
448                                     LightOverlay.renderCross(tessellator, buffer, camera, world, mutable, color, entityContext);
449                                 }
450                             }
451                         }
452                     }
453                     RenderSystem.disableBlend();
454                     RenderSystem.enableTexture();
455                     if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
456                 }
457             }
458         });
459     }
460     
461     private enum CrossType {
462         YELLOW,
463         RED,
464         NONE
465     }
466 }