From e4c46c704ec5449322e40744da73df0ccdce2be5 Mon Sep 17 00:00:00 2001 From: StartForKiller Date: Tue, 18 Apr 2023 03:54:51 +0200 Subject: [PATCH] Fix grid loading and use rtree for getNearbyGroups --- .../grasscutter/config/ConfigContainer.java | 2 +- .../emu/grasscutter/data/server/Grid.java | 60 +++++++------------ .../scripts/SceneScriptManager.java | 6 +- .../java/emu/grasscutter/utils/FileUtils.java | 4 +- .../emu/grasscutter/utils/GridPosition.java | 10 ++++ 5 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java index f1396607..3a62132e 100644 --- a/src/main/java/emu/grasscutter/config/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java @@ -87,7 +87,7 @@ public class ConfigContainer { public String packets = "./packets/"; public String scripts = "resources:Scripts/"; public String plugins = "./plugins/"; - public String cache = "./cache/"; + public String cache = "./server/cache/"; // UNUSED (potentially added later?) // public String dumps = "./dumps/"; diff --git a/src/main/java/emu/grasscutter/data/server/Grid.java b/src/main/java/emu/grasscutter/data/server/Grid.java index d8deb424..37d30196 100644 --- a/src/main/java/emu/grasscutter/data/server/Grid.java +++ b/src/main/java/emu/grasscutter/data/server/Grid.java @@ -1,49 +1,36 @@ package emu.grasscutter.data.server; import java.util.*; +import java.util.Map.Entry; import emu.grasscutter.Grasscutter; +import emu.grasscutter.scripts.SceneIndexManager; import emu.grasscutter.utils.GridPosition; import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import com.github.davidmoten.rtreemulti.RTree; +import com.github.davidmoten.rtreemulti.geometry.Geometry; import lombok.val; public class Grid { public Map> grid; - public Int2ObjectMap>> gridOptimized; + public transient RTree>, Geometry> gridOptimized = null; - private HashSet nearbyGroups = new HashSet<>(100); + private transient HashSet nearbyGroups = new HashSet<>(100); private void Optimize() { if(gridOptimized == null) { - gridOptimized = new Int2ObjectOpenHashMap<>(); + List>> gridValues = new ArrayList<>(); grid.forEach((k, v) -> { - int x = k.getX() & 0xFFFF; - int z = k.getZ() & 0xFFFF; - if(!gridOptimized.containsKey(x)) { - gridOptimized.put(x, new Int2ObjectOpenHashMap<>()); - gridOptimized.get(x).put(z, v); - } else { - val zG = gridOptimized.get(x); - if(!zG.containsKey(z)) { - zG.put(z, v); - } else { - zG.get(z).addAll(v); - } - } + gridValues.add(new AbstractMap.SimpleEntry<>(k, v)); }); + gridOptimized = SceneIndexManager.buildIndex(2, gridValues, entry -> entry.getKey().toPoint()); } - } - public Set GetGroup(GridPosition pos) { - int x = pos.getX() & 0xFFFF; - int z = pos.getZ() & 0xFFFF; - if(!gridOptimized.containsKey(x)) return new HashSet<>(); - val xG = gridOptimized.get(x); - if(!xG.containsKey(z)) return new HashSet<>(); - return xG.get(z); } public Set getNearbyGroups(int vision_level, Position position) { @@ -56,19 +43,18 @@ public class Grid { GridPosition pos = new GridPosition(position, width); nearbyGroups.clear(); - //construct a nearby pisition list, add 1 more because a player can be in an edge case, this should not affect much the loading - for(int x = 0; x < vision_range_grid + 1; x++) { - for(int z = 0; z < vision_range_grid + 1; z++) { - nearbyGroups.addAll(GetGroup(pos.addClone( x, z))); - nearbyGroups.addAll(GetGroup(pos.addClone(-x, z))); - nearbyGroups.addAll(GetGroup(pos.addClone( x, -z))); - nearbyGroups.addAll(GetGroup(pos.addClone(-x, -z))); - //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, z), new HashSet<>())); - //nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>())); - //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, -z), new HashSet<>())); - //nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, -z), new HashSet<>())); - } - } + //construct a nearby position list, add 1 more because a player can be in an edge case, this should not affect much the loading + //for(int x = 0; x < vision_range_grid + 1; x++) { + // for(int z = 0; z < vision_range_grid + 1; z++) { + // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, z), new HashSet<>())); + // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>())); + // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, -z), new HashSet<>())); + // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, -z), new HashSet<>())); + // } + //} + + //Optimized version + SceneIndexManager.queryNeighbors(gridOptimized, pos.toDoubleArray(), vision_range_grid + 1).forEach(e -> nearbyGroups.addAll(e.getValue())); return nearbyGroups; } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 22ede204..8dc4ec2e 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -390,16 +390,16 @@ public class SceneScriptManager { public List getGroupGrids() { int sceneId = scene.getId(); - if (groupGridsCache.containsKey(sceneId)) { + if (groupGridsCache.containsKey(sceneId) && groupGridsCache.get(sceneId) != null) { Grasscutter.getLogger().debug("Hit cache for scene {}",sceneId); return groupGridsCache.get(sceneId); } else { - var path = FileUtils.getCachePath("Grids/scene" + sceneId + "_grid.json"); + var path = FileUtils.getCachePath("scene" + sceneId + "_grid.json"); if (path.toFile().isFile() && !Grasscutter.config.server.game.cacheSceneEntitiesEveryRun) { try { var groupGrids = JsonUtils.loadToList(path, Grid.class); groupGridsCache.put(sceneId, groupGrids); - return groupGrids; + if(groupGrids != null) return groupGrids; } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/emu/grasscutter/utils/FileUtils.java b/src/main/java/emu/grasscutter/utils/FileUtils.java index 4904864b..fb58da95 100644 --- a/src/main/java/emu/grasscutter/utils/FileUtils.java +++ b/src/main/java/emu/grasscutter/utils/FileUtils.java @@ -19,7 +19,7 @@ public final class FileUtils { private static final Path DATA_USER_PATH = Path.of(Grasscutter.config.folderStructure.data); private static final Path PACKETS_PATH = Path.of(Grasscutter.config.folderStructure.packets); private static final Path PLUGINS_PATH = Path.of(Grasscutter.config.folderStructure.plugins); - private static final Path CACHE_PATH = Path.of(Grasscutter.config.folderStructure.cache); + private static final Path CACHE_PATH; private static final Path RESOURCES_PATH; private static final Path SCRIPTS_PATH; static { @@ -87,6 +87,8 @@ public final class FileUtils { SCRIPTS_PATH = (scripts.startsWith("resources:")) ? RESOURCES_PATH.resolve(scripts.substring("resources:".length())) : Path.of(scripts); + + CACHE_PATH = RESOURCES_PATH.resolve(Grasscutter.config.folderStructure.cache); }; private static final String[] TSJ_JSON_TSV = {"tsj", "json", "tsv"}; diff --git a/src/main/java/emu/grasscutter/utils/GridPosition.java b/src/main/java/emu/grasscutter/utils/GridPosition.java index a2ba3c41..19952949 100644 --- a/src/main/java/emu/grasscutter/utils/GridPosition.java +++ b/src/main/java/emu/grasscutter/utils/GridPosition.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.io.Serializable; import java.util.List; +import com.github.davidmoten.rtreemulti.geometry.Point; + import dev.morphia.annotations.Entity; import lombok.Getter; import lombok.Setter; @@ -90,10 +92,18 @@ public class GridPosition implements Serializable { return new int[]{ x, z, width }; } + public double[] toDoubleArray() { + return new double[]{ x, z }; + } + public int[] toXZIntArray() { return new int[]{ x, z }; } + public Point toPoint() { + return Point.create(x,z); + } + @Override public int hashCode() {