Fix grid loading and use rtree for getNearbyGroups

This commit is contained in:
StartForKiller 2023-04-18 03:54:51 +02:00
parent a7f14a9cd8
commit e4c46c704e
No known key found for this signature in database
GPG Key ID: CE5EA6FC54AAB4F0
5 changed files with 40 additions and 42 deletions

View File

@ -87,7 +87,7 @@ public class ConfigContainer {
public String packets = "./packets/"; public String packets = "./packets/";
public String scripts = "resources:Scripts/"; public String scripts = "resources:Scripts/";
public String plugins = "./plugins/"; public String plugins = "./plugins/";
public String cache = "./cache/"; public String cache = "./server/cache/";
// UNUSED (potentially added later?) // UNUSED (potentially added later?)
// public String dumps = "./dumps/"; // public String dumps = "./dumps/";

View File

@ -1,49 +1,36 @@
package emu.grasscutter.data.server; package emu.grasscutter.data.server;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.utils.GridPosition; import emu.grasscutter.utils.GridPosition;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry;
import lombok.val; import lombok.val;
public class Grid { public class Grid {
public Map<GridPosition, Set<Integer>> grid; public Map<GridPosition, Set<Integer>> grid;
public Int2ObjectMap<Int2ObjectMap<Set<Integer>>> gridOptimized; public transient RTree<Entry<GridPosition, Set<Integer>>, Geometry> gridOptimized = null;
private HashSet<Integer> nearbyGroups = new HashSet<>(100); private transient HashSet<Integer> nearbyGroups = new HashSet<>(100);
private void Optimize() { private void Optimize() {
if(gridOptimized == null) { if(gridOptimized == null) {
gridOptimized = new Int2ObjectOpenHashMap<>(); List<Entry<GridPosition, Set<Integer>>> gridValues = new ArrayList<>();
grid.forEach((k, v) -> { grid.forEach((k, v) -> {
int x = k.getX() & 0xFFFF; gridValues.add(new AbstractMap.SimpleEntry<>(k, v));
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);
}
}
}); });
gridOptimized = SceneIndexManager.buildIndex(2, gridValues, entry -> entry.getKey().toPoint());
} }
}
public Set<Integer> 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<Integer> getNearbyGroups(int vision_level, Position position) { public Set<Integer> getNearbyGroups(int vision_level, Position position) {
@ -56,19 +43,18 @@ public class Grid {
GridPosition pos = new GridPosition(position, width); GridPosition pos = new GridPosition(position, width);
nearbyGroups.clear(); 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 //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 x = 0; x < vision_range_grid + 1; x++) {
for(int z = 0; z < vision_range_grid + 1; z++) { // for(int z = 0; z < vision_range_grid + 1; z++) {
nearbyGroups.addAll(GetGroup(pos.addClone( x, z))); // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, z), new HashSet<>()));
nearbyGroups.addAll(GetGroup(pos.addClone(-x, z))); // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>()));
nearbyGroups.addAll(GetGroup(pos.addClone( x, -z))); // //nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, -z), new HashSet<>()));
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<>()));
//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; return nearbyGroups;
} }

View File

@ -390,16 +390,16 @@ public class SceneScriptManager {
public List<Grid> getGroupGrids() { public List<Grid> getGroupGrids() {
int sceneId = scene.getId(); int sceneId = scene.getId();
if (groupGridsCache.containsKey(sceneId)) { if (groupGridsCache.containsKey(sceneId) && groupGridsCache.get(sceneId) != null) {
Grasscutter.getLogger().debug("Hit cache for scene {}",sceneId); Grasscutter.getLogger().debug("Hit cache for scene {}",sceneId);
return groupGridsCache.get(sceneId); return groupGridsCache.get(sceneId);
} else { } 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) { if (path.toFile().isFile() && !Grasscutter.config.server.game.cacheSceneEntitiesEveryRun) {
try { try {
var groupGrids = JsonUtils.loadToList(path, Grid.class); var groupGrids = JsonUtils.loadToList(path, Grid.class);
groupGridsCache.put(sceneId, groupGrids); groupGridsCache.put(sceneId, groupGrids);
return groupGrids; if(groupGrids != null) return groupGrids;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -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 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 PACKETS_PATH = Path.of(Grasscutter.config.folderStructure.packets);
private static final Path PLUGINS_PATH = Path.of(Grasscutter.config.folderStructure.plugins); 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 RESOURCES_PATH;
private static final Path SCRIPTS_PATH; private static final Path SCRIPTS_PATH;
static { static {
@ -87,6 +87,8 @@ public final class FileUtils {
SCRIPTS_PATH = (scripts.startsWith("resources:")) SCRIPTS_PATH = (scripts.startsWith("resources:"))
? RESOURCES_PATH.resolve(scripts.substring("resources:".length())) ? RESOURCES_PATH.resolve(scripts.substring("resources:".length()))
: Path.of(scripts); : Path.of(scripts);
CACHE_PATH = RESOURCES_PATH.resolve(Grasscutter.config.folderStructure.cache);
}; };
private static final String[] TSJ_JSON_TSV = {"tsj", "json", "tsv"}; private static final String[] TSJ_JSON_TSV = {"tsj", "json", "tsv"};

View File

@ -4,6 +4,8 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import com.github.davidmoten.rtreemulti.geometry.Point;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -90,10 +92,18 @@ public class GridPosition implements Serializable {
return new int[]{ x, z, width }; return new int[]{ x, z, width };
} }
public double[] toDoubleArray() {
return new double[]{ x, z };
}
public int[] toXZIntArray() { public int[] toXZIntArray() {
return new int[]{ x, z }; return new int[]{ x, z };
} }
public Point toPoint() {
return Point.create(x,z);
}
@Override @Override
public int hashCode() public int hashCode()
{ {