aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder')
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoom.java381
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoomInfoRegistry.java163
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/RoomMatcher.java145
3 files changed, 689 insertions, 0 deletions
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoom.java b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoom.java
new file mode 100755
index 00000000..5834d67e
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoom.java
@@ -0,0 +1,381 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.dungeon.roomfinder;
+
+import com.google.common.collect.Sets;
+import kr.syeyoung.dungeonsguide.mod.dungeon.DungeonContext;
+import kr.syeyoung.dungeonsguide.mod.dungeon.MapProcessor;
+import kr.syeyoung.dungeonsguide.dungeon.data.DungeonRoomInfo;
+import kr.syeyoung.dungeonsguide.mod.dungeon.doorfinder.DungeonDoor;
+import kr.syeyoung.dungeonsguide.mod.dungeon.doorfinder.EDungeonDoorType;
+import kr.syeyoung.dungeonsguide.mod.dungeon.events.impl.DungeonStateChangeEvent;
+import kr.syeyoung.dungeonsguide.dungeon.mechanics.dunegonmechanic.DungeonMechanic;
+import kr.syeyoung.dungeonsguide.dungeon.mechanics.DungeonRoomDoor;
+import kr.syeyoung.dungeonsguide.mod.dungeon.pathfinding.*;
+import kr.syeyoung.dungeonsguide.mod.dungeon.roomedit.EditingContext;
+import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.ProcessorFactory;
+import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.RoomProcessor;
+import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.RoomProcessorGenerator;
+import kr.syeyoung.dungeonsguide.mod.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.mod.features.impl.secret.FeaturePathfindStrategy;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.block.Block;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.pathfinding.PathEntity;
+import net.minecraft.pathfinding.PathFinder;
+import net.minecraft.pathfinding.PathPoint;
+import net.minecraft.util.*;
+import net.minecraft.world.ChunkCache;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+
+import javax.vecmath.Vector2d;
+import java.awt.*;
+import java.util.List;
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@Getter
+public class DungeonRoom {
+ private final List<Point> unitPoints;
+ private final short shape;
+ private final byte color;
+
+ private final BlockPos min;
+ private final BlockPos max;
+ private final Point minRoomPt;
+
+ private final DungeonContext context;
+
+ private final List<DungeonDoor> doors = new ArrayList<>();
+
+ private DungeonRoomInfo dungeonRoomInfo;
+
+ private final int unitWidth; // X
+ private final int unitHeight; // Z
+
+ @Setter
+ private int totalSecrets = -1;
+ private RoomState currentState = RoomState.DISCOVERED;
+
+ private Map<String, DungeonMechanic> cached = null;
+
+ @Getter
+ private final World cachedWorld;
+ public Map<String, DungeonMechanic> getMechanics() {
+ if (cached == null || EditingContext.getEditingContext() != null) {
+ cached = new HashMap<>(dungeonRoomInfo.getMechanics());
+ int index = 0;
+ for (DungeonDoor door : doors) {
+ if (door.getType().isExist()) cached.put((door.getType().getName())+"-"+(++index), new DungeonRoomDoor(this, door));
+ }
+ }
+ return cached;
+ }
+
+ public void setCurrentState(RoomState currentState) {
+ context.createEvent(new DungeonStateChangeEvent(unitPoints.get(0), dungeonRoomInfo.getName(), this.currentState, currentState));
+ this.currentState = currentState;
+ }
+
+ private final Map<BlockPos, AStarFineGrid> activeBetterAStar = new HashMap<>();
+ private final Map<BlockPos, AStarCornerCut> activeBetterAStarCornerCut = new HashMap<>();
+ private final Map<BlockPos, ThetaStar> activeThetaStar = new HashMap<>();
+
+ public ScheduledFuture<List<Vec3>> createEntityPathTo(IBlockAccess blockaccess, Entity entityIn, BlockPos targetPos, float dist, int timeout) {
+ FeaturePathfindStrategy.PathfindStrategy pathfindStrategy = FeatureRegistry.SECRET_PATHFIND_STRATEGY.getPathfindStrat();
+ if (pathfindStrategy == FeaturePathfindStrategy.PathfindStrategy.JPS_LEGACY) {
+ return asyncPathFinder.schedule(() -> {
+ BlockPos min = new BlockPos(getMin().getX(), 0, getMin().getZ());
+ BlockPos max= new BlockPos(getMax().getX(), 255, getMax().getZ());
+ JPSPathfinder pathFinder = new JPSPathfinder(this);
+ pathFinder.pathfind(entityIn.getPositionVector(), new Vec3(targetPos).addVector(0.5, 0.5, 0.5), 1.5f,timeout);
+ return pathFinder.getRoute();
+ }, 0, TimeUnit.MILLISECONDS);
+ } else if (pathfindStrategy == FeaturePathfindStrategy.PathfindStrategy.A_STAR_FINE_GRID) {
+ return asyncPathFinder.schedule(() -> {
+ AStarFineGrid pathFinder =
+ activeBetterAStar.computeIfAbsent(targetPos, (pos) -> new AStarFineGrid(this, new Vec3(pos.getX(), pos.getY(), pos.getZ()).addVector(0.5, 0.5, 0.5)));
+ pathFinder.pathfind(entityIn.getPositionVector(),timeout);
+ return pathFinder.getRoute();
+ }, 0, TimeUnit.MILLISECONDS);
+ }else if (pathfindStrategy == FeaturePathfindStrategy.PathfindStrategy.A_STAR_DIAGONAL) {
+ return asyncPathFinder.schedule(() -> {
+ AStarCornerCut pathFinder =
+ activeBetterAStarCornerCut.computeIfAbsent(targetPos, (pos) -> new AStarCornerCut(this, new Vec3(pos.getX(), pos.getY(), pos.getZ()).addVector(0.5, 0.5, 0.5)));
+ pathFinder.pathfind(entityIn.getPositionVector(),timeout);
+ return pathFinder.getRoute();
+ }, 0, TimeUnit.MILLISECONDS);
+ } else if (pathfindStrategy == FeaturePathfindStrategy.PathfindStrategy.THETA_STAR) {
+ return asyncPathFinder.schedule(() -> {
+ ThetaStar pathFinder =
+ activeThetaStar.computeIfAbsent(targetPos, (pos) -> new ThetaStar(this, new Vec3(pos.getX(), pos.getY(), pos.getZ()).addVector(0.5, 0.5, 0.5)));
+ pathFinder.pathfind(entityIn.getPositionVector(),timeout);
+ return pathFinder.getRoute();
+ }, 0, TimeUnit.MILLISECONDS);
+ } else {
+ return asyncPathFinder.schedule(() -> {
+ PathFinder pathFinder = new PathFinder(nodeProcessorDungeonRoom);
+ PathEntity latest = pathFinder.createEntityPathTo(blockaccess, entityIn, targetPos, dist);
+ if (latest != null) {
+ List<Vec3> poses = new ArrayList<>();
+ for (int i = 0; i < latest.getCurrentPathLength(); i++) {
+ PathPoint pathPoint = latest.getPathPointFromIndex(i);
+ poses.add(new Vec3(getMin().add(pathPoint.xCoord, pathPoint.yCoord, pathPoint.zCoord)).addVector(0.5,0.5,0.5));
+ }
+ return poses;
+ }
+ return new ArrayList<>();
+ }, 0, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private static final ScheduledExecutorService asyncPathFinder = Executors.newScheduledThreadPool(4);
+ @Getter
+ private final NodeProcessorDungeonRoom nodeProcessorDungeonRoom;
+
+ @Getter
+ private final Map<String, Object> roomContext = new HashMap<>();
+
+ @AllArgsConstructor
+ @Getter
+ public enum RoomState {
+ DISCOVERED(0), COMPLETE_WITHOUT_SECRETS(0), FINISHED(0), FAILED(-14);
+ private final int scoreModifier;
+ }
+
+ private RoomProcessor roomProcessor;
+
+ public DungeonRoom(List<Point> points, short shape, byte color, BlockPos min, BlockPos max, DungeonContext context, Set<Tuple<Vector2d, EDungeonDoorType>> doorsAndStates) {
+ this.unitPoints = points;
+ this.shape = shape;
+ this.color = color;
+ this.min = min;
+ this.max = max;
+ this.context = context;
+
+ minRoomPt = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ for (Point pt : unitPoints) {
+ if (pt.x < minRoomPt.x) minRoomPt.x = pt.x;
+ if (pt.y < minRoomPt.y) minRoomPt.y = pt.y;
+ }
+ unitWidth = (int) Math.ceil(max.getX() - min.getX() / 32.0);
+ unitHeight = (int) Math.ceil(max.getZ() - min.getZ() / 32.0);
+
+
+ ChunkCache chunkCache = new ChunkCache(getContext().getWorld(), min.add(-3, 0, -3), max.add(3,0,3), 0);
+ this.cachedWorld = new CachedWorld(chunkCache);
+
+
+
+ minx = min.getX() * 2; miny = 0; minz = min.getZ() * 2;
+ maxx = max.getX() * 2 + 2; maxy = 255 * 2 + 2; maxz = max.getZ() * 2 + 2;
+
+ lenx = maxx - minx;
+ leny = maxy - miny;
+ lenz = maxz - minz;
+ arr = new long[lenx *leny * lenz * 2 / 8];;
+
+ buildDoors(doorsAndStates);
+ buildRoom();
+ nodeProcessorDungeonRoom = new NodeProcessorDungeonRoom(this);
+ updateRoomProcessor();
+
+
+ }
+
+ private static final Set<Vector2d> directions = Sets.newHashSet(new Vector2d(0,16), new Vector2d(0, -16), new Vector2d(16, 0), new Vector2d(-16 , 0));
+
+ private void buildDoors(Set<Tuple<Vector2d, EDungeonDoorType>> doorsAndStates) {
+ Set<Tuple<BlockPos, EDungeonDoorType>> positions = new HashSet<>();
+ BlockPos pos = context.getMapProcessor().roomPointToWorldPoint(minRoomPt).add(16,0,16);
+ for (Tuple<Vector2d, EDungeonDoorType> doorsAndState : doorsAndStates) {
+ Vector2d vector2d = doorsAndState.getFirst();
+ BlockPos neu = pos.add(vector2d.x * 32, 0, vector2d.y * 32);
+ positions.add(new Tuple<>(neu, doorsAndState.getSecond()));
+ }
+
+ for (Tuple<BlockPos, EDungeonDoorType> door : positions) {
+ doors.add(new DungeonDoor(context.getWorld(), door.getFirst(), door.getSecond()));
+ }
+ }
+
+ private RoomMatcher roomMatcher = null;
+ private void buildRoom() {
+ if (roomMatcher == null)
+ roomMatcher = new RoomMatcher(this);
+ DungeonRoomInfo dungeonRoomInfo = roomMatcher.match();
+ if (dungeonRoomInfo == null) {
+ dungeonRoomInfo = roomMatcher.createNew();
+ if (color == 18) dungeonRoomInfo.setProcessorId("bossroom");
+ }
+ this.dungeonRoomInfo = dungeonRoomInfo;
+ totalSecrets = dungeonRoomInfo.getTotalSecrets();
+ }
+
+ public void updateRoomProcessor() {
+ RoomProcessorGenerator roomProcessorGenerator = ProcessorFactory.getRoomProcessorGenerator(dungeonRoomInfo.getProcessorId());
+ if (roomProcessorGenerator == null) this.roomProcessor = null;
+ else this.roomProcessor = roomProcessorGenerator.createNew(this);
+ }
+
+ public Block getAbsoluteBlockAt(int x, int y, int z) {
+ // validate x y z's
+ BlockPos pos = new BlockPos(x,y,z);
+ if (canAccessAbsolute(pos)) {
+ return cachedWorld.getBlockState(pos).getBlock();
+ }
+ return null;
+ }
+
+ public Block getRelativeBlockAt(int x, int y, int z) {
+ // validate x y z's
+ if (canAccessRelative(x,z)) {
+ BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ());
+ return cachedWorld.getBlockState(pos).getBlock();
+ }
+ return null;
+ }
+
+ public BlockPos getRelativeBlockPosAt(int x, int y, int z) {
+ BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ());
+ return pos;
+ }
+
+ public int getRelativeBlockDataAt(int x, int y, int z) {
+ // validate x y z's
+ if (canAccessRelative(x,z)) {
+ BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ());
+ IBlockState iBlockState = cachedWorld.getBlockState(pos);
+ return iBlockState.getBlock().getMetaFromState(iBlockState);
+ }
+ return -1;
+ }
+
+ public int getAbsoluteBlockDataAt(int x, int y, int z) {
+ // validate x y z's
+ BlockPos pos = new BlockPos(x,y,z);
+ if (canAccessAbsolute(pos)) {
+ IBlockState iBlockState = cachedWorld.getBlockState(pos);
+ return iBlockState.getBlock().getMetaFromState(iBlockState);
+ }
+ return -1;
+ }
+
+ public boolean canAccessAbsolute(BlockPos pos) {
+ MapProcessor mapProcessor = this.context.getMapProcessor();
+ Point roomPt = mapProcessor.worldPointToRoomPoint(pos);
+ roomPt.translate(-minRoomPt.x, -minRoomPt.y);
+
+ return (shape >>(roomPt.y *4 +roomPt.x) & 0x1) > 0;
+ }
+ public boolean canAccessRelative(int x, int z) {
+ return x>= 0 && z >= 0 && (shape >>((z/32) *4 +(x/32)) & 0x1) > 0;
+ }
+
+
+
+ long[] arr;
+ // These values are doubled
+ private final int minx;
+ private final int miny;
+ private final int minz;
+ private final int maxx;
+ private final int maxy;
+ private final int maxz;
+ private final int lenx, leny, lenz;
+ private static final float playerWidth = 0.3f;
+ public boolean isBlocked(int x,int y, int z) {
+ if (x < minx || z < minz || x >= maxx || z >= maxz || y < miny || y >= maxy) return true;
+ int dx = x - minx, dy = y - miny, dz = z - minz;
+ int bitIdx = dx * leny * lenz + dy * lenz + dz;
+ int location = bitIdx / 4;
+ int bitStart = (2 * (bitIdx % 4));
+ long theBit = arr[location];
+ if (((theBit >> bitStart) & 0x2) > 0) return ((theBit >> bitStart) & 1) > 0;
+ float wX = x / 2.0f, wY = y / 2.0f, wZ = z / 2.0f;
+
+
+ AxisAlignedBB bb = AxisAlignedBB.fromBounds(wX - playerWidth, wY, wZ - playerWidth, wX + playerWidth, wY + 1.9f, wZ + playerWidth);
+
+ int i = MathHelper.floor_double(bb.minX);
+ int j = MathHelper.floor_double(bb.maxX + 1.0D);
+ int k = MathHelper.floor_double(bb.minY);
+ int l = MathHelper.floor_double(bb.maxY + 1.0D);
+ int i1 = MathHelper.floor_double(bb.minZ);
+ int j1 = MathHelper.floor_double(bb.maxZ + 1.0D);
+ BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
+
+ List<AxisAlignedBB> list = new ArrayList<>();
+ for (int k1 = i; k1 < j; ++k1) {
+ for (int l1 = i1; l1 < j1; ++l1) {
+ for (int i2 = k - 1; i2 < l; ++i2) {
+ blockPos.set(k1, i2, l1);
+ IBlockState iblockstate1 = cachedWorld.getBlockState(blockPos);
+ Block b = iblockstate1.getBlock();
+ if (!b.getMaterial().blocksMovement())continue;
+ if (b.isFullCube() && i2 == k-1) continue;
+ if (iblockstate1.equals( NodeProcessorDungeonRoom.preBuilt)) continue;
+ if (b.isFullCube()) {
+ theBit |= (3L << bitStart);
+ arr[location] = theBit;
+ return true;
+ }
+ try {
+ b.addCollisionBoxesToList(cachedWorld, blockPos, iblockstate1, bb, list, null);
+ } catch (Exception e) {
+ return true;
+ }
+ if (list.size() > 0) {
+ theBit |= (3L << bitStart);
+ arr[location] = theBit;
+ return true;
+ }
+ }
+ }
+ }
+ theBit |= 2L << bitStart;
+ arr[location] = theBit;
+ return false;
+ }
+
+
+ public void resetBlock(BlockPos pos) {
+ for (int x = -1; x <= 1; x++) {
+ for (int y = -1; y <= 1; y++) {
+ for (int z = -1; z <= 1; z++) {
+ resetBlock(pos.getX()*2 + x, pos.getY()*2 + y, pos.getZ()*2 + z);
+ }
+ }
+ }
+ }
+ private void resetBlock(int x, int y, int z) {
+ if (x < minx || z < minz || x >= maxx || z >= maxz || y < miny || y >= maxy) return;
+ int dx = x - minx, dy = y - miny, dz = z - minz;
+ int bitIdx = dx * leny * lenz + dy * lenz + dz;
+ int location = bitIdx / 4;
+ arr[location] = 0;
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoomInfoRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoomInfoRegistry.java
new file mode 100755
index 00000000..72000d38
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/DungeonRoomInfoRegistry.java
@@ -0,0 +1,163 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.dungeon.roomfinder;
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import kr.syeyoung.dungeonsguide.Main;
+import kr.syeyoung.dungeonsguide.dungeon.data.DungeonRoomInfo;
+import lombok.Getter;
+import net.minecraft.client.Minecraft;
+import org.apache.commons.io.IOUtils;
+import org.jetbrains.annotations.NotNull;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+public class DungeonRoomInfoRegistry {
+ @Getter
+ private static final List<DungeonRoomInfo> registered = new ArrayList<DungeonRoomInfo>();
+ private static final Map<Short, List<DungeonRoomInfo>> shapeMap = new HashMap<Short, List<DungeonRoomInfo>>();
+ private static final Map<UUID, DungeonRoomInfo> uuidMap = new HashMap<UUID, DungeonRoomInfo>();
+
+ static Gson gson = new Gson();
+
+ public static void register(@NotNull DungeonRoomInfo dungeonRoomInfo) {
+
+// System.out.println("Loading room: " + dungeonRoomInfo.getUuid());
+//
+// File file = new File(Main.getConfigDir() + "/" + "rooms" + "/" + dungeonRoomInfo.getUuid() + ".json");
+// if(!file.exists()){
+// try {
+// FileUtils.writeStringToFile(file, gson.toJson(dungeonRoomInfo));
+// } catch (IOException e) {
+// throw new RuntimeException(e);
+// }
+// }
+
+
+
+ if (uuidMap.containsKey(dungeonRoomInfo.getUuid())) {
+ DungeonRoomInfo dri1 = uuidMap.get(dungeonRoomInfo.getUuid());
+ registered.remove(dri1);
+ shapeMap.get(dri1.getShape()).remove(dri1);
+ uuidMap.remove(dri1.getUuid());
+ }
+ dungeonRoomInfo.setRegistered(true);
+ registered.add(dungeonRoomInfo);
+ uuidMap.put(dungeonRoomInfo.getUuid(), dungeonRoomInfo);
+ List<DungeonRoomInfo> roomInfos = shapeMap.get(dungeonRoomInfo.getShape());
+ if (roomInfos == null) {
+ roomInfos = new ArrayList<>();
+ }
+ roomInfos.add(dungeonRoomInfo);
+ shapeMap.put(dungeonRoomInfo.getShape(), roomInfos);
+ }
+
+
+ public static List<DungeonRoomInfo> getByShape(Short shape) {
+ List<DungeonRoomInfo> dungeonRoomInfos = shapeMap.get(shape);
+ return dungeonRoomInfos == null ? Collections.emptyList() : dungeonRoomInfos;
+ }
+
+ public static DungeonRoomInfo getByUUID(UUID uid) {
+ return uuidMap.get(uid);
+ }
+
+ public static void unregister(DungeonRoomInfo dungeonRoomInfo) {
+ if (!dungeonRoomInfo.isRegistered()) throw new IllegalStateException("what tha fak? that is not registered one");
+ if (!uuidMap.containsKey(dungeonRoomInfo.getUuid())) throw new IllegalStateException("what tha fak? that is not registered one, but you desperately wanted to trick this program");
+ dungeonRoomInfo.setRegistered(false);
+ registered.remove(dungeonRoomInfo);
+ shapeMap.get(dungeonRoomInfo.getShape()).remove(dungeonRoomInfo);
+ uuidMap.remove(dungeonRoomInfo.getUuid());
+ }
+
+ public static void saveAll(File dir) {
+ dir.mkdirs();
+ boolean isDev = Minecraft.getMinecraft().getSession().getPlayerID().replace("-","").equals("e686fe0aab804a71ac7011dc8c2b534c");
+ StringBuilder nameidstring = new StringBuilder("name,uuid,processsor,secrets");
+ StringBuilder ids = new StringBuilder();
+ for (DungeonRoomInfo dungeonRoomInfo : registered) {
+ try {
+ if (!dungeonRoomInfo.isUserMade() && !isDev) continue;
+ FileOutputStream fos = new FileOutputStream(new File(dir, dungeonRoomInfo.getUuid().toString() + ".roomdata"));
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(dungeonRoomInfo);
+ oos.flush();
+ oos.close();
+
+ nameidstring.append("\n").append(dungeonRoomInfo.getName()).append(",").append(dungeonRoomInfo.getUuid()).append(",").append(dungeonRoomInfo.getProcessorId()).append(",").append(dungeonRoomInfo.getTotalSecrets());
+ ids.append("roomdata/").append(dungeonRoomInfo.getUuid()).append(".roomdata\n");
+ } catch (Exception e) {e.printStackTrace();}
+ }
+
+ try {
+ Files.write(nameidstring.toString(), new File(dir, "roomidmapping.csv"), Charset.defaultCharset());
+ Files.write(ids.toString(), new File(dir, "datas.txt"), Charset.defaultCharset());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void loadAll(File dir) throws BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException {
+ registered.clear();
+ shapeMap.clear();
+ uuidMap.clear();
+ try {
+ List<String> lines = IOUtils.readLines(Main.class.getResourceAsStream("/roomdata/datas.txt"));
+ for (String name : lines) {
+ if (!name.endsWith(".roomdata")) continue;
+ try {
+ InputStream fis = Main.class.getResourceAsStream("/"+name);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ DungeonRoomInfo dri = (DungeonRoomInfo) ois.readObject();
+ ois.close();
+ fis.close();
+ register(dri);
+ } catch (Exception e) {
+ System.out.println(name);
+ e.printStackTrace();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ for (File f : dir.listFiles()) {
+ if (!f.getName().endsWith(".roomdata")) continue;
+ try {
+ InputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ DungeonRoomInfo dri = (DungeonRoomInfo) ois.readObject();
+ ois.close();
+ fis.close();
+ register(dri);
+ } catch (Exception e) {
+ System.out.println(f.getName());e.printStackTrace();}
+ }
+ }
+
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/RoomMatcher.java b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/RoomMatcher.java
new file mode 100755
index 00000000..994a19d8
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomfinder/RoomMatcher.java
@@ -0,0 +1,145 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.dungeon.roomfinder;
+
+import kr.syeyoung.dungeonsguide.mod.chat.ChatTransmitter;
+import kr.syeyoung.dungeonsguide.dungeon.data.DungeonRoomInfo;
+import kr.syeyoung.dungeonsguide.mod.utils.ArrayUtils;
+import kr.syeyoung.dungeonsguide.mod.utils.ShortUtils;
+import lombok.Getter;
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.util.ChatComponentText;
+
+import java.util.List;
+
+public class RoomMatcher {
+ private final DungeonRoom dungeonRoom;
+ @Getter
+ private DungeonRoomInfo match;
+ @Getter
+ private int rotation; // how much the "found room" has to rotate clockwise to match the given dungeon room info. !
+ private boolean triedMatch = false;
+
+
+ public RoomMatcher(DungeonRoom dungeonRoom) {
+ this.dungeonRoom = dungeonRoom;
+ }
+
+ public DungeonRoomInfo match() {
+ if (triedMatch) return match;
+
+ int zz = dungeonRoom.getMax().getZ() - dungeonRoom.getMin().getZ() + 1;
+ int xx = dungeonRoom.getMax().getX() - dungeonRoom.getMin().getX() + 1;
+ for (int z = 0; z < zz; z ++) {
+ for (int x = 0; x < xx; x++) {
+ if (x % 8 == 0 && z % 8 == 0 && dungeonRoom.getContext().getWorld().getChunkFromBlockCoords(dungeonRoom.getRelativeBlockPosAt(x, 0, z)).isEmpty()) {
+ ChatTransmitter.sendDebugChat(new ChatComponentText("Chunk Not loaded in Room Matcher"));
+ }
+ }
+ }
+
+ triedMatch = true;
+ int lowestcost = 10;
+ int lowestRot = 0;
+ DungeonRoomInfo bestMatch = null;
+ for (int rotation = 0; rotation < 4; rotation++) {
+ short shape = dungeonRoom.getShape();
+ for (int j = 0; j<rotation; j++)
+ shape = ShortUtils.rotateClockwise(shape);
+ shape = ShortUtils.topLeftifyInt(shape);
+
+ List<DungeonRoomInfo> roomInfoList = DungeonRoomInfoRegistry.getByShape(shape);
+ for (DungeonRoomInfo roomInfo : roomInfoList) {
+ int cost = tryMatching(roomInfo, rotation);
+ if (cost == 0) {
+ match = roomInfo;
+ this.rotation = rotation;
+ return match;
+ }
+ if (cost < lowestcost) {
+ lowestcost = cost;
+ bestMatch = roomInfo;
+ lowestRot = rotation;
+ }
+ }
+ }
+ match = bestMatch;
+ this.rotation = lowestRot;
+ return bestMatch;
+ }
+
+ private int tryMatching(DungeonRoomInfo dungeonRoomInfo, int rotation) {
+ if (dungeonRoomInfo.getColor() != dungeonRoom.getColor()) return Integer.MAX_VALUE;
+
+ int[][] res = dungeonRoomInfo.getBlocks();
+ for (int i = 0; i < rotation; i++)
+ res = ArrayUtils.rotateCounterClockwise(res);
+
+ int wrongs = 0;
+ for (int z = 0; z < res.length; z ++) {
+ for (int x = 0; x < res[0].length; x++) {
+ int data = res[z][x];
+ if (data == -1) continue;
+ Block b = dungeonRoom.getRelativeBlockAt(x,0,z);
+
+ if (b == null || Block.getIdFromBlock(b) != data) {
+ wrongs++;
+
+ if (wrongs > 10) return wrongs;
+ }
+ }
+ }
+ return wrongs;
+ }
+
+ private static final int offset = 3;
+ public DungeonRoomInfo createNew() {
+ DungeonRoomInfo roomInfo = new DungeonRoomInfo(dungeonRoom.getShape(), dungeonRoom.getColor());
+
+ int maxX = dungeonRoom.getMax().getX();
+ int maxZ = dungeonRoom.getMax().getZ();
+ int minX = dungeonRoom.getMin().getX();
+ int minZ = dungeonRoom.getMin().getZ();
+ int[][] data = new int[dungeonRoom.getMax().getZ() - dungeonRoom.getMin().getZ() +2][dungeonRoom.getMax().getX() - dungeonRoom.getMin().getX() + 2];
+
+ for (int z = 0; z < data.length; z++) {
+ for (int x = 0; x < data[0].length; x++) {
+ if (!(dungeonRoom.canAccessRelative(x + offset, z + offset)
+ && dungeonRoom.canAccessRelative(x - offset -1 , z - offset-1)
+ && dungeonRoom.canAccessRelative(x + offset , z - offset-1)
+ && dungeonRoom.canAccessRelative(x - offset -1 , z + offset))) {
+ data[z][x] = -1;
+ continue;
+ }
+
+ Block b = dungeonRoom.getRelativeBlockAt(x,0,z);
+ if (b == null || b == Blocks.chest || b == Blocks.trapped_chest) {
+ data[z][x] = -1;
+ } else {
+ data[z][x] = Block.getIdFromBlock(b);
+ }
+ }
+ }
+
+ roomInfo.setBlocks(data);
+ roomInfo.setUserMade(true);
+ return roomInfo;
+ }
+}