]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/commitdiff
Re-add NBT loading
authorIrtimaled <irtimaled@gmail.com>
Thu, 8 Aug 2019 04:58:51 +0000 (21:58 -0700)
committerIrtimaled <irtimaled@gmail.com>
Sun, 11 Aug 2019 06:39:03 +0000 (23:39 -0700)
README.md
src/main/java/com/irtimaled/bbor/client/ClientProxy.java
src/main/java/com/irtimaled/bbor/client/NBTFileParser.java [new file with mode: 0644]
src/main/java/com/irtimaled/bbor/client/SetWorldData.java [new file with mode: 0644]
src/main/java/com/irtimaled/bbor/client/events/ConnectedToRemoteServer.java [new file with mode: 0644]
src/main/java/com/irtimaled/bbor/client/interop/ClientInterop.java
src/main/java/com/irtimaled/bbor/mixin/client/network/MixinNetHandlerLoginClient.java

index 2701d47f9bad19b35877a85f0f45b7ebb2b39fb4..4f07bf701bf7eaf72a8695e9b7a344e356f828f2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -108,10 +108,23 @@ Another option to configure is to open the config/BBOutlineReloaded.cfg file wit
 
 ## Bounding boxes when connected to servers
 
-There are a couple of options when you want bounding boxes to show whilst accessing servers:-
+There are a few options when you want bounding boxes to show whilst accessing servers:-
 
 - Use a modded server - Ensure the server is running with this mod loaded (as described above), and connect to the server with a client with this mod loaded. 
 - Keep cache - With the "Keep Cache Between Sessions" config setting enabled, Open a copy of the world in single player and move around to capture all the structures you want in the cache. Once you are happy with the structures you have cached, quit the single player game and connect to the server without closing Minecraft. You will see all the structures from the cache.
