diff --git a/serverdata/buildout/areas.sdb b/serverdata/buildout/areas.sdb index a1b9ed287..03d37876f 100644 --- a/serverdata/buildout/areas.sdb +++ b/serverdata/buildout/areas.sdb @@ -57,7 +57,7 @@ INTEGER PRIMARY KEY TEXT NOT NULL TEXT NOT NULL TEXT NOT NULL DEFAULT "" REAL NO -12 endor endor_global -8196 -8196 8196 8196 0 -11 endor_asommers endor_asommers_global -8196 -8196 8196 8196 0 -10 dungeon1 dungeon1_global -8196 -8196 8196 8196 0 --9 tatooine tatooine_global -8196 -8196 8196 8196 0 +-9 dev_area dev_area_global -8196 -8196 8196 8196 0 -8 dathomir dathomir_global -8196 -8196 8196 8196 0 -7 dantooine dantooine_global -8196 -8196 8196 8196 0 -6 creature_test creature_test_global -8196 -8196 8196 8196 0 diff --git a/src/resources/buildout/BuildoutArea.java b/src/resources/buildout/BuildoutArea.java index 74abca95d..2a93aede6 100644 --- a/src/resources/buildout/BuildoutArea.java +++ b/src/resources/buildout/BuildoutArea.java @@ -1,5 +1,6 @@ package resources.buildout; +import resources.Location; import resources.Terrain; public class BuildoutArea implements Comparable { @@ -99,6 +100,18 @@ public class BuildoutArea implements Comparable { return 0; } + public void adjustLocation(Location l) { + if (!isAdjustCoordinates()) + return; + l.translatePosition(x1, 0, z1); + } + + public void readjustLocation(Location l) { + if (!isAdjustCoordinates()) + return; + l.translatePosition(-x1, 0, -z1); + } + public String toString() { return String.format("%s %s: %.1f, %.1f/%.1f, %.1f", name, terrain.getName(), x1, z1, x2, z2); } diff --git a/src/resources/buildout/BuildoutAreaGrid.java b/src/resources/buildout/BuildoutAreaGrid.java new file mode 100644 index 000000000..fa89b8247 --- /dev/null +++ b/src/resources/buildout/BuildoutAreaGrid.java @@ -0,0 +1,101 @@ +package resources.buildout; + +import java.util.ArrayList; +import java.util.List; + +import resources.Terrain; + +public class BuildoutAreaGrid { + + private static final double MIN = -1024*16; + private static final double MAX = 1024*16; + + private final BuildoutAreaNode [][] grid; + + public BuildoutAreaGrid() { + this(32); + } + + public BuildoutAreaGrid(int width) { + grid = new BuildoutAreaNode[width][width]; + } + + public void clear() { + synchronized (grid) { + for (int y = 0; y < grid.length; y++) { + for (int x = 0; x < grid.length; x++) { + grid[y][x] = null; + } + } + } + } + + public void addBuildoutArea(BuildoutArea area) { + int zMax = getNodeIndex(area.getZ2()); + int xMax = getNodeIndex(area.getX2()); + BuildoutAreaNode node = null; + synchronized (grid) { + for (int z = getNodeIndex(area.getZ1()); z <= zMax && z < grid.length; z++) { + for (int x = getNodeIndex(area.getX1()); x <= xMax && x < grid.length; x++) { + node = grid[z][x]; + if (node == null) { + node = new BuildoutAreaNode(); + grid[z][x] = node; + } + node.add(area); + } + } + } + } + + public BuildoutArea getBuildoutArea(Terrain t, double x, double z) { + BuildoutAreaNode node; + synchronized (grid) { + node = grid[getNodeIndex(z)][getNodeIndex(x)]; + if (node == null) + return null; + } + return node.getBestMatch(t, x, z); + } + + private int getNodeIndex(double x) { + return (int) ((x-MIN)/(MAX-MIN)*grid.length); + } + + private static class BuildoutAreaNode { + + private List areas; + + public BuildoutAreaNode() { + areas = new ArrayList<>(); + } + + public void add(BuildoutArea area) { + synchronized (areas) { + areas.add(area); + areas.sort((a, b) -> Double.compare(getArea(a), getArea(b))); + } + } + + public BuildoutArea getBestMatch(Terrain t, double x, double z) { + synchronized (areas) { + for (BuildoutArea area : areas) { + if (!isWithin(area, t, x, z)) + continue; + return area; // It's known to be the best area due to how it is sorted upon add + } + } + return null; + } + + private boolean isWithin(BuildoutArea area, Terrain t, double x, double z) { + return area.getTerrain() == t && x >= area.getX1() && x <= area.getX2() && z >= area.getZ1() && z <= area.getZ2(); + } + + private double getArea(BuildoutArea area) { + return (area.getX2() - area.getX1()) * (area.getZ2() - area.getZ1()); + } + + } + +} diff --git a/src/services/objects/ClientBuildoutService.java b/src/services/objects/ClientBuildoutService.java index 3e7e26ba4..b1ba9d471 100644 --- a/src/services/objects/ClientBuildoutService.java +++ b/src/services/objects/ClientBuildoutService.java @@ -11,18 +11,19 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import intents.object.ObjectCreatedIntent; import intents.player.PlayerTransformedIntent; import resources.Location; import resources.Terrain; import resources.buildout.BuildoutArea; import resources.buildout.BuildoutArea.BuildoutAreaBuilder; +import resources.buildout.BuildoutAreaGrid; import resources.config.ConfigFile; import resources.control.Intent; import resources.control.Service; import resources.objects.SWGObject; import resources.objects.SWGObject.ObjectClassification; import resources.objects.cell.CellObject; -import resources.objects.creature.CreatureObject; import resources.server_info.CrcDatabase; import resources.server_info.Log; import resources.server_info.RelationalServerData; @@ -37,14 +38,15 @@ public class ClientBuildoutService extends Service { + "FROM objects " + "ORDER BY buildout_id ASC"; - private final List areas; + private final BuildoutAreaGrid areaGrid; private final Map areasById; public ClientBuildoutService() { - areas = new ArrayList<>(); + areaGrid = new BuildoutAreaGrid(); areasById = new Hashtable<>(1000); // Number of buildout areas registerForIntent(PlayerTransformedIntent.TYPE); + registerForIntent(ObjectCreatedIntent.TYPE); } @Override @@ -54,6 +56,10 @@ public class ClientBuildoutService extends Service { if (i instanceof PlayerTransformedIntent) handlePlayerTransform((PlayerTransformedIntent) i); break; + case ObjectCreatedIntent.TYPE: + if (i instanceof ObjectCreatedIntent) + handleObjectCreated((ObjectCreatedIntent) i); + break; default: break; } @@ -145,7 +151,7 @@ public class ClientBuildoutService extends Service { BuildoutArea primary = null; // Stored as "best area" for what we want to load try (RelationalServerData data = RelationalServerFactory.getServerData("buildout/areas.db", "areas")) { try (ResultSet set = data.executeQuery(GET_BUILDOUT_AREAS)) { - areas.clear(); + areaGrid.clear(); areasById.clear(); AreaIndexes ind = new AreaIndexes(set); boolean loaded = false; @@ -171,17 +177,26 @@ public class ClientBuildoutService extends Service { private void loadArea(BuildoutArea area) { area.setLoaded(true); - areas.add(area); + areaGrid.addBuildoutArea(area); areasById.put(area.getId(), area); } private void handlePlayerTransform(PlayerTransformedIntent pti) { - CreatureObject creature = pti.getPlayer(); - Location world = creature.getWorldLocation(); - BuildoutArea area = creature.getBuildoutArea(); - if (area == null || compare(area, world.getTerrain(), world.getX(), world.getZ()) != 0) { - area = getAreaForObject(creature); - creature.setBuildoutArea(area); + setObjectArea(pti.getPlayer()); + } + + private void handleObjectCreated(ObjectCreatedIntent oci) { + setObjectArea(oci.getObject()); + } + + private void setObjectArea(SWGObject obj) { + if (obj.getParent() != null) + return; // Not necessary if the object is in another object + Location world = obj.getWorldLocation(); + BuildoutArea area = obj.getBuildoutArea(); + if (area == null || !isWithin(area, world.getTerrain(), world.getX(), world.getZ())) { + area = getAreaForObject(obj); + obj.setBuildoutArea(area); } } @@ -201,39 +216,11 @@ public class ClientBuildoutService extends Service { private BuildoutArea getAreaForObject(SWGObject obj) { Location l = obj.getWorldLocation(); - return binarySearch(l.getTerrain(), l.getX(), l.getZ(), 0, areas.size()); + return areaGrid.getBuildoutArea(l.getTerrain(), l.getX(), l.getZ()); } - private BuildoutArea binarySearch(Terrain t, double x, double z, int low, int high) { - int mid = mid(low, high); - BuildoutArea midArea = areas.get(mid); - int comp = compare(midArea, t, x, z); - if (comp == 0) - return midArea; - if (low == mid) - return null; - if (comp < 0) - return binarySearch(t, x, z, low, mid); - return binarySearch(t, x, z, mid, high); - } - - private int compare(BuildoutArea cur, Terrain t, double x, double z) { - int comp = cur.getTerrain().getName().compareTo(t.getName()); - if (comp != 0) - return comp; - if (x < cur.getX1()) - return 1; - if (x > cur.getX2()) - return -1; - if (z < cur.getZ1()) - return 1; - if (z > cur.getZ2()) - return -1; - return 0; - } - - private int mid(int low, int high) { - return (low + high) / 2; + private boolean isWithin(BuildoutArea area, Terrain t, double x, double z) { + return area.getTerrain() == t && x >= area.getX1() && x <= area.getX2() && z >= area.getZ1() && z <= area.getZ2(); } private void logInfo(String message, Object ... args) { diff --git a/src/utilities/BuildoutGenerator.java b/src/utilities/BuildoutGenerator.java index 744306bc5..e3aecc55a 100644 --- a/src/utilities/BuildoutGenerator.java +++ b/src/utilities/BuildoutGenerator.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map.Entry; import resources.Location; @@ -181,7 +182,8 @@ public class BuildoutGenerator { Collections.sort(areas); for (int i = fallbackAreas.size()-1; i >= 0; i--) { GenBuildoutArea fallback = fallbackAreas.get(i); - gen.writeLine(fallback.id, fallback.terrain.getName(), fallback.terrain.getName() + "_global", "", -8196, -8196, 8196, 8196, "0"); + String name = fallback.terrain.name().toLowerCase(Locale.US); + gen.writeLine(fallback.id, name, name + "_global", "", -8196, -8196, 8196, 8196, "0"); } int percent = 0; for (int i = 0; i < areas.size(); i++) {