diff --git a/Client/libs/TuddyLIB.jar b/Client/libs/TuddyLIB.jar index 7a99aaf..0585856 100644 Binary files a/Client/libs/TuddyLIB.jar and b/Client/libs/TuddyLIB.jar differ diff --git a/Client/src/main/java/com/baseband/client/gui/lib/component/Component.java b/Client/src/main/java/com/baseband/client/gui/lib/component/Component.java index df52ecf..2e5066a 100644 --- a/Client/src/main/java/com/baseband/client/gui/lib/component/Component.java +++ b/Client/src/main/java/com/baseband/client/gui/lib/component/Component.java @@ -25,7 +25,7 @@ public abstract class Component { @Marker(Integer.MIN_VALUE) static final boolean FALSE = false; - public static final int MARKER_PREFIX = -512; + private static final int MARKER_PREFIX = -512; public static final int M_GUI_EXPANDED = MARKER_PREFIX + 1; public Point loc; diff --git a/Client/src/main/java/com/baseband/client/module/Feature.java b/Client/src/main/java/com/baseband/client/module/Feature.java index 37525ff..91e6a50 100644 --- a/Client/src/main/java/com/baseband/client/module/Feature.java +++ b/Client/src/main/java/com/baseband/client/module/Feature.java @@ -28,6 +28,9 @@ import java.util.HashMap; //Double fuck you if this involves commands too then it's a feature not a module public abstract class Feature extends ToggleButton implements SetCommand { + private static final int MARKER_PREFIX = -1024; + public static final int M_ENABLED = MARKER_PREFIX + 1; + protected Setup Setup; protected BaseBand bb; @@ -35,7 +38,7 @@ public abstract class Feature extends ToggleButton implements SetCommand { public Category category; - @Marker(1) + @Marker(M_ENABLED) public boolean enabled = defaultEnable(); @Config("Toggle") @@ -176,7 +179,7 @@ public abstract class Feature extends ToggleButton implements SetCommand { } handle = settings; - BaseBand.registerUpdater(settings.linkWith(this, FieldFinder.findMarked(Feature.class, 1)).name("Enabled")).populate(); + BaseBand.registerUpdater(settings.linkWith(this, FieldFinder.findMarked(Feature.class, M_ENABLED)).name("Enabled")).populate(); Description description = getClass().getDeclaredAnnotation(Description.class); if(description != null) diff --git a/Client/src/main/java/com/baseband/client/module/render/HUD.java b/Client/src/main/java/com/baseband/client/module/render/HUD.java index 870cc65..ee51145 100644 --- a/Client/src/main/java/com/baseband/client/module/render/HUD.java +++ b/Client/src/main/java/com/baseband/client/module/render/HUD.java @@ -40,7 +40,7 @@ public class HUD extends Feature { private final long start = new Date().getTime(); public Notification(String text) { - this(text, 5000); + this(text, 10000); } public Notification(String text, int ms) { @@ -181,7 +181,7 @@ public class HUD extends Feature { int localYSize = ySize; String text = TextSplitter.breakText(notif.text, xSize - textOffset * 2); - localYSize -= TextSplitter.getStringHeight(text) - mc.fontRenderer.FONT_HEIGHT; + localYSize += TextSplitter.getStringHeight(text) - mc.fontRenderer.FONT_HEIGHT; int textboxYSize = localYSize; localYSize = notif.opacity(localYSize, 1); diff --git a/Client/src/main/java/com/baseband/client/module/world/SeedOverlay.java b/Client/src/main/java/com/baseband/client/module/world/SeedOverlay.java index ae052d2..2282db4 100644 --- a/Client/src/main/java/com/baseband/client/module/world/SeedOverlay.java +++ b/Client/src/main/java/com/baseband/client/module/world/SeedOverlay.java @@ -2,6 +2,7 @@ package com.baseband.client.module.world; import com.baseband.client.BaseBand; import com.baseband.client.configuration.annotation.Config; +import com.baseband.client.configuration.annotation.Gate; import com.baseband.client.configuration.annotation.Range; import com.baseband.client.module.Feature; import com.baseband.client.module.category.World; @@ -9,12 +10,20 @@ import com.baseband.client.module.render.ClickGUI; import com.baseband.client.util.adapt.SimpleWorldGenerator; import com.baseband.client.util.misc.Description; import com.baseband.client.util.misc.GlobalUtil; +import com.baseband.client.util.misc.Marker; import com.baseband.client.util.misc.Trigger; +import com.baseband.client.util.render.Pixels; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.chunk.Chunk; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.lwjgl.opengl.GL11; +import de.tudbut.tools.Lock; + +import java.util.Objects; import static com.baseband.client.util.render.Tesselator.*; @@ -24,11 +33,24 @@ import static com.baseband.client.util.render.Tesselator.*; public class SeedOverlay extends Feature { @Config("Seed") + @Description("The world seed you want to overlay") public String seed = "enter the server seed"; @Config("Distance (chunks)") + @Description("The distance at which chunks should be checked. Higher is a lot slower. ยง7(This is O(n^2).)") + @Gate(1) @Range("0..8") - public int rd = 8; + public int rd = 4; + + @Config("Opacity") + @Description("The opacity of rendered overlay.") + @Range("0.01..0.8") + public float opacity = 0.2f; + + @Marker(1) + boolean canSetRD = true; + @Marker(2) + boolean running = false; SimpleWorldGenerator genOverworld = null, genNether = null, genEnd = null; @@ -38,56 +60,223 @@ public class SeedOverlay extends Feature { } @Trigger("Generate now") + @Description("Hit this after entering the Seed - this starts the world generators and initializes the render buffers.") + @Gate(1) public void generate() { - BaseBand.ifFeaturePresent(ClickGUI.class, gui -> gui.setEnabled(false)); - long seed; - try { - seed = Long.parseLong(this.seed); - } catch (NumberFormatException e) { - seed = this.seed.hashCode(); - } - BaseBand.notify("Creating world with seed " + seed + "..."); - genOverworld = SimpleWorldGenerator.overworld(mc.world.getWorldInfo(), seed); - genNether = SimpleWorldGenerator.nether(mc.world.getWorldInfo(), seed); - genEnd = SimpleWorldGenerator.end(mc.world.getWorldInfo(), seed); - int rd = this.rd + 1; - int length = rd * 16 * 256 * rd * 16; - BaseBand.notify("World generators acquired. Allocating render buffer with " + (length * 4 / 1024) + "KB space."); - bufferPosition = new BlockPos((mc.player.chunkCoordX - 4) * 16, 0, (mc.player.chunkCoordZ - 4) * 16); - buffer = new int[rd * 16][256][rd * 16]; - GlobalUtil.LOGGER.info("Allocated."); - for (int x = 0; x < rd * 16; x++) { - for (int z = 0; z < rd * 16; z++) { - buffer[x][0][z] = 0x800000ff; + running = true; + canSetRD = false; + new Thread(() -> { + BaseBand.ifFeaturePresent(ClickGUI.class, gui -> gui.setEnabled(false)); + long seed; + try { + seed = Long.parseLong(this.seed); + } catch (NumberFormatException e) { + seed = this.seed.hashCode(); } + BaseBand.notify("Creating world with seed " + seed + "..."); + int rd = this.rd * 2 + 1; + genOverworld = SimpleWorldGenerator.overworld(mc.world.getWorldInfo(), seed, rd); + genNether = SimpleWorldGenerator.nether(mc.world.getWorldInfo(), seed, rd); + genEnd = SimpleWorldGenerator.end(mc.world.getWorldInfo(), seed, rd); + int length = rd * 16 * 256 * rd * 16; + BaseBand.notify("World generators acquired. Allocating render buffer with " + (length * 4 / 1024) + "KB space."); + bufferPosition = new BlockPos((mc.player.chunkCoordX - this.rd) * 16, 0, (mc.player.chunkCoordZ - this.rd) * 16); + frontBuffer = new int[rd * 16][256][rd * 16]; + backBuffer = new int[rd * 16][256][rd * 16]; + GlobalUtil.LOGGER.info("Allocated."); + for (int x = 0; x < rd * 16; x++) { + for (int z = 0; z < rd * 16; z++) { + frontBuffer[x][0][z] = 0x800000ff; + backBuffer[x][0][z] = 0x800000ff; + } + } + GlobalUtil.LOGGER.info("Filled buffer with test state."); + BaseBand.notify("Render buffer created. Starting..."); + setEnabled(true); + }, this + " init").start(); + } + + @Trigger("Stop world") + @Description("Shuts down the generators, frees the render buffers, disables the module. This must be done before changing the Distance.") + @Gate(2) + public void stopWorld() { + new Thread(() -> { + setEnabled(false); + + running = false; + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // these should already be stopped! + if(updater.isAlive() || generator.isAlive()) { + GlobalUtil.LOGGER.warn(this + " threads were still running when stopping the world. This should not happen! Stopping them forcefully."); + //noinspection deprecation + updater.stop(); + //noinspection deprecation + generator.stop(); + } + + canSetRD = true; + + genOverworld.destroy(); + genNether.destroy(); + genEnd.destroy(); + genOverworld = null; + genNether = null; + genEnd = null; + bufferPosition = null; + frontBuffer = null; + backBuffer = null; + }, this + " stopper").start(); + } + + Thread updater, generator; + + @Override + public void onEnable() { + updater = new Thread(this::updateSeedOverlay, this + " updater"); + updater.start(); + generator = new Thread(this::genLoop, this + " generator"); + generator.start(); + } + + private void genLoop() { + Lock lock = new Lock(); + while (enabled) { + lock.lock(100); + int rd = this.rd * 2 + 1; + net.minecraft.world.World mcWorld = mc.world; + EntityPlayerSP player = mc.player; + + if(notIngame()) + break; + + SimpleWorldGenerator gen = getGen(mcWorld); + + int ocx = (player.chunkCoordX - this.rd); + int ocz = (player.chunkCoordZ - this.rd); + + gen.tick(); + + for (int x = -1; x < rd + 1; x++) { + for (int z = -1; z < rd + 1; z++) { + gen.provideChunk(ocx + x, ocz + z); + if(!enabled) + return; + } + } + lock.waitHere(); } - GlobalUtil.LOGGER.info("Filled buffer with test state."); - BaseBand.notify("Render buffer created. Starting..."); - setEnabled(true); + } + + private SimpleWorldGenerator getGen(net.minecraft.world.World world) { + SimpleWorldGenerator gen; + switch (world.provider.getDimensionType()) { + case OVERWORLD: + gen = genOverworld; + break; + case NETHER: + gen = genNether; + break; + case THE_END: + gen = genEnd; + break; + default: + throw new RuntimeException("Dimension " + world.provider.getDimensionType() + " not available for SeedOverlay!"); + } + return gen; } BlockPos bufferPosition = null; - int[][][] buffer = null; + int[][][] backBuffer = null; + int[][][] frontBuffer = null; + + private void updateSeedOverlay() { + while (enabled) { + int rd = this.rd * 2 + 1; + net.minecraft.world.World world = mc.world; + EntityPlayerSP player = mc.player; + + if(notIngame()) + break; + + SimpleWorldGenerator gen = getGen(world); + + int ocx = (player.chunkCoordX - this.rd), ox = ocx * 16; + int ocz = (player.chunkCoordZ - this.rd), oz = ocz * 16; + + Chunk[][] chunks = new Chunk[rd][rd]; + + for (int x = 0; x < chunks.length; x++) { + for (int z = 0; z < chunks[x].length; z++) { + chunks[x][z] = gen.getLoadedChunk(ocx + x, ocz + z); + } + } + + int red = Pixels.mulTransparency(0xffff0000, opacity); + int green = Pixels.mulTransparency(0xff00ff00, opacity); + int yellow = Pixels.mulTransparency(0xffffff00, opacity); + for (int x = 0; x < backBuffer.length; x++) { + for (int z = 0; z < backBuffer[x][0].length; z++) { + if(!enabled) + return; + Chunk c = chunks[x / 16][z / 16]; + Chunk rc = world.getChunk(ocx + x / 16, ocz + z / 16); + if(c == null || !c.isTerrainPopulated()) + continue; + if(rc.isEmpty() || !rc.isTerrainPopulated()) + continue; + for (int y = 0; y < backBuffer[x].length; y++) { + if(c.getBlockState(x, y, z).getBlock() == rc.getBlockState(x, y, z).getBlock()) { + backBuffer[x][y][z] = 0; + continue; + } + if(rc.getBlockState(x, y, z).getBlock() == Blocks.AIR) { + backBuffer[x][y][z] = red; + continue; + } + if(c.getBlockState(x, y, z).getBlock() == Blocks.AIR) { + backBuffer[x][y][z] = green; + continue; + } + backBuffer[x][y][z] = yellow; + } + } + } + swapBuffers(new BlockPos(ox, 0, oz)); + } + } + + private synchronized void swapBuffers(BlockPos bufferPosition) { + this.bufferPosition = bufferPosition; + int[][][] oldFront = this.frontBuffer; + this.frontBuffer = this.backBuffer; + this.backBuffer = oldFront; + } @SubscribeEvent - public void onRender(RenderWorldLastEvent event) { - if(buffer == null) { + public synchronized void onRender(RenderWorldLastEvent event) { + if(frontBuffer == null) { BaseBand.notify("Please input a |Seed| and trigger |Generate now|"); toggle(); return; } - Vec3d p = mc.player.getPositionEyes(event.getPartialTicks()); + Vec3d p = Objects.requireNonNull(mc.getRenderViewEntity()).getPositionEyes(event.getPartialTicks()); ready(); translate(-p.x + bufferPosition.getX(), -p.y + bufferPosition.getY(), -p.z + bufferPosition.getZ()); color(0xffff00ff); // if this renders, this is an error depth(false); begin(GL11.GL_QUADS); - for (int x = 0; x < buffer.length; x++) { - for (int y = 0; y < buffer[x].length; y++) { - for (int z = 0; z < buffer[y].length; z++) { - int c = buffer[x][y][z]; + for (int x = 0; x < frontBuffer.length; x++) { + for (int y = 0; y < frontBuffer[x].length; y++) { + for (int z = 0; z < frontBuffer[x][y].length; z++) { + int c = frontBuffer[x][y][z]; if(c != 0) { - color(c); + changeColor(c); drawBlockFacesNow(x, y, z); } } diff --git a/Client/src/main/java/com/baseband/client/util/adapt/SimpleWorldGenerator.java b/Client/src/main/java/com/baseband/client/util/adapt/SimpleWorldGenerator.java index f053efb..503dcc1 100644 --- a/Client/src/main/java/com/baseband/client/util/adapt/SimpleWorldGenerator.java +++ b/Client/src/main/java/com/baseband/client/util/adapt/SimpleWorldGenerator.java @@ -1,11 +1,12 @@ package com.baseband.client.util.adapt; import com.baseband.client.Setup; -import de.tudbut.obj.Vector2i; +import com.baseband.client.util.misc.GlobalUtil; import net.minecraft.client.Minecraft; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.profiler.Profiler; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.DimensionType; import net.minecraft.world.World; import net.minecraft.world.WorldProvider; @@ -25,74 +26,83 @@ import java.util.Map; public class SimpleWorldGenerator implements IChunkProvider { - final World world; - final HashMap loaded = new HashMap<>(); + public final World world; + final HashMap loaded = new HashMap<>(); final IChunkGenerator generator; final Minecraft mc = Minecraft.getMinecraft(); - public SimpleWorldGenerator(World world, IChunkGenerator generator) { + final int distance; + + private SimpleWorldGenerator(DummyWorld world, IChunkGenerator generator, int distance) { this.world = world; this.generator = generator; + this.distance = distance; + world.setChunkProvider(this); } - public static SimpleWorldGenerator overworld(WorldInfo original, long seed) { + public static SimpleWorldGenerator overworld(WorldInfo original, long seed, int unloadDistance) { NBTTagCompound nbt = original.cloneNBTCompound(null); nbt.setLong("RandomSeed", seed); - World world = new DummyWorld(new WorldInfo(nbt), DimensionType.OVERWORLD); - return new SimpleWorldGenerator(world, new ChunkGeneratorOverworld(world, seed, true, null)); + DummyWorld world = new DummyWorld(new WorldInfo(nbt), DimensionType.OVERWORLD); + return new SimpleWorldGenerator(world, new ChunkGeneratorOverworld(world, seed, true, ""), unloadDistance); } - public static SimpleWorldGenerator nether(WorldInfo original, long seed) { + public static SimpleWorldGenerator nether(WorldInfo original, long seed, int unloadDistance) { NBTTagCompound nbt = original.cloneNBTCompound(null); nbt.setLong("RandomSeed", seed); - World world = new DummyWorld(new WorldInfo(nbt), DimensionType.NETHER); - return new SimpleWorldGenerator(world, new ChunkGeneratorHell(world, true, seed)); + DummyWorld world = new DummyWorld(new WorldInfo(nbt), DimensionType.NETHER); + return new SimpleWorldGenerator(world, new ChunkGeneratorHell(world, true, seed), unloadDistance); } - public static SimpleWorldGenerator end(WorldInfo original, long seed) { + public static SimpleWorldGenerator end(WorldInfo original, long seed, int unloadDistance) { NBTTagCompound nbt = original.cloneNBTCompound(null); nbt.setLong("RandomSeed", seed); - World world = new DummyWorld(new WorldInfo(nbt), DimensionType.THE_END); - return new SimpleWorldGenerator(world, new ChunkGeneratorEnd(world, true, seed, new BlockPos(0,0,0))); + DummyWorld world = new DummyWorld(new WorldInfo(nbt), DimensionType.THE_END); + return new SimpleWorldGenerator(world, new ChunkGeneratorEnd(world, true, seed, new BlockPos(0,0,0)), unloadDistance); } @Nullable @Override public Chunk getLoadedChunk(int x, int z) { - return loaded.get(new Vector2i(x, z)); + return loaded.get(new ChunkPos(x, z)); } @Override @Nonnull public Chunk provideChunk(int x, int z) { Chunk chunk = getLoadedChunk(x, z); - return chunk != null ? chunk : gen(x, z); + if(chunk == null) + return gen(x, z); + return chunk; } - public Chunk gen(int x, int z) { + public synchronized Chunk gen(int x, int z) { + GlobalUtil.LOGGER.debug(this + " is generating chunk at {} {}", x, z); + Chunk chunk = generator.generateChunk(x, z); - loaded.put(new Vector2i(x, z), chunk); + loaded.put(chunk.getPos(), chunk); chunk.onLoad(); chunk.populate(this, generator); chunk.onTick(true); + GlobalUtil.LOGGER.debug("Chunk gen finished"); + return chunk; } @Override - public boolean tick() { + public synchronized boolean tick() { - for (Map.Entry entry : loaded.entrySet()) { - Vector2i pos = entry.getKey(); - if(Math.abs(mc.player.chunkCoordX - pos.getX()) > mc.gameSettings.renderDistanceChunks) { - entry.getValue().onUnload(); - loaded.remove(pos); + for (Map.Entry entry : loaded.entrySet().toArray(new Map.Entry[0])) { + ChunkPos pos = entry.getKey(); + Chunk chunk = entry.getValue(); + if(Math.abs(mc.player.chunkCoordX - pos.x) > distance) { + onUnload(pos, chunk); continue; } - if(Math.abs(mc.player.chunkCoordZ - pos.getY()) > mc.gameSettings.renderDistanceChunks) { - entry.getValue().onUnload(); - loaded.remove(pos); + if(Math.abs(mc.player.chunkCoordZ - pos.z) > distance) { + onUnload(pos, chunk); continue; } } @@ -100,20 +110,39 @@ public class SimpleWorldGenerator implements IChunkProvider { return false; } + private void onUnload(ChunkPos pos, Chunk chunk) { + GlobalUtil.LOGGER.debug(this + " is unloading chunk at {} {}", pos.x, pos.z); + chunk.onUnload(); + loaded.remove(pos); + } + @Override @Nonnull public String makeString() { - return Setup.get().Name + ".SimpleWorldGenerator"; + return Setup.get().Name + "." + this; + } + + @Override + public String toString() { + return "SimpleWorldGenerator"; } @Override public boolean isChunkGeneratedAt(int x, int z) { - return loaded.containsKey(new Vector2i(x, z)); + return loaded.containsKey(new ChunkPos(x, z)); + } + + public synchronized void destroy() { + for (Map.Entry entry : loaded.entrySet().toArray(new Map.Entry[0])) { + onUnload(entry.getKey(), entry.getValue()); + } } private static class DummyWorld extends World { + public DummyWorld(WorldInfo info, DimensionType type) { super(new SaveHandlerMP(), info, new DummyWorldProvider(type), new Profiler(), false); + provider.setWorld(this); } @Override @@ -123,8 +152,12 @@ public class SimpleWorldGenerator implements IChunkProvider { } @Override - protected boolean isChunkLoaded(int x, int y, boolean b) { - throw new IllegalAccessError("Tried to treat dummy world like a real world"); + protected boolean isChunkLoaded(int x, int z, boolean b) { + return chunkProvider.isChunkGeneratedAt(x, z); + } + + public void setChunkProvider(SimpleWorldGenerator chunkProvider) { + this.chunkProvider = chunkProvider; } } diff --git a/Client/src/main/java/com/baseband/client/util/render/Tesselator.java b/Client/src/main/java/com/baseband/client/util/render/Tesselator.java index 582c028..71a2d7b 100644 --- a/Client/src/main/java/com/baseband/client/util/render/Tesselator.java +++ b/Client/src/main/java/com/baseband/client/util/render/Tesselator.java @@ -37,12 +37,18 @@ public class Tesselator { glColor4ub(bytes[1], bytes[2], bytes[3], bytes[0]); color = argb; } + public static void changeColor(int argb) { + byte[] bytes = PBIC.putInt(argb); + glColor4ub(bytes[1], bytes[2], bytes[3], bytes[0]); + } public static void depth(boolean b) { depth = b; - if(b) + if(b) { glEnable(GL_DEPTH_TEST); - else + } else { + glDisable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT); + } } public static void put(double x, double y, double z) { glVertex3d(x,y,z);