+- Load dat files(*) - Copy the dat files listed below into config/BBOutlineReloaded/{host}/{port} and these will be loaded when you connect to the vanilla server. {host} is the name or ip you use to connect to the server; {port} is the port you specify when connecting. The mod will load any/all of following files:-
+  - level.dat; include this for world spawn, spawn and slime chunks to be rendered.
+  - Fortress.dat; include this for Nether Fortresses to be rendered.
+  - EndCities.dat; include this for EndCities to be rendered.
+  - Mineshaft.dat; include this for Mineshafts to be rendered.
+  - Monument.dat; include this for Ocean Monuments to be rendered.
+  - Stronghold.dat; include this for Strongholds to be rendered.
+  - Mansion.dat; include this for Mansions to be rendered.
+  - Temple.dat; include this for Desert & Jungle Temples and witch huts to be rendered.
+
+(*) This is becoming more and more irrelevant as dat files are being phased out, and this feature may be removed/replaced in the future.
+
+It is also possible to include the villages.dat, villages_end.dat & villages_nether.dat files and it will render villages however these files only contain the villages that were loaded when the files were copied and will not handle any changes that occur with villages such as when doors are added/removed or villager population changes.
 
 ## Links
 - Forge - [Download](https://files.minecraftforge.net/)
index 6b80f8c5a22fbf6bcf4314ac967da0fdb9d6a4fe..b79212362fdbffffd96c54b27374a30cd89e1995 100644 (file)
@@ -10,6 +10,8 @@ import com.irtimaled.bbor.common.VillageColorCache;
 import com.irtimaled.bbor.config.ConfigManager;
 import com.irtimaled.bbor.config.Setting;
 
+import java.net.InetSocketAddress;
+
 public class ClientProxy extends CommonProxy {
     public static final String Name = "Bounding Box Outline Reloaded";
     public static boolean active;
@@ -41,6 +43,7 @@ public class ClientProxy extends CommonProxy {
     public void init() {
         super.init();
         EventBus.subscribe(Render.class, this::render);
+        EventBus.subscribe(ConnectedToRemoteServer.class, this::connectedToServer);
         EventBus.subscribe(DisconnectedFromRemoteServer.class, e -> disconnectedFromServer());
         EventBus.subscribe(InitializeClientReceived.class, this::onInitializeClientReceived);
         EventBus.subscribe(AddBoundingBoxReceived.class, this::addBoundingBox);
@@ -57,6 +60,16 @@ public class ClientProxy extends CommonProxy {
         renderer.render(event.getDimensionId(), ConfigManager.outerBoxesOnly.get());
     }
 
+    private void connectedToServer(ConnectedToRemoteServer event) {
+        InetSocketAddress internetAddress = event.getInternetAddress();
+        NBTFileParser.loadLocalDatFiles(internetAddress.getHostName(), internetAddress.getPort(), this::setWorldData, this::getOrCreateCache);
+    }
+
+    private void setWorldData(long seed, int spawnX, int spawnZ) {
+        setWorldSpawn(spawnX, spawnZ);
+        setSeed(seed);
+    }
+
     private void disconnectedFromServer() {
         active = false;
         if (ConfigManager.keepCacheBetweenSessions.get()) return;
diff --git a/src/main/java/com/irtimaled/bbor/client/NBTFileParser.java b/src/main/java/com/irtimaled/bbor/client/NBTFileParser.java
new file mode 100644 (file)
index 0000000..d994e49
--- /dev/null
@@ -0,0 +1,165 @@
+package com.irtimaled.bbor.client;
+
+import com.irtimaled.bbor.Logger;
+import com.irtimaled.bbor.common.BoundingBoxCache;
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.Dimensions;
+import com.irtimaled.bbor.common.models.AbstractBoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBoxStructure;
+import com.irtimaled.bbor.common.models.BoundingBoxVillage;
+import com.irtimaled.bbor.common.models.Coords;
+import com.irtimaled.bbor.config.ConfigManager;
+import net.minecraft.nbt.CompressedStreamTools;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+class NBTFileParser {
+    static void loadLocalDatFiles(String host, int port, SetWorldData setWorldData, GetCache createCache) {
+        Logger.info("Looking for local structures (host:port=%s:%d)", host, port);
+        String path = String.format("BBOutlineReloaded%s%s%s%d", File.separator, host, File.separator, port);
+        File localStructuresFolder = new File(ConfigManager.configDir, path);
+        Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        if (!localStructuresFolder.exists()) {
+            path = String.format("BBOutlineReloaded%s%s", File.separator, host);
+            localStructuresFolder = new File(ConfigManager.configDir, path);
+            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        }
+        if (!localStructuresFolder.exists()) {
+            path = String.format("BBOutlineReloaded%s%s,%d", File.separator, host, port);
+            localStructuresFolder = new File(ConfigManager.configDir, path);
+            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        }
+        if (!localStructuresFolder.exists()) {
+            Logger.info("No local structures folders found");
+            return;
+        }
+        loadWorldData(localStructuresFolder, setWorldData);
+        populateBoundingBoxCache(localStructuresFolder, createCache);
+    }
+
+    private static void loadWorldData(File localStructuresFolder, SetWorldData setWorldData) {
+        File file = new File(localStructuresFolder, "level.dat");
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound data = nbt.getCompoundTag("Data");
+        long seed = data.getLong("RandomSeed");
+        int spawnX = data.getInteger("SpawnX");
+        int spawnZ = data.getInteger("SpawnZ");
+        Logger.info("Loaded level.dat (seed: %d, spawn: %d,%d)", seed, spawnX, spawnZ);
+        setWorldData.accept(seed, spawnX, spawnZ);
+    }
+
+    private static void populateBoundingBoxCache(File localStructuresFolder, GetCache createCache) {
+        loadOverworldStructures(localStructuresFolder, createCache.apply(Dimensions.OVERWORLD));
+        loadNetherStructures(localStructuresFolder, createCache.apply(Dimensions.NETHER));
+        loadEndStructures(localStructuresFolder, createCache.apply(Dimensions.THE_END));
+    }
+
+    private static void loadOverworldStructures(File localStructuresFolder, BoundingBoxCache cache) {
+        loadStructure(localStructuresFolder, cache, "Temple.dat", "TeDP", BoundingBoxType.DesertTemple);
+        loadStructure(localStructuresFolder, cache, "Temple.dat", "TeJP", BoundingBoxType.JungleTemple);
+        loadStructure(localStructuresFolder, cache, "Temple.dat", "TeSH", BoundingBoxType.WitchHut);
+        loadStructure(localStructuresFolder, cache, "Monument.dat", "*", BoundingBoxType.OceanMonument);
+        loadStructure(localStructuresFolder, cache, "Stronghold.dat", "*", BoundingBoxType.Stronghold);
+        loadStructure(localStructuresFolder, cache, "Mansion.dat", "*", BoundingBoxType.Mansion);
+        loadStructure(localStructuresFolder, cache, "Mineshaft.dat", "*", BoundingBoxType.MineShaft);
+        loadVillages(localStructuresFolder, cache, "Villages.dat");
+    }
+
+    private static void loadNetherStructures(File localStructuresFolder, BoundingBoxCache cache) {
+        loadStructure(localStructuresFolder, cache, "Fortress.dat", "*", BoundingBoxType.NetherFortress);
+        loadVillages(localStructuresFolder, cache, "villages_nether.dat");
+    }
+
+    private static void loadEndStructures(File localStructuresFolder, BoundingBoxCache cache) {
+        loadVillages(localStructuresFolder, cache, "Villages_end.dat");
+        loadStructure(localStructuresFolder, cache, "EndCity.dat", "*", BoundingBoxType.EndCity);
+    }
+
+    private static void loadStructure(File localStructuresFolder, BoundingBoxCache cache, String fileName, String id, BoundingBoxType type) {
+        File file = new File(localStructuresFolder, fileName);
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound features = nbt.getCompoundTag("data")
+                .getCompoundTag("Features");
+        int loadedStructureCount = 0;
+        for (Object key : features.getKeySet()) {
+            NBTTagCompound feature = features.getCompoundTag((String) key);
+            AbstractBoundingBox structure = buildStructure(feature, type);
+            Set<AbstractBoundingBox> boundingBoxes = new HashSet<>();
+            NBTTagCompound[] children = getChildCompoundTags(feature, "Children");
+            for (NBTTagCompound child : children) {
+                if (id.equals(child.getString("id")) || id.equals("*"))
+                    boundingBoxes.add(buildStructure(child, type));
+            }
+            if (boundingBoxes.size() > 0)
+                ++loadedStructureCount;
+            cache.addBoundingBoxes(structure, boundingBoxes);
+        }
+
+        Logger.info("Loaded %s (%d structures with type %s)", fileName, loadedStructureCount, id);
+    }
+
+    private static BoundingBoxStructure buildStructure(NBTTagCompound feature, BoundingBoxType type) {
+        int[] bb = feature.getIntArray("BB");
+        Coords minCoords = new Coords(bb[0], bb[1], bb[2]);
+        Coords maxCoords = new Coords(bb[3], bb[4], bb[5]);
+        return BoundingBoxStructure.from(minCoords, maxCoords, type);
+    }
+
+    private static void loadVillages(File localStructuresFolder, BoundingBoxCache cache, String fileName) {
+        File file = new File(localStructuresFolder, fileName);
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound[] villages = getChildCompoundTags(nbt.getCompoundTag("data"), "Villages");
+        for (NBTTagCompound village : villages) {
+            Coords center = new Coords(village.getInteger("CX"), village.getInteger("CY"), village.getInteger("CZ"));
+            int radius = village.getInteger("Radius");
+            int population = village.getInteger("PopSize");
+            Set<Coords> doors = getDoors(village);
+            AbstractBoundingBox boundingBox = BoundingBoxVillage.from(center, radius, village.hashCode(), population, doors);
+            cache.addBoundingBox(boundingBox);
+        }
+
+        Logger.info("Loaded %s (%d villages)", fileName, villages.length);
+    }
+
+    private static Set<Coords> getDoors(NBTTagCompound village) {
+        Set<Coords> doors = new HashSet<>();
+        for (NBTTagCompound door : getChildCompoundTags(village, "Doors")) {
+            doors.add(new Coords(door.getInteger("X"), door.getInteger("Y"), door.getInteger("Z")));
+        }
+        return doors;
+    }
+
+    private static NBTTagCompound loadNbtFile(File file) {
+        if (!file.exists())
+            return null;
+        try {
+            return CompressedStreamTools.readCompressed(new FileInputStream(file));
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private static NBTTagCompound[] getChildCompoundTags(NBTTagCompound parent, String key) {
+        NBTTagList tagList = parent.getTagList(key, 10);
+        NBTTagCompound[] result = new NBTTagCompound[tagList.tagCount()];
+        for (int index = 0; index < tagList.tagCount(); index++) {
+            result[index] = tagList.getCompoundTagAt(index);
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/com/irtimaled/bbor/client/SetWorldData.java b/src/main/java/com/irtimaled/bbor/client/SetWorldData.java
new file mode 100644 (file)
index 0000000..c4d39b1
--- /dev/null
@@ -0,0 +1,6 @@
+package com.irtimaled.bbor.client;
+
+@FunctionalInterface
+interface SetWorldData {
+    void accept(Long seed, Integer spawnX, Integer spawnZ);
+}
diff --git a/src/main/java/com/irtimaled/bbor/client/events/ConnectedToRemoteServer.java b/src/main/java/com/irtimaled/bbor/client/events/ConnectedToRemoteServer.java
new file mode 100644 (file)
index 0000000..e40991c
--- /dev/null
@@ -0,0 +1,15 @@
+package com.irtimaled.bbor.client.events;
+
+import java.net.InetSocketAddress;
+
+public class ConnectedToRemoteServer {
+    private final InetSocketAddress internetAddress;
+
+    public ConnectedToRemoteServer(InetSocketAddress internetAddress) {
+        this.internetAddress = internetAddress;
+    }
+
+    public InetSocketAddress getInternetAddress() {
+        return internetAddress;
+    }
+}
index 5fc0f973a5e165a4a8177266928c690c7fa0f05a..f23fe6e2d68ce7c6a6d6353534d875c664f7fb58 100644 (file)
@@ -1,15 +1,22 @@
 package com.irtimaled.bbor.client.interop;
 
 import com.irtimaled.bbor.client.PlayerCoords;
-import com.irtimaled.bbor.client.events.DisconnectedFromRemoteServer;
-import com.irtimaled.bbor.client.events.Render;
-import com.irtimaled.bbor.client.events.SeedCommandTyped;
-import com.irtimaled.bbor.client.events.UpdateWorldSpawnReceived;
+import com.irtimaled.bbor.client.events.*;
 import com.irtimaled.bbor.common.EventBus;
+import com.irtimaled.bbor.common.TypeHelper;
 import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.network.NetworkManager;
 import net.minecraft.util.math.BlockPos;
 
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
 public class ClientInterop {
+    public static void connectedToRemoteServer(NetworkManager networkManager) {
+        SocketAddress remoteAddress = networkManager.getRemoteAddress();
+        TypeHelper.doIfType(remoteAddress, InetSocketAddress.class, inetSocketAddress -> EventBus.publish(new ConnectedToRemoteServer(inetSocketAddress)));
+    }
+
     public static void disconnectedFromRemoteServer() {
         EventBus.publish(new DisconnectedFromRemoteServer());
     }
index aa21b8328609a8d105339c44aa830517e2af0410..e4d462bdcee95870d03331861e47ad0eb9e590b4 100644 (file)
@@ -2,13 +2,25 @@ package com.irtimaled.bbor.mixin.client.network;
 
 import com.irtimaled.bbor.client.interop.ClientInterop;
 import net.minecraft.client.network.NetHandlerLoginClient;
+import net.minecraft.network.NetworkManager;
+import org.spongepowered.asm.mixin.Final;
 import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
 @Mixin(NetHandlerLoginClient.class)
 public abstract class MixinNetHandlerLoginClient {
+    @Shadow
+    @Final
+    private NetworkManager networkManager;
+
+    @Inject(method = "handleLoginSuccess", at = @At(value = "RETURN"))
+    private void handleLoginSuccess(CallbackInfo ci) {
+        ClientInterop.connectedToRemoteServer(networkManager);
+    }
+
     @Inject(method = "onDisconnect", at = @At("HEAD"))
     private void onDisconnect(CallbackInfo ci) {
         ClientInterop.disconnectedFromRemoteServer();