LightOverlayClient.java 26 KB

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