# BoundingBoxOutlineReloaded
-BoundingBoxOutlineReloaded is a mod for Minecraft Forge, LiteLoader, and Vanilla
+BoundingBoxOutlineReloaded is a mod for Minecraft Vanilla, Forge, LiteLoader, Rift and Fabric
# Why did I make it?
- Strongholds; yellow boxes show each room in the stronghold. Does anyone make anything with silverfish spawners?
- Woodland Mansions; brown boxes show each room in the woodland mansion. Here's Johnny!
- Mine Shafts; light gray boxes illustrate each of the mine shafts. Cobwebs... grrr!
-- Villages; multicoloured spheres encircle the village, with boxes marking if and where iron golems will spawn. You should see the iron titan... CRAZY!
+- Villages; multicoloured spheres encircle the village, with boxes marking if and where iron golems will spawn. You should see the iron titan... CRAZY!
- Slime chunks; bright green boxes highlight where slimes will spawn, with a dynamic box that rises to where the players feet are to help find them from the surface. Bouncy... bouncy...
- World Spawn & spawn chunks; red boxes outline the world spawn and the spawn chunks (active & lazy).
+- Igloos (*); white boxes show where igloos are. Maybe you can convert the zombie villager back?
+- Shipwrecks (*); cyan boxes are like a lighthouse those wary sailors wish they'd had. Time to find some buried treasure?
+- Ocean ruins (*); cyan boxes show the different ocean ruin structures. Watch out for those pesky drowns tho!
+- Buried treasure (*); cyan boxes highlight where the heart of the sea can be found. Conduit anyone?
+
+(*) Due to how Minecraft generates these structures they will initially float above where they should be, however upon re-logging they should drop down to the correct height. Unfortunately (or fortunately?) buried treasure will always appear at y-90 (unless Mojang change something that is!)
# How it works
As chunks are loaded the game provides metadata about all the different structures & features in those chunks. The mod interprets this meta data, caches the results, and renders the bounding boxes to the screen. In an SMP environment this data is not present on the clients so the mod needs to run on the server where the processing happens and then the relevant metadata is sent to the clients for them to render.
-# Installing (Forge/LiteLoader)
+# Installing (Forge/LiteLoader/Rift/Fabric)
-Make sure you have Forge or LiteLoader installed then drop the mod file into the appropriate mods folder. [Forge Only] If you want to use client/server with your SMP server then make sure the mod is installed on both client and server.
+Make sure you have the relevant mod loader installed then drop the mod file into the appropriate mods folder. [Forge Only] If you want to use client/server with your SMP server then make sure the mod is installed on both client and server.
# Installing (Vanilla)
-Copy the contents of the Vanilla archive into the JAR file as per usual. In a SMP scenario you'll need to use local dat files - see below.
+Double-click the jar file and a profile for the relevant version of Minecraft will be created/updated in the launcher. In a SMP scenario you'll need to use local dat files - see below.
# Configuring
Strongholds | Both | Process/Render Strongholds | drawStrongholds | true/false | false
Woodland Mansions | Both | Process/Render Mansions | drawMansions | true/false | true
Mine Shafts | Both | Process/Render Mine Shafts | drawMineShafts | true/false | false
+Igloos | Client | Process/Render Igloos | drawIgloos | true/false | true
+Shipwrecks | Client | Process/Render Shipwrecks | drawShipwrecks | true/false | true
+Ocean Ruins | Client | Process/Render Ocean Ruins | drawOceanRuins | true/false | true
+Buried Treasure | Client | Process/Render Buried Treasure | drawBuriedTreasure | true/false | true
Villages | Both | Process/Render Villages | drawVillages | true/false | true
Village spheres | Client | Render Villages as spheres instead of cuboids | renderVillageAsSphere | true/false | true
Village Iron Golem Spawn Area | Client | Render Iron Golem Spawn Area within valid Villages | drawIronGolemSpawnArea | true/false | true
Press B, sit back and enjoy the goodness flowing onto your screen.
Press O to switch the "Display Outer Boxes Only" mode on and off, this will allow you to see the full boundary of Nether Fortresses, End Cities, Strongholds and Mineshafts
-# Using with vanilla servers
+# Bounding boxes when connected to vanilla servers
There are two options when you want bounding boxes to show whilst accessing vanilla servers:-
import com.irtimaled.bbor.common.models.WorldData;
import com.irtimaled.bbor.config.ConfigManager;
import net.minecraft.client.Minecraft;
-import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import java.awt.*;
import java.util.HashSet;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.stream.Collectors;
class ClientBoundingBoxProvider {
+ private static final int CHUNK_SIZE = 16;
private final ClientDimensionCache dimensionCache;
ClientBoundingBoxProvider(ClientDimensionCache dimensionCache) {
this.dimensionCache = dimensionCache;
}
- Set<BoundingBox> getBoundingBoxes(DimensionType dimensionType, Boolean outerBoxOnly, WorldClient world) {
+ private boolean isWithinRenderDistance(BlockPos minBlockPos, BlockPos maxBlockPos) {
+ int renderDistanceBlocks = Minecraft.getInstance().gameSettings.renderDistanceChunks * CHUNK_SIZE;
+ int minX = MathHelper.floor(PlayerData.getX() - renderDistanceBlocks);
+ int maxX = MathHelper.floor(PlayerData.getX() + renderDistanceBlocks);
+ int minZ = MathHelper.floor(PlayerData.getZ() - renderDistanceBlocks);
+ int maxZ = MathHelper.floor(PlayerData.getZ() + renderDistanceBlocks);
+
+ return maxBlockPos.getX() > minX &&
+ maxBlockPos.getZ() > minZ &&
+ minBlockPos.getX() < maxX &&
+ minBlockPos.getZ() < maxZ;
+ }
+
+ Set<BoundingBox> getBoundingBoxes(DimensionType dimensionType, Boolean outerBoxOnly) {
Set<BoundingBox> boundingBoxes = getClientBoundingBoxes(dimensionType);
BoundingBoxCache boundingBoxCache = dimensionCache.getBoundingBoxes(dimensionType);
- if (boundingBoxCache != null) {
- if (outerBoxOnly) {
- boundingBoxes.addAll(boundingBoxCache.getBoundingBoxes().keySet());
- } else {
- boundingBoxCache.getBoundingBoxes()
- .values()
- .forEach(boundingBoxes::addAll);
- }
+ if (boundingBoxCache == null)
+ return boundingBoxes;
+
+ for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : boundingBoxCache.getBoundingBoxes().entrySet()) {
+ BoundingBox bb = entry.getKey();
+ if (!isWithinRenderDistance(bb.getMinBlockPos(), bb.getMaxBlockPos())) continue;
+ if (outerBoxOnly)
+ boundingBoxes.add(bb);
+ else
+ boundingBoxes.addAll(entry.getValue());
}
- return boundingBoxes.stream()
- .filter(bb -> world.isAreaLoaded(bb.getMinBlockPos(), bb.getMaxBlockPos()))
- .collect(Collectors.toSet());
+
+ return boundingBoxes;
+ }
+
+ private void addIfWithinRenderDistance(Set<BoundingBox> boundingBoxes, BoundingBox boundingBox)
+ {
+ if(isWithinRenderDistance(boundingBox.getMinBlockPos(), boundingBox.getMaxBlockPos()))
+ boundingBoxes.add(boundingBox);
}
private Set<BoundingBox> getClientBoundingBoxes(DimensionType dimensionType) {
Set<BoundingBox> boundingBoxes = new HashSet<>();
if (worldData != null && dimensionType == DimensionType.OVERWORLD) {
if (ConfigManager.drawWorldSpawn.getBoolean()) {
- boundingBoxes.add(getWorldSpawnBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
- boundingBoxes.add(buildSpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+ addIfWithinRenderDistance(boundingBoxes, getSpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+ addIfWithinRenderDistance(boundingBoxes, getWorldSpawnBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
}
if (ConfigManager.drawLazySpawnChunks.getBoolean()) {
- boundingBoxes.add(getLazySpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+ addIfWithinRenderDistance(boundingBoxes, getLazySpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
}
if (ConfigManager.drawSlimeChunks.getBoolean()) {
boundingBoxes.addAll(this.getSlimeChunks());
}
private Set<BoundingBoxSlimeChunk> getSlimeChunks() {
- Minecraft minecraft = Minecraft.getInstance();
- int renderDistanceChunks = minecraft.gameSettings.renderDistanceChunks;
- int playerChunkX = MathHelper.floor(minecraft.player.posX / 16.0D);
- int playerChunkZ = MathHelper.floor(minecraft.player.posZ / 16.0D);
+ int renderDistanceChunks = Minecraft.getInstance().gameSettings.renderDistanceChunks;
+ int playerChunkX = MathHelper.floor(PlayerData.getX() / CHUNK_SIZE);
+ int playerChunkZ = MathHelper.floor(PlayerData.getZ() / CHUNK_SIZE);
Set<BoundingBoxSlimeChunk> slimeChunks = new HashSet<>();
for (int chunkX = playerChunkX - renderDistanceChunks; chunkX <= playerChunkX + renderDistanceChunks; ++chunkX) {
for (int chunkZ = playerChunkZ - renderDistanceChunks; chunkZ <= playerChunkZ + renderDistanceChunks; ++chunkZ) {
- if (isSlimeChunk(chunkX, chunkZ)) {
- ChunkPos chunk = new ChunkPos(chunkX, chunkZ);
- BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
- BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
- if (minecraft.world.isAreaLoaded(minBlockPos, maxBlockPos)) {
- slimeChunks.add(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos, Color.GREEN));
- }
- }
+ if (!isSlimeChunk(chunkX, chunkZ)) continue;
+ ChunkPos chunk = new ChunkPos(chunkX, chunkZ);
+ BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
+ BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
+ slimeChunks.add(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos, Color.GREEN));
}
}
return slimeChunks;
return r.nextInt(10) == 0;
}
- private BoundingBox buildSpawnChunksBoundingBox(int spawnX, int spawnZ) {
+ private BoundingBox getSpawnChunksBoundingBox(int spawnX, int spawnZ) {
return dimensionCache.getOrSetSpawnChunks(() -> buildSpawnChunksBoundingBox(spawnX, spawnZ, 12));
}
}
private BoundingBox buildSpawnChunksBoundingBox(int spawnX, int spawnZ, int size) {
- double chunkSize = 16;
- double midOffset = chunkSize * (size / 2);
- double midX = Math.round((float) (spawnX / chunkSize)) * chunkSize;
- double midZ = Math.round((float) (spawnZ / chunkSize)) * chunkSize;
+ double midOffset = CHUNK_SIZE * (size / 2.0);
+ double midX = Math.round((float) (spawnX / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
+ double midZ = Math.round((float) (spawnZ / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
BlockPos minBlockPos = new BlockPos(midX - midOffset, 0, midZ - midOffset);
- if (spawnX / chunkSize % 0.5D == 0.0D && spawnZ / chunkSize % 0.5D == 0.0D) {
- midX += chunkSize;
- midZ += chunkSize;
+ if (spawnX / (double) CHUNK_SIZE % 0.5D == 0.0D && spawnZ / (double) CHUNK_SIZE % 0.5D == 0.0D) {
+ midX += (double) CHUNK_SIZE;
+ midZ += (double) CHUNK_SIZE;
}
BlockPos maxBlockPos = new BlockPos(midX + midOffset, 0, midZ + midOffset);
return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, Color.RED);
drawLazySpawnChunks = SetupBooleanProperty(config, "features", "drawLazySpawnChunks", false, "If set to true the lazy spawn chunks bounding boxes will be drawn. (default: false)");
drawEndCities = SetupBooleanProperty(config, "features", "drawEndCities", true, "If set to true end city bounding boxes will be drawn. (default: true)");
drawMansions = SetupBooleanProperty(config, "features", "drawMansions", true, "If set to true woodland mansions will be drawn. (default: true)");
- drawShipwrecks = SetupBooleanProperty(config, "features", "drawShipwrecks", false, "If set to true shipwrecks will be drawn. (default: false)");
- drawOceanRuins = SetupBooleanProperty(config, "features", "drawOceanRuins", false, "If set to true ocean ruins will be drawn. (default: false)");
- drawBuriedTreasure = SetupBooleanProperty(config, "features", "drawBuriedTreasures", false, "If set to true buried treasure will be drawn. (default: false)");
- drawIgloos = SetupBooleanProperty(config, "features", "drawIgloos", false, "If set to true igloos will be drawn. (default: false)");
+ drawIgloos = SetupBooleanProperty(config, "features", "drawIgloos", true, "If set to true igloos will be drawn. (default: true)");
+ drawShipwrecks = SetupBooleanProperty(config, "features", "drawShipwrecks", true, "If set to true shipwrecks will be drawn. (default: true)");
+ drawOceanRuins = SetupBooleanProperty(config, "features", "drawOceanRuins", true, "If set to true ocean ruins will be drawn. (default: true)");
+ drawBuriedTreasure = SetupBooleanProperty(config, "features", "drawBuriedTreasures", true, "If set to true buried treasure will be drawn. (default: true)");
config.save();
}