1 package me.shedaniel.lightoverlay;
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 net.minecraft.block.Block;
8 import net.minecraft.block.BlockState;
9 import net.minecraft.client.Minecraft;
10 import net.minecraft.client.entity.player.ClientPlayerEntity;
11 import net.minecraft.client.gui.FontRenderer;
12 import net.minecraft.client.renderer.*;
13 import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
14 import net.minecraft.client.settings.KeyBinding;
15 import net.minecraft.client.util.InputMappings;
16 import net.minecraft.client.world.ClientWorld;
17 import net.minecraft.entity.Entity;
18 import net.minecraft.entity.EntityClassification;
19 import net.minecraft.entity.EntityType;
20 import net.minecraft.network.IPacket;
21 import net.minecraft.network.play.server.SChangeBlockPacket;
22 import net.minecraft.network.play.server.SChunkDataPacket;
23 import net.minecraft.network.play.server.SMultiBlockChangePacket;
24 import net.minecraft.tags.BlockTags;
25 import net.minecraft.util.Direction;
26 import net.minecraft.util.ResourceLocation;
27 import net.minecraft.util.math.BlockPos;
28 import net.minecraft.util.math.ChunkPos;
29 import net.minecraft.util.math.MathHelper;
30 import net.minecraft.util.math.Vec3d;
31 import net.minecraft.util.math.shapes.ISelectionContext;
32 import net.minecraft.util.math.shapes.VoxelShape;
33 import net.minecraft.util.text.TranslationTextComponent;
34 import net.minecraft.world.IBlockReader;
35 import net.minecraft.world.LightType;
36 import net.minecraft.world.World;
37 import net.minecraft.world.biome.Biome;
38 import net.minecraft.world.chunk.Chunk;
39 import net.minecraft.world.chunk.ChunkStatus;
40 import net.minecraft.world.lighting.IWorldLightListener;
41 import net.minecraftforge.api.distmarker.Dist;
42 import net.minecraftforge.client.event.InputEvent;
43 import net.minecraftforge.client.event.RenderWorldLastEvent;
44 import net.minecraftforge.client.settings.KeyConflictContext;
45 import net.minecraftforge.client.settings.KeyModifier;
46 import net.minecraftforge.common.MinecraftForge;
47 import net.minecraftforge.event.TickEvent;
48 import net.minecraftforge.eventbus.api.SubscribeEvent;
49 import net.minecraftforge.fml.DistExecutor;
50 import net.minecraftforge.fml.client.registry.ClientRegistry;
51 import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
52 import org.apache.logging.log4j.LogManager;
53 import org.lwjgl.opengl.GL11;
56 import java.io.FileInputStream;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.text.DecimalFormat;
61 import java.util.concurrent.ExecutorService;
62 import java.util.concurrent.Executors;
64 public class LightOverlayClient {
66 static final DecimalFormat FORMAT = new DecimalFormat("#.#");
67 private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
68 private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
69 private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
70 private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
71 private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
72 private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
73 static int reach = 12;
74 static int crossLevel = 7;
75 static boolean showNumber = false;
76 static EntityType<Entity> testingEntityType;
77 static float lineWidth = 1.0F;
78 static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
79 static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
80 private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
81 private static boolean enabled = false;
82 private static int threadNumber = 0;
83 private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
84 Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
85 thread.setDaemon(true);
88 private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
89 private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
90 private static long ticks = 0;
92 public static void register() {
94 loadConfig(configFile);
97 testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
98 enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
99 increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
100 decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
101 increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
102 decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
103 MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
106 //noinspection Convert2MethodRef
107 DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
108 } catch (Exception e) {
113 public static CrossType getCrossType(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener block, IWorldLightListener sky, ISelectionContext selectionContext) {
114 BlockState blockBelowState = reader.getBlockState(down);
115 BlockState blockUpperState = reader.getBlockState(pos);
116 VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, selectionContext);
117 if (!blockUpperState.getFluidState().isEmpty())
118 return CrossType.NONE;
119 /* WorldEntitySpawner.func_222266_a */
120 // Check if the outline is full
121 if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
122 return CrossType.NONE;
123 // Check if there is power
124 if (blockUpperState.canProvidePower())
125 return CrossType.NONE;
126 // Check if the collision has a bump
127 if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
128 return CrossType.NONE;
129 if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
130 return CrossType.NONE;
131 // Check block state allow spawning (excludes bedrock and barriers automatically)
132 if (!blockBelowState.canEntitySpawn(reader, down, testingEntityType))
133 return CrossType.NONE;
134 if (block.getLightFor(pos) > crossLevel)
135 return CrossType.NONE;
136 if (sky.getLightFor(pos) > crossLevel)
137 return CrossType.YELLOW;
138 return CrossType.RED;
141 public static int getCrossLevel(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener light, ISelectionContext context) {
142 BlockState blockBelowState = reader.getBlockState(down);
143 BlockState blockUpperState = reader.getBlockState(pos);
144 VoxelShape collisionShape = blockBelowState.getCollisionShape(reader, down, context);
145 VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, context);
146 if (!blockUpperState.getFluidState().isEmpty())
148 if (!blockBelowState.getFluidState().isEmpty())
150 if (blockBelowState.isAir(reader, down))
152 if (!blockUpperState.isAir(reader, pos))
154 return light.getLightFor(pos);
157 public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
158 double d0 = info.getProjectedView().x;
159 double d1 = info.getProjectedView().y - .005D;
160 VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
161 if (!upperOutlineShape.isEmpty())
162 d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
163 double d2 = info.getProjectedView().z;
164 buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
165 int red = (color >> 16) & 255;
166 int green = (color >> 8) & 255;
167 int blue = color & 255;
168 buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
169 buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
170 buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
171 buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
175 public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
176 String string_1 = String.valueOf(level);
177 FontRenderer fontRenderer = minecraft.fontRenderer;
178 double double_4 = info.getProjectedView().x;
179 double double_5 = info.getProjectedView().y;
180 VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
181 if (!upperOutlineShape.isEmpty())
182 double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
183 double double_6 = info.getProjectedView().z;
184 RenderSystem.pushMatrix();
185 RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
186 RenderSystem.rotatef(90, 1, 0, 0);
187 RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
189 RenderSystem.scalef(-size, -size, size);
190 float float_3 = (float) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
191 RenderSystem.enableAlphaTest();
192 IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer());
193 fontRenderer.renderString(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
194 vertexConsumerProvider$Immediate_1.finish();
195 RenderSystem.popMatrix();
198 @SubscribeEvent(receiveCanceled = true)
199 public static void handleInput(InputEvent.KeyInputEvent event) {
200 if (enableOverlay.isPressed())
202 if (increaseReach.isPressed()) {
206 saveConfig(configFile);
207 } catch (IOException e) {
210 Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
212 if (decreaseReach.isPressed()) {
216 saveConfig(configFile);
217 } catch (IOException e) {
220 Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
222 if (increaseLineWidth.isPressed()) {
226 saveConfig(configFile);
227 } catch (IOException e) {
230 Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
232 if (decreaseLineWidth.isPressed()) {
236 saveConfig(configFile);
237 } catch (IOException e) {
240 Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
244 public static void queueChunkAndNear(ChunkPos pos) {
245 for (int xOffset = -1; xOffset <= 1; xOffset++) {
246 for (int zOffset = -1; zOffset <= 1; zOffset++) {
247 queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
252 public static void queueChunk(ChunkPos pos) {
253 if (!POS.contains(pos))
257 public static int getChunkRange() {
258 return Math.max(MathHelper.ceil(reach / 16f), 1);
262 public static void tick(TickEvent.ClientTickEvent event) {
263 if (event.phase == TickEvent.Phase.END) {
265 Minecraft minecraft = Minecraft.getInstance();
267 if (minecraft.player == null || !enabled) {
270 ClientPlayerEntity player = minecraft.player;
271 ClientWorld world = minecraft.world;
272 ISelectionContext selectionContext = ISelectionContext.forEntity(player);
273 Vec3d[] playerPos = {null};
274 int playerPosX = ((int) player.getPosX()) >> 4;
275 int playerPosZ = ((int) player.getPosZ()) >> 4;
276 if (ticks % 20 == 0) {
277 for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
278 for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
279 ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
280 if (!CHUNK_MAP.containsKey(chunkPos))
281 queueChunk(chunkPos);
285 if (!POS.isEmpty()) {
286 if (playerPos[0] == null) {
287 playerPos[0] = player.getPositionVec();
289 ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.getBlock(8, 0, 8).distanceSq(playerPos[0].x, 0, playerPos[0].z, false))).get();
290 EXECUTOR.submit(() -> {
291 if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
292 calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext);
294 CHUNK_MAP.remove(pos);
299 Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
300 while (chunkMapIterator.hasNext()) {
301 Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
302 if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
303 chunkMapIterator.remove();
307 } catch (Exception e) {
308 LogManager.getLogger().throwing(e);
313 private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) {
314 Map<Long, Object> map = Maps.newHashMap();
316 IWorldLightListener block = chunk.getWorldLightManager().getLightEngine(LightType.BLOCK);
317 IWorldLightListener sky = showNumber ? null : chunk.getWorldLightManager().getLightEngine(LightType.SKY);
318 for (BlockPos pos : BlockPos.getAllInBoxMutable(chunkPos.getXStart(), 0, chunkPos.getZStart(), chunkPos.getXEnd(), 256, chunkPos.getZEnd())) {
319 BlockPos down = pos.down();
321 int level = LightOverlayClient.getCrossLevel(pos, down, chunk, block, selectionContext);
323 map.put(pos.toLong(), level);
326 Biome biome = world.getBiomeManager().getBiome(pos);
327 if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
328 CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext);
329 if (type != CrossType.NONE) {
330 map.put(pos.toLong(), type);
336 CHUNK_MAP.put(chunkPos, map);
340 public static void renderWorldLast(RenderWorldLastEvent event) {
341 if (LightOverlayClient.enabled) {
342 RenderSystem.pushMatrix();
343 RenderSystem.loadIdentity();
344 RenderSystem.multMatrix(event.getMatrixStack().getLast().getMatrix());
345 Minecraft client = Minecraft.getInstance();
346 ClientPlayerEntity playerEntity = client.player;
347 int playerPosX = ((int) playerEntity.getPosX()) >> 4;
348 int playerPosZ = ((int) playerEntity.getPosZ()) >> 4;
349 ISelectionContext selectionContext = ISelectionContext.forEntity(playerEntity);
350 World world = client.world;
351 BlockPos playerPos = playerEntity.getPosition();
352 ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
354 RenderSystem.enableTexture();
355 RenderSystem.depthMask(true);
356 BlockPos.Mutable mutable = new BlockPos.Mutable();
357 for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
358 if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
361 for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
362 if (objectEntry.getValue() instanceof Integer) {
363 mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
364 if (mutable.withinDistance(playerPos, reach)) {
365 BlockPos down = mutable.down();
366 LightOverlayClient.renderLevel(client, info, world, mutable, down, (Integer) objectEntry.getValue(), selectionContext);
371 RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
372 RenderSystem.enableDepthTest();
374 RenderSystem.enableDepthTest();
375 RenderSystem.disableTexture();
376 RenderSystem.enableBlend();
377 RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
378 RenderSystem.disableLighting();
379 GL11.glEnable(GL11.GL_LINE_SMOOTH);
380 RenderSystem.lineWidth(lineWidth);
381 Tessellator tessellator = Tessellator.getInstance();
382 BufferBuilder buffer = tessellator.getBuffer();
383 BlockPos.Mutable mutable = new BlockPos.Mutable();
384 for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
385 if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
388 for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
389 if (objectEntry.getValue() instanceof CrossType) {
390 mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
391 if (mutable.withinDistance(playerPos, reach)) {
392 BlockPos down = mutable.down();
393 int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
394 LightOverlayClient.renderCross(info, tessellator, buffer, world, mutable, color, selectionContext);
399 RenderSystem.disableBlend();
400 RenderSystem.enableTexture();
401 GL11.glDisable(GL11.GL_LINE_SMOOTH);
403 RenderSystem.popMatrix();
407 private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
408 KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
409 ClientRegistry.registerKeyBinding(keyBinding);
413 static void loadConfig(File file) {
416 yellowColor = 0xFFFF00;
417 if (!file.exists() || !file.canRead())
419 FileInputStream fis = new FileInputStream(file);
420 Properties properties = new Properties();
421 properties.load(fis);
423 reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
424 crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
425 showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
426 lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
429 r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
430 g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
431 b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
432 yellowColor = (r << 16) + (g << 8) + b;
436 r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
437 g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
438 b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
439 redColor = (r << 16) + (g << 8) + b;
442 } catch (Exception e) {
447 yellowColor = 0xFFFF00;
450 } catch (IOException ex) {
451 ex.printStackTrace();
456 static void saveConfig(File file) throws IOException {
457 FileOutputStream fos = new FileOutputStream(file, false);
458 fos.write("# Light Overlay Config".getBytes());
459 fos.write("\n".getBytes());
460 fos.write(("reach=" + reach).getBytes());
461 fos.write("\n".getBytes());
462 fos.write(("crossLevel=" + crossLevel).getBytes());
463 fos.write("\n".getBytes());
464 fos.write(("showNumber=" + showNumber).getBytes());
465 fos.write("\n".getBytes());
466 fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
467 fos.write("\n".getBytes());
468 fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
469 fos.write("\n".getBytes());
470 fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
471 fos.write("\n".getBytes());
472 fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
473 fos.write("\n".getBytes());
474 fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
475 fos.write("\n".getBytes());
476 fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
477 fos.write("\n".getBytes());
478 fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
482 public static void processPacket(IPacket<?> packet) {
483 if (packet instanceof SChangeBlockPacket) {
484 LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos()));
485 } else if (packet instanceof SChunkDataPacket) {
486 LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ()));
487 } else if (packet instanceof SMultiBlockChangePacket) {
488 ChunkPos chunkPos = ObfuscationReflectionHelper.getPrivateValue(SMultiBlockChangePacket.class, (SMultiBlockChangePacket) packet, "field_148925_b");
489 LightOverlayClient.queueChunkAndNear(new ChunkPos(chunkPos.x, chunkPos.z));
493 private enum CrossType